diff --git a/main.js b/main.js
index 98d0621..1f858c7 100644
--- a/main.js
+++ b/main.js
@@ -1,7 +1,7 @@
/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
*/
-"use strict";var Cn=Object.defineProperty;var So=Object.getOwnPropertyDescriptor;var Co=Object.getOwnPropertyNames;var Fo=Object.prototype.hasOwnProperty;var Qe=(e,t)=>{for(var n in t)Cn(e,n,{get:t[n],enumerable:!0})},Mo=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Co(t))!Fo.call(e,i)&&i!==n&&Cn(e,i,{get:()=>t[i],enumerable:!(r=So(t,i))||r.enumerable});return e};var wo=e=>Mo(Cn({},"__esModule",{value:!0}),e);var bl={};Qe(bl,{default:()=>yl});module.exports=wo(bl);var bo=require("obsidian");function L(e,t){return`${e}:${t}`}function Fn(e){return e?.cardType==="basic"||e?.cardType==="semantic-qa"}function Kr(e){return e?.cardType==="cloze"}function G(e){return e?.cardType==="qa-group"}var Zr=require("obsidian");function ie(){return(0,Zr.getLanguage)()==="zh"?"zh":"en"}function ae(e,t){return t.join(e==="zh"?"\u3001":", ")}var Mn={commands:{syncCurrentFileToAnki:"Sync current file to Anki",syncVaultToAnki:"Sync vault to Anki",rebuildCardIndex:"Rebuild card index",clearCurrentFileSyncedCards:"Clear synced cards in current file",cleanupEmptyDecks:"Clean up empty decks"},settings:{pluginTitle:"Anki Heading Sync",ankiConnectUrl:{name:"AnkiConnect URL",desc:"Default is http://127.0.0.1:8765",placeholder:"http://127.0.0.1:8765"},qaHeadingLevel:{name:"QA heading level",desc:"Default is H4"},clozeHeadingLevel:{name:"Cloze heading level",desc:"Default is H5"},cardAnswerCutoffMode:{name:"Card answer cutoff mode",desc:"A valid sync marker always wins. Without a valid marker, either keep the whole heading block or stop before the first 2+ consecutive blank lines.",options:{headingBlock:"heading-block: whole heading block",doubleBlankLines:"double-blank-lines: stop before the first 2+ blank lines"}},qaGroup:{title:"QA Group 12",marker:{name:"QA Group marker",desc:"When a QA heading ends with this hashtag marker, the whole heading block syncs as one ObsiAnki QA Group 12 note.",placeholder:"#anki-list"},managedNoteType:"Managed note type: {{modelName}}. QA Group sync writes Stem / GroupId / Src / S01..S12 directly. The same note type can also appear in the field mapping panels below if you want to reuse it for other routes.",managedModelContract:"Managed model contract: {{fieldCount}} fields and {{templateCount}} templates are checked automatically during sync."},semanticQa:{title:"Semantic QA",marker:{name:"Semantic QA marker",desc:"When a QA heading ends with this hashtag marker, first-level list items with indented child content become separate cards.",placeholder:"#anki-list-qa"},previewTitle:"Semantic QA preview",triggerHeadingExample:"Trigger heading example: {{heading}}",previewUnavailable:"Preview unavailable. Use a trailing hashtag marker such as #anki-list-qa.",questionPreview:"Question preview: {{question}}",answerPreview:"Answer preview: {{answer}}"},syncOptions:{addObsidianBacklink:{name:"Add Obsidian backlink",desc:"Append a backlink to the source heading into synced cards."},obsidianBacklinkLabel:{name:"Obsidian backlink label",desc:"The text shown for the backlink. Blank input falls back to Open in Obsidian.",placeholder:"Open in Obsidian"},obsidianBacklinkPlacement:{name:"Obsidian backlink placement",desc:"Choose whether the backlink is appended to the question field or placed at the start or end of the answer body.",options:{questionLastLine:"Last line of question field",answerFirstLine:"First line of answer body",answerLastLine:"Last line of answer body"}},highlightsToCloze:{name:"Highlights to Cloze",desc:"Convert ==highlight== segments into cloze deletions for cloze cards."},syncObsidianTagsToAnki:{name:"Sync Obsidian tags to Anki",desc:"Sync Obsidian-recognized tags from the current note into Anki note tags, including nested tags. On each sync, these tags overwrite the managed tags in Anki based on the current Obsidian content."},keepPureTagLinesInCardBody:{name:"Keep pure tag lines in card body",desc:'When turned off, remove every line in the card body that contains only tags, such as "#ProjectA #\u91CD\u70B9/\u6848\u4F8B". Inline tags like "This is a #tag example" and lines with ordinary text like "\u6807\u7B7E\uFF1A#\u9879\u76EEA" are kept. Extra blank lines created by removal are cleaned up automatically.'}},mapping:{title:"Note type field mappings",refresh:{name:"Refresh note types from Anki",button:"Refresh note types"},status:{idle:"Refresh note types from Anki to load the available note types.",loadedCount:"Loaded {{count}} note types from Anki.",empty:"Anki returned no note types.",failedLoadNoteTypes:"Failed to load note types from Anki.",loadedSavedMapping:"Loaded saved mapping for {{modelName}}.",selectedModel:"Selected {{modelName}}. Read fields from Anki to create or refresh its mapping.",loadedFieldsForModel:"Loaded fields for {{modelName}}. Review the suggested mapping and save it.",failedLoadFields:"Failed to load fields for {{modelName}}.",noLoadedFields:"No loaded fields for {{modelName}}. Read fields from Anki first.",savedMapping:"Saved mapping for {{modelName}}.",failedSaveMapping:"Failed to save mapping for {{modelName}}.",savedMappingReady:"Saved mapping is ready for {{title}}.",noSavedMapping:"No saved mapping for {{title}}. Read fields from Anki first."},section:{basic:{title:"QA / Basic",description:"Choose the QA note type, read its fields from Anki, then confirm the title/body mapping."},cloze:{title:"Cloze",description:"Choose the cloze note type, read its fields from Anki, then confirm the main field mapping."},semanticQa:{title:"Semantic QA",description:"Choose the semantic QA note type, read its fields from Anki, then confirm the title/body mapping for child cards."}},noteTypeLabel:"{{title}} note type",noteTypeDesc:"Loaded from Anki note types. Refresh if the latest models are not shown.",fieldsLabel:"{{title}} fields",fieldsDesc:"Load the current note type fields from Anki, then review the suggested mapping.",readFieldsButton:"Read fields from Anki",loadedFields:"Loaded fields: {{fields}}",loadedFieldsNone:"Loaded fields: none. Read fields from Anki first.",mappingLabel:"{{title}} mapping",mappingDesc:"Save the current field mapping for this specific note type.",saveMappingButton:"Save mapping",titleFieldLabel:"{{title}} title field",titleFieldDesc:"Which Anki field should receive the heading/title fragment.",bodyFieldLabel:"{{title}} body field",bodyFieldDesc:"Which Anki field should receive the body fragment.",mainField:{name:"Cloze main field",desc:"The selected field receives title +
=p))){a=o===61?1:2;break}}if(e.sCount[u]<0)continue;let f=!1;for(let g=0,p=r.length;g
3||e.sCount[a]<0)continue;let c=!1;for(let l=0,d=r.length;l ${t} ]*>.*?<\/a><\/p>/g,"").replace(/ =p))){a=o===61?1:2;break}}if(e.sCount[u]<0)continue;let f=!1;for(let g=0,p=r.length;g 3||e.sCount[a]<0)continue;let c=!1;for(let l=0,d=r.length;l ${t} ]*>.*?<\/a><\/p>/g,"").replace(/ne((2147483647-i)/c)&&ke("overflow"),i+=(s-r)*c,r=s;for(let l of e)if(l
${t}`:t,body:e.body};let r=`
]*>.*?<\/a>/g,"").trim()}function nl(e){return e.includes("GroupId")&&e.includes("Src")&&e.some(n=>/^S\d+_Id$/i.test(n))&&e.some(n=>/^S\d+_Q$/i.test(n))&&e.some(n=>/^S\d+_A$/i.test(n))?e.filter(n=>n==="GroupId"||n==="Src"||/^S\d+_Id$/i.test(n)):[]}function rl(e){let t=[];return{text:e.replace(Wd,r=>{let i=`@@QA_GROUP_INLINE_CODE_${t.length}@@`;return t.push(r),i}),restore:r=>t.reduce((i,a,o)=>i.split(`@@QA_GROUP_INLINE_CODE_${o}@@`).join(a),r)}}var il="manual-render-v3",ve=class{constructor(t=new Oe){this.deckResolutionService=t}resolve(t,n){let r=al(t.cardType,n),i=this.deckResolutionService.resolve(t,n.defaultDeck,n.folderDeckMode),a=i.resolvedDeck.value,o=n.noteFieldMappings[L(t.cardType,r)]??null,u={version:il,cardType:t.cardType,noteModel:r,mapping:o,addObsidianBacklink:n.addObsidianBacklink,obsidianBacklinkLabel:n.obsidianBacklinkLabel,obsidianBacklinkPlacement:n.obsidianBacklinkPlacement,convertHighlightsToCloze:n.convertHighlightsToCloze,keepPureTagLinesInCardBody:n.keepPureTagLinesInCardBody},s=O(JSON.stringify(u));return{deck:a,noteModel:r,renderConfigHash:s,warnings:i.warnings}}};function al(e,t){if(Ai(e)){if(!t.clozeNoteType.trim())throw new C("errors.noteFieldMapping.noteTypeNotSelected.cloze");return t.clozeNoteType}if(e==="semantic-qa"){if(!t.semanticQaNoteType.trim())throw new C("errors.noteFieldMapping.noteTypeNotSelected.semanticQa");return t.semanticQaNoteType}if(!t.qaNoteType.trim())throw new C("errors.noteFieldMapping.noteTypeNotSelected.basic");return t.qaNoteType}var yn=class{constructor(t=new ve){this.renderConfigService=t}plan(t,n,r,i){let a=new Map,o=new Set(n.pendingWriteBack.filter(y=>y.markerKind!=="group-gi").map(y=>Ee(y.filePath,y.blockStartLine,y.rawBlockHash))),u=new Set,s=[],c=[],l=[],d=[],f=[],g=new Map,p=0;for(let y of t){if(a.has(y.syncKey))throw new Error(`Duplicate syncKey detected in current scan: ${y.syncKey}`);if(a.set(y.syncKey,y),y.noteId!==void 0){let b=N(y.noteId);if(u.has(b))throw new Error(`Duplicate noteId detected in current scan: ${y.noteId}`);u.add(b)}let S=y.noteId!==void 0?n.cards[N(y.noteId)]:void 0,x=this.renderConfigService.resolve(y,i);for(let b of x.warnings)g.set(ze(b),b);let k=y.noteId,M=Ee(y.filePath,y.blockStartLine,y.rawBlockHash),v={card:{...y,noteId:k},noteId:k,deck:x.deck,noteModel:x.noteModel,renderConfigHash:x.renderConfigHash};if(!k){s.push(v);continue}if(l.push(v),(y.idMarkerState!=="present-valid"||y.noteIdSource!=="marker"||o.has(M))&&f.push(v),!S){c.push(v);continue}let _=S.rawBlockHash!==y.rawBlockHash||S.renderConfigHash!==x.renderConfigHash||!Ki(S.tagsHint,y.tagsHint)||S.orphan,A=S.deck!==x.deck;_&&c.push(v),A&&d.push(v),!_&&!A&&(p+=1)}let F=new Set(r),h=Object.values(n.cards).filter(y=>F.has(y.filePath)&&!u.has(N(y.noteId)));return{toCreate:s,toUpdate:c,toVerifyDeck:l,toChangeDeck:d,toRewriteMarker:f,toOrphan:h,unchangedCards:p,warnings:Array.from(g.values())}}};var mo=new tt({breaks:!0,html:!0,linkify:!0}),ol=/`[^`\n]+`/g,ul=/```[\s\S]*?```|~~~[\s\S]*?~~~/g,sl=/(?`\\[${h.trim()}\\]`),c=c.replace(cl,(F,h)=>`\\(${h.trim()}\\)`),i&&r.convertHighlightsToCloze&&(c=c.replace(ho,"{$1}")),i&&(c=c.replace(ll,(F,h,y)=>`{{c${h?Number(h):d++}::${y}}}`)),c=c.replace(pl,(F,h)=>{let y=r.resourceResolver.resolveEmbed(h,n.card.filePath);return y?(l.push({kind:y.kind,fileName:y.fileName,absolutePath:y.absolutePath,altText:y.altText}),y.kind==="audio"?`[sound:${y.fileName}]`:``):F}),c=c.replace(fl,(F,h)=>{let y=r.resourceResolver.resolveWikiLink(h,n.card.filePath);return y?`${rt(y.displayText)}`:F}),c=c.replace(ho,"$1");let f=jr(c,dl,"MATH");c=f.text,c=s.restore(c),c=u.restore(c);let g=(a?mo.renderInline(c):mo.render(c)).trim(),p=f.restore(g,rt);return{html:a?p:gn(p),media:l}}};function jr(e,t,n){let r=[];return{text:e.replace(t,a=>{let o=`@@${n}_${r.length}@@`;return r.push(a),o}),restore:(a,o=u=>u)=>r.reduce((u,s,c)=>u.split(`@@${n}_${c}@@`).join(o(s)),a)}}function gl(e){let t=new Set;return e.filter(n=>{let r=`${n.kind}:${n.absolutePath}:${n.fileName}`;return t.has(r)?!1:(t.add(r),!0)})}function rt(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}var it=class extends C{constructor(t){super("errors.currentFileOutOfScope",{filePath:t}),this.name="CurrentFileOutOfScopeError"}},xn=class{constructor(t,n,r,i=new Ut(t),a=new yn,o=new bn,u=new Bt(r),s=new jt(t),c=new ve,l=()=>Date.now()){this.vaultGateway=t;this.pluginStateRepository=n;this.ankiGateway=r;this.fileIndexerService=i;this.diffPlannerService=a;this.renderer=o;this.ankiBatchExecutor=u;this.markdownWriteBackService=s;this.scanScopeService=new Le;if(this.qaGroupSyncService=new kn(r,void 0,void 0,void 0,void 0,void 0,void 0,this.vaultGateway.createBacklink.bind(this.vaultGateway)),this.groupMarkerService=new j,ml(c)){this.renderConfigService=c,this.now=l;return}this.renderConfigService=new ve,this.now=c}async syncVault(t){oe(t);let n=await this.pluginStateRepository.load(),r=await this.fileIndexerService.indexVault(t,n);return this.syncIndexedResult(r,n,t)}async syncFile(t,n){if(oe(n),!this.scanScopeService.isPathInScope(t,n.scopeMode,n.includeFolders,n.excludeFolders))throw new it(t);let r=await this.pluginStateRepository.load(),i=await this.fileIndexerService.indexFile(t,n,r);return this.syncIndexedResult(i,r,n)}async rebuildIndex(t){oe(t);let n=await this.pluginStateRepository.load(),r=await this.fileIndexerService.indexVault(t,n,!0),i=this.diffPlannerService.plan(r.cards,n,r.scopedFilePaths,t),a=new Map(r.indexedFiles.map(d=>[d.filePath,d])),o=i.toRewriteMarker.filter(d=>d.noteId!==void 0),u=this.buildReindexedGroupStates(r.groupBlocks,n),s=await this.markdownWriteBackService.write(o,a,u.markerWrites),c=ko(n,r.scopedFilePaths,u.syncedGroupBlocks),l=this.buildNextState(n,r.cards,r.indexedFiles,u.syncedGroupBlocks,t,new Set(s.writtenSyncKeys),new Map,i.toOrphan,c);if(l.pendingWriteBack=[...n.pendingWriteBack.filter(d=>!new Set(r.scopedFilePaths).has(d.filePath)),...s.pendingEntries],this.markFilesDirty(l,[...s.failureFiles.map(d=>d.filePath),...s.conflictFiles]),await this.pluginStateRepository.save(l),s.failureFiles.length>0)throw this.createWriteBackFailureError(s.failureFiles);return{scannedFiles:r.scannedFiles,scannedCards:r.cards.length+r.groupBlocks.length,created:0,updated:0,migratedNoteTypes:0,migratedDecks:0,orphaned:i.toOrphan.length+c.length,uploadedMedia:0,skippedUnchangedCards:r.skippedUnchangedCards,rewrittenMarkers:s.writtenSyncKeys.length,markerWriteConflictFiles:s.conflictFiles,warnings:yo(i.warnings,u.warnings)}}async syncIndexedResult(t,n,r){let i=this.diffPlannerService.plan(t.cards,n,t.scopedFilePaths,r),a={addObsidianBacklink:r.addObsidianBacklink,obsidianBacklinkLabel:r.obsidianBacklinkLabel,obsidianBacklinkPlacement:r.obsidianBacklinkPlacement,convertHighlightsToCloze:r.convertHighlightsToCloze,keepPureTagLinesInCardBody:r.keepPureTagLinesInCardBody,resourceResolver:this.vaultGateway},o=new Map;for(let g of[...i.toCreate,...i.toUpdate])o.has(g.card.syncKey)||o.set(g.card.syncKey,this.renderer.render(g,a));let u=await this.ankiBatchExecutor.execute(i,o,async g=>this.renderer.render(g,a),r.noteFieldMappings),s=await this.qaGroupSyncService.sync(t.groupBlocks,n,r),c=new Map(t.indexedFiles.map(g=>[g.filePath,g])),l=await this.markdownWriteBackService.write(u.markerWrites,c,s.markerWrites),d=ko(n,t.scopedFilePaths,s.syncedGroupBlocks),f=this.buildNextState(n,t.cards,t.indexedFiles,s.syncedGroupBlocks,r,new Set([...u.touchedSyncKeys,...s.touchedSyncKeys,...l.writtenSyncKeys]),u.resolvedNoteIds,i.toOrphan,d);if(f.pendingWriteBack=[...n.pendingWriteBack.filter(g=>!new Set(t.scopedFilePaths).has(g.filePath)),...l.pendingEntries],this.markFilesDirty(f,[...l.failureFiles.map(g=>g.filePath),...l.conflictFiles]),await this.pluginStateRepository.save(f),l.failureFiles.length>0)throw this.createWriteBackFailureError(l.failureFiles);return{scannedFiles:t.scannedFiles,scannedCards:t.cards.length+t.groupBlocks.length,created:u.created+s.created,updated:u.updated+s.updated,migratedNoteTypes:u.migratedNoteTypes+s.migratedNoteTypes,migratedDecks:u.migratedDecks+s.migratedDecks,orphaned:i.toOrphan.length+d.length,uploadedMedia:u.uploadedMedia,skippedUnchangedCards:t.skippedUnchangedCards,rewrittenMarkers:l.writtenSyncKeys.length,markerWriteConflictFiles:l.conflictFiles,warnings:yo(i.warnings,s.warnings)}}buildNextState(t,n,r,i,a,o,u,s,c){let l=this.now(),d=zn(a),f=new Set(r.filter(h=>h.content!==void 0).map(h=>h.filePath)),g=new Map;for(let h of i){let y=g.get(h.filePath);y?y.push(h):g.set(h.filePath,[h])}let p={files:{...t.files},cards:{...t.cards},groupBlocks:{...t.groupBlocks??{}},pendingWriteBack:[...t.pendingWriteBack]};for(let h of f)delete p.files[h];for(let[h,y]of Object.entries(p.cards))f.has(y.filePath)&&delete p.cards[h];for(let[h,y]of Object.entries(p.groupBlocks??{}))f.has(y.filePath)&&delete p.groupBlocks?.[h];let F=new Map;for(let h of n){let y=u.get(h.syncKey)??h.noteId;y!==void 0&&F.set(h.syncKey,y)}for(let h of r){if(h.content===void 0)continue;let y=Array.from(new Set(h.cards.map(S=>F.get(S.syncKey)).filter(S=>S!==void 0)));p.files[h.filePath]={filePath:h.filePath,fileHash:h.fileHash,fileStamp:h.fileStamp,deckRulesFingerprint:d,lastIndexedAt:l,noteIds:y,groupIds:(g.get(h.filePath)??[]).map(S=>S.groupId)}}for(let h of n){let y=F.get(h.syncKey);if(y===void 0)continue;let S=t.cards[N(y)]??(h.noteId!==void 0?t.cards[N(h.noteId)]:void 0),x=this.renderConfigService.resolve(h,a);p.cards[N(y)]={noteId:y,filePath:h.filePath,heading:h.heading,backlinkHeadingText:h.backlinkHeadingText,headingLevel:h.headingLevel,bodyMarkdown:h.bodyMarkdown,cardType:h.cardType,blockStartOffset:h.blockStartOffset,blockEndOffset:h.blockEndOffset,blockStartLine:h.blockStartLine,bodyStartLine:h.bodyStartLine,blockEndLine:h.blockEndLine,contentEndLine:h.contentEndLine,markerLine:h.markerLine,rawBlockText:h.rawBlockText,rawBlockHash:h.rawBlockHash,renderConfigHash:x.renderConfigHash,deck:x.deck,deckHint:h.deckHint,deckHintSource:h.deckHintSource,deckWarnings:[...h.deckWarnings],tagsHint:h.tagsHint,lastSyncedAt:o.has(h.syncKey)?l:S?.lastSyncedAt??0,orphan:!1}}for(let h of s)p.cards[N(h.noteId)]={...h,orphan:!0};for(let h of i)p.groupBlocks??(p.groupBlocks={}),p.groupBlocks[h.groupId]=h;for(let h of c)p.groupBlocks??(p.groupBlocks={}),p.groupBlocks[h.groupId]={...h,orphan:!0};return p}buildReindexedGroupStates(t,n){let r=[],i=[],a=[],o=new Map(Object.values(n.groupBlocks??{}).filter(s=>!s.orphan).map(s=>[s.noteId,s])),u=new Map;for(let s of Object.values(n.groupBlocks??{}).filter(c=>!c.orphan)){let c=u.get(s.src);c?c.push(s):u.set(s.src,[s])}for(let s of t){let c=(s.groupId?n.groupBlocks?.[s.groupId]:void 0)??(s.noteId!==void 0?o.get(s.noteId):void 0)??hl(u.get(s.src));if(!c)continue;let l=Object.fromEntries(c.items.filter(d=>d.itemId&&d.slot).map(d=>[d.itemId,d.slot]));r.push({...c,filePath:s.filePath,headingText:s.headingText,backlinkHeadingText:s.backlinkHeadingText,headingLevel:s.headingLevel,stem:s.stem,src:s.src,blockStartOffset:s.blockStartOffset,blockEndOffset:s.blockEndOffset,blockStartLine:s.blockStartLine,bodyStartLine:s.bodyStartLine,blockEndLine:s.blockEndLine,contentEndLine:s.contentEndLine,markerLine:s.markerLine,markerIndent:s.markerIndent,rawBlockText:s.rawBlockText,rawBlockHash:s.rawBlockHash,deckHint:s.deckHint,deckHintSource:s.deckHintSource,deckWarnings:[...s.deckWarnings],tagsHint:s.tagsHint?[...s.tagsHint]:[],orphan:!1}),a.push(...s.deckWarnings),s.sourceContent&&(s.markerState!=="present-valid"||!this.groupMarkerService.isEquivalent(s.groupMarker,c.noteId,l,c.freeSlots)||kl(s))&&i.push({syncKey:s.syncKey,filePath:s.filePath,blockStartLine:s.blockStartLine,contentEndLine:s.contentEndLine,blockEndLine:s.blockEndLine,markerLine:s.markerLine,markerIndent:s.markerIndent,noteId:c.noteId,groupId:c.groupId,itemToSlot:l,freeSlots:[...c.freeSlots],sourceContent:s.sourceContent})}return{syncedGroupBlocks:r,markerWrites:i,warnings:a}}markFilesDirty(t,n){for(let r of new Set(n))t.files[r]&&(t.files[r]={...t.files[r],fileHash:"",fileStamp:""})}createWriteBackFailureError(t){return new C("errors.writeBack.summary",{fileCount:t.length},{failures:t})}};function ml(e){return typeof e=="object"&&e!==null&&"resolve"in e}function ko(e,t,n){let r=new Set(t),i=new Set(n.map(a=>a.groupId));return Object.values(e.groupBlocks??{}).filter(a=>r.has(a.filePath)&&!i.has(a.groupId)).map(a=>({...a,orphan:!0}))}function yo(...e){let t=new Map;for(let n of e)for(let r of n)t.set(ze(r),r);return[...t.values()]}function hl(e){if(!(!e||e.length!==1))return e[0]}function kl(e){if(!e.sourceContent)return!1;let t=e.sourceContent.split(/\r?\n/);for(let n=e.blockStartLine;n
=4||e.src.charCodeAt(i)!==62)return!1;if(r)return!0;let u=[],s=[],c=[],l=[],d=e.md.block.ruler.getRules("blockquote"),f=e.parentType;e.parentType="blockquote";let g=!1,p;for(p=t;pne((2147483647-i)/c)&&ke("overflow"),i+=(s-r)*c,r=s;for(let l of e)if(l
${t}`:t,body:e.body};let r=`
]*>.*?<\/a>/g,"").trim()}function al(e){return e.includes("GroupId")&&e.includes("Src")&&e.some(n=>/^S\d+_Id$/i.test(n))&&e.some(n=>/^S\d+_Q$/i.test(n))&&e.some(n=>/^S\d+_A$/i.test(n))?e.filter(n=>n==="GroupId"||n==="Src"||/^S\d+_Id$/i.test(n)):[]}function ol(e){let t=[];return{text:e.replace(jd,r=>{let i=`@@QA_GROUP_INLINE_CODE_${t.length}@@`;return t.push(r),i}),restore:r=>t.reduce((i,a,o)=>i.split(`@@QA_GROUP_INLINE_CODE_${o}@@`).join(a),r)}}var ul="manual-render-v3",ve=class{constructor(t=new Oe){this.deckResolutionService=t}resolve(t,n){let r=sl(t.cardType,n),i=this.deckResolutionService.resolve(t,n.defaultDeck,n.folderDeckMode),a=i.resolvedDeck.value,o=n.noteFieldMappings[L(t.cardType,r)]??null,u={version:ul,cardType:t.cardType,noteModel:r,mapping:o,addObsidianBacklink:n.addObsidianBacklink,obsidianBacklinkLabel:n.obsidianBacklinkLabel,obsidianBacklinkPlacement:n.obsidianBacklinkPlacement,convertHighlightsToCloze:n.convertHighlightsToCloze,keepPureTagLinesInCardBody:n.keepPureTagLinesInCardBody},s=O(JSON.stringify(u));return{deck:a,noteModel:r,renderConfigHash:s,warnings:i.warnings}}};function sl(e,t){if(Ai(e)){if(!t.clozeNoteType.trim())throw new C("errors.noteFieldMapping.noteTypeNotSelected.cloze");return t.clozeNoteType}if(e==="semantic-qa"){if(!t.semanticQaNoteType.trim())throw new C("errors.noteFieldMapping.noteTypeNotSelected.semanticQa");return t.semanticQaNoteType}if(!t.qaNoteType.trim())throw new C("errors.noteFieldMapping.noteTypeNotSelected.basic");return t.qaNoteType}var yn=class{constructor(t=new ve){this.renderConfigService=t}plan(t,n,r,i){let a=new Map,o=new Set(n.pendingWriteBack.filter(y=>y.markerKind!=="group-gi").map(y=>Ee(y.filePath,y.blockStartLine,y.rawBlockHash))),u=new Set,s=[],c=[],l=[],d=[],f=[],g=new Map,p=0;for(let y of t){if(a.has(y.syncKey))throw new Error(`Duplicate syncKey detected in current scan: ${y.syncKey}`);if(a.set(y.syncKey,y),y.noteId!==void 0){let b=N(y.noteId);if(u.has(b))throw new Error(`Duplicate noteId detected in current scan: ${y.noteId}`);u.add(b)}let S=y.noteId!==void 0?n.cards[N(y.noteId)]:void 0,x=this.renderConfigService.resolve(y,i);for(let b of x.warnings)g.set(ze(b),b);let k=y.noteId,M=Ee(y.filePath,y.blockStartLine,y.rawBlockHash),v={card:{...y,noteId:k},noteId:k,deck:x.deck,noteModel:x.noteModel,renderConfigHash:x.renderConfigHash};if(!k){s.push(v);continue}if(l.push(v),(y.idMarkerState!=="present-valid"||y.noteIdSource!=="marker"||o.has(M))&&f.push(v),!S){c.push(v);continue}let _=S.rawBlockHash!==y.rawBlockHash||S.renderConfigHash!==x.renderConfigHash||!Ki(S.tagsHint,y.tagsHint)||S.orphan,A=S.deck!==x.deck;_&&c.push(v),A&&d.push(v),!_&&!A&&(p+=1)}let F=new Set(r),h=Object.values(n.cards).filter(y=>F.has(y.filePath)&&!u.has(N(y.noteId)));return{toCreate:s,toUpdate:c,toVerifyDeck:l,toChangeDeck:d,toRewriteMarker:f,toOrphan:h,unchangedCards:p,warnings:Array.from(g.values())}}};var mo=new tt({breaks:!0,html:!0,linkify:!0}),cl=/`[^`\n]+`/g,dl=/```[\s\S]*?```|~~~[\s\S]*?~~~/g,ll=/(?`\\[${h.trim()}\\]`),c=c.replace(pl,(F,h)=>`\\(${h.trim()}\\)`),i&&r.convertHighlightsToCloze&&(c=c.replace(ho,"{$1}")),i&&(c=c.replace(gl,(F,h,y)=>`{{c${h?Number(h):d++}::${y}}}`)),c=c.replace(ml,(F,h)=>{let y=r.resourceResolver.resolveEmbed(h,n.card.filePath);return y?(l.push({kind:y.kind,fileName:y.fileName,absolutePath:y.absolutePath,altText:y.altText}),y.kind==="audio"?`[sound:${y.fileName}]`:``):F}),c=c.replace(hl,(F,h)=>{let y=r.resourceResolver.resolveWikiLink(h,n.card.filePath);return y?`${rt(y.displayText)}`:F}),c=c.replace(ho,"$1");let f=jr(c,fl,"MATH");c=f.text,c=s.restore(c),c=u.restore(c);let g=(a?mo.renderInline(c):mo.render(c)).trim(),p=f.restore(g,rt);return{html:a?p:gn(p),media:l}}};function jr(e,t,n){let r=[];return{text:e.replace(t,a=>{let o=`@@${n}_${r.length}@@`;return r.push(a),o}),restore:(a,o=u=>u)=>r.reduce((u,s,c)=>u.split(`@@${n}_${c}@@`).join(o(s)),a)}}function kl(e){let t=new Set;return e.filter(n=>{let r=`${n.kind}:${n.absolutePath}:${n.fileName}`;return t.has(r)?!1:(t.add(r),!0)})}function rt(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}var it=class extends C{constructor(t){super("errors.currentFileOutOfScope",{filePath:t}),this.name="CurrentFileOutOfScopeError"}},xn=class{constructor(t,n,r,i=new Ut(t),a=new yn,o=new bn,u=new Bt(r),s=new jt(t),c=new ve,l=()=>Date.now()){this.vaultGateway=t;this.pluginStateRepository=n;this.ankiGateway=r;this.fileIndexerService=i;this.diffPlannerService=a;this.renderer=o;this.ankiBatchExecutor=u;this.markdownWriteBackService=s;this.scanScopeService=new Le;if(this.qaGroupSyncService=new kn(r,void 0,void 0,void 0,void 0,void 0,void 0,this.vaultGateway.createBacklink.bind(this.vaultGateway)),this.groupMarkerService=new j,yl(c)){this.renderConfigService=c,this.now=l;return}this.renderConfigService=new ve,this.now=c}async syncVault(t){oe(t);let n=await this.pluginStateRepository.load(),r=await this.fileIndexerService.indexVault(t,n);return this.syncIndexedResult(r,n,t)}async syncFile(t,n){if(oe(n),!this.scanScopeService.isPathInScope(t,n.scopeMode,n.includeFolders,n.excludeFolders))throw new it(t);let r=await this.pluginStateRepository.load(),i=await this.fileIndexerService.indexFile(t,n,r);return this.syncIndexedResult(i,r,n)}async rebuildIndex(t){oe(t);let n=await this.pluginStateRepository.load(),r=await this.fileIndexerService.indexVault(t,n,!0),i=this.diffPlannerService.plan(r.cards,n,r.scopedFilePaths,t),a=new Map(r.indexedFiles.map(d=>[d.filePath,d])),o=i.toRewriteMarker.filter(d=>d.noteId!==void 0),u=this.buildReindexedGroupStates(r.groupBlocks,n),s=await this.markdownWriteBackService.write(o,a,u.markerWrites),c=ko(n,r.scopedFilePaths,u.syncedGroupBlocks),l=this.buildNextState(n,r.cards,r.indexedFiles,u.syncedGroupBlocks,t,new Set(s.writtenSyncKeys),new Map,i.toOrphan,c);if(l.pendingWriteBack=[...n.pendingWriteBack.filter(d=>!new Set(r.scopedFilePaths).has(d.filePath)),...s.pendingEntries],this.markFilesDirty(l,[...s.failureFiles.map(d=>d.filePath),...s.conflictFiles]),await this.pluginStateRepository.save(l),s.failureFiles.length>0)throw this.createWriteBackFailureError(s.failureFiles);return{scannedFiles:r.scannedFiles,scannedCards:r.cards.length+r.groupBlocks.length,created:0,updated:0,migratedNoteTypes:0,migratedDecks:0,orphaned:i.toOrphan.length+c.length,uploadedMedia:0,skippedUnchangedCards:r.skippedUnchangedCards,rewrittenMarkers:s.writtenSyncKeys.length,markerWriteConflictFiles:s.conflictFiles,warnings:yo(i.warnings,u.warnings)}}async syncIndexedResult(t,n,r){let i=this.diffPlannerService.plan(t.cards,n,t.scopedFilePaths,r),a={addObsidianBacklink:r.addObsidianBacklink,obsidianBacklinkLabel:r.obsidianBacklinkLabel,obsidianBacklinkPlacement:r.obsidianBacklinkPlacement,convertHighlightsToCloze:r.convertHighlightsToCloze,keepPureTagLinesInCardBody:r.keepPureTagLinesInCardBody,resourceResolver:this.vaultGateway},o=new Map;for(let g of[...i.toCreate,...i.toUpdate])o.has(g.card.syncKey)||o.set(g.card.syncKey,this.renderer.render(g,a));let u=await this.ankiBatchExecutor.execute(i,o,async g=>this.renderer.render(g,a),r.noteFieldMappings),s=await this.qaGroupSyncService.sync(t.groupBlocks,n,r),c=new Map(t.indexedFiles.map(g=>[g.filePath,g])),l=await this.markdownWriteBackService.write(u.markerWrites,c,s.markerWrites),d=ko(n,t.scopedFilePaths,s.syncedGroupBlocks),f=this.buildNextState(n,t.cards,t.indexedFiles,s.syncedGroupBlocks,r,new Set([...u.touchedSyncKeys,...s.touchedSyncKeys,...l.writtenSyncKeys]),u.resolvedNoteIds,i.toOrphan,d);if(f.pendingWriteBack=[...n.pendingWriteBack.filter(g=>!new Set(t.scopedFilePaths).has(g.filePath)),...l.pendingEntries],this.markFilesDirty(f,[...l.failureFiles.map(g=>g.filePath),...l.conflictFiles]),await this.pluginStateRepository.save(f),l.failureFiles.length>0)throw this.createWriteBackFailureError(l.failureFiles);return{scannedFiles:t.scannedFiles,scannedCards:t.cards.length+t.groupBlocks.length,created:u.created+s.created,updated:u.updated+s.updated,migratedNoteTypes:u.migratedNoteTypes+s.migratedNoteTypes,migratedDecks:u.migratedDecks+s.migratedDecks,orphaned:i.toOrphan.length+d.length,uploadedMedia:u.uploadedMedia,skippedUnchangedCards:t.skippedUnchangedCards,rewrittenMarkers:l.writtenSyncKeys.length,markerWriteConflictFiles:l.conflictFiles,warnings:yo(i.warnings,s.warnings)}}buildNextState(t,n,r,i,a,o,u,s,c){let l=this.now(),d=zn(a),f=new Set(r.filter(h=>h.content!==void 0).map(h=>h.filePath)),g=new Map;for(let h of i){let y=g.get(h.filePath);y?y.push(h):g.set(h.filePath,[h])}let p={files:{...t.files},cards:{...t.cards},groupBlocks:{...t.groupBlocks??{}},pendingWriteBack:[...t.pendingWriteBack]};for(let h of f)delete p.files[h];for(let[h,y]of Object.entries(p.cards))f.has(y.filePath)&&delete p.cards[h];for(let[h,y]of Object.entries(p.groupBlocks??{}))f.has(y.filePath)&&delete p.groupBlocks?.[h];let F=new Map;for(let h of n){let y=u.get(h.syncKey)??h.noteId;y!==void 0&&F.set(h.syncKey,y)}for(let h of r){if(h.content===void 0)continue;let y=Array.from(new Set(h.cards.map(S=>F.get(S.syncKey)).filter(S=>S!==void 0)));p.files[h.filePath]={filePath:h.filePath,fileHash:h.fileHash,fileStamp:h.fileStamp,deckRulesFingerprint:d,lastIndexedAt:l,noteIds:y,groupIds:(g.get(h.filePath)??[]).map(S=>S.groupId)}}for(let h of n){let y=F.get(h.syncKey);if(y===void 0)continue;let S=t.cards[N(y)]??(h.noteId!==void 0?t.cards[N(h.noteId)]:void 0),x=this.renderConfigService.resolve(h,a);p.cards[N(y)]={noteId:y,filePath:h.filePath,heading:h.heading,backlinkHeadingText:h.backlinkHeadingText,headingLevel:h.headingLevel,bodyMarkdown:h.bodyMarkdown,cardType:h.cardType,blockStartOffset:h.blockStartOffset,blockEndOffset:h.blockEndOffset,blockStartLine:h.blockStartLine,bodyStartLine:h.bodyStartLine,blockEndLine:h.blockEndLine,contentEndLine:h.contentEndLine,markerLine:h.markerLine,rawBlockText:h.rawBlockText,rawBlockHash:h.rawBlockHash,renderConfigHash:x.renderConfigHash,deck:x.deck,deckHint:h.deckHint,deckHintSource:h.deckHintSource,deckWarnings:[...h.deckWarnings],tagsHint:h.tagsHint,lastSyncedAt:o.has(h.syncKey)?l:S?.lastSyncedAt??0,orphan:!1}}for(let h of s)p.cards[N(h.noteId)]={...h,orphan:!0};for(let h of i)p.groupBlocks??(p.groupBlocks={}),p.groupBlocks[h.groupId]=h;for(let h of c)p.groupBlocks??(p.groupBlocks={}),p.groupBlocks[h.groupId]={...h,orphan:!0};return p}buildReindexedGroupStates(t,n){let r=[],i=[],a=[],o=new Map(Object.values(n.groupBlocks??{}).filter(s=>!s.orphan).map(s=>[s.noteId,s])),u=new Map;for(let s of Object.values(n.groupBlocks??{}).filter(c=>!c.orphan)){let c=u.get(s.src);c?c.push(s):u.set(s.src,[s])}for(let s of t){let c=(s.groupId?n.groupBlocks?.[s.groupId]:void 0)??(s.noteId!==void 0?o.get(s.noteId):void 0)??bl(u.get(s.src));if(!c)continue;let l=Object.fromEntries(c.items.filter(d=>d.itemId&&d.slot).map(d=>[d.itemId,d.slot]));r.push({...c,filePath:s.filePath,headingText:s.headingText,backlinkHeadingText:s.backlinkHeadingText,headingLevel:s.headingLevel,stem:s.stem,src:s.src,blockStartOffset:s.blockStartOffset,blockEndOffset:s.blockEndOffset,blockStartLine:s.blockStartLine,bodyStartLine:s.bodyStartLine,blockEndLine:s.blockEndLine,contentEndLine:s.contentEndLine,markerLine:s.markerLine,markerIndent:s.markerIndent,rawBlockText:s.rawBlockText,rawBlockHash:s.rawBlockHash,deckHint:s.deckHint,deckHintSource:s.deckHintSource,deckWarnings:[...s.deckWarnings],tagsHint:s.tagsHint?[...s.tagsHint]:[],orphan:!1}),a.push(...s.deckWarnings),s.sourceContent&&(s.markerState!=="present-valid"||!this.groupMarkerService.isEquivalent(s.groupMarker,c.noteId,l,c.freeSlots)||xl(s))&&i.push({syncKey:s.syncKey,filePath:s.filePath,blockStartLine:s.blockStartLine,contentEndLine:s.contentEndLine,blockEndLine:s.blockEndLine,markerLine:s.markerLine,markerIndent:s.markerIndent,noteId:c.noteId,groupId:c.groupId,itemToSlot:l,freeSlots:[...c.freeSlots],sourceContent:s.sourceContent})}return{syncedGroupBlocks:r,markerWrites:i,warnings:a}}markFilesDirty(t,n){for(let r of new Set(n))t.files[r]&&(t.files[r]={...t.files[r],fileHash:"",fileStamp:""})}createWriteBackFailureError(t){return new C("errors.writeBack.summary",{fileCount:t.length},{failures:t})}};function yl(e){return typeof e=="object"&&e!==null&&"resolve"in e}function ko(e,t,n){let r=new Set(t),i=new Set(n.map(a=>a.groupId));return Object.values(e.groupBlocks??{}).filter(a=>r.has(a.filePath)&&!i.has(a.groupId)).map(a=>({...a,orphan:!0}))}function yo(...e){let t=new Map;for(let n of e)for(let r of n)t.set(ze(r),r);return[...t.values()]}function bl(e){if(!(!e||e.length!==1))return e[0]}function xl(e){if(!e.sourceContent)return!1;let t=e.sourceContent.split(/\r?\n/);for(let n=e.blockStartLine;n