=0;i-=1)this._docs.splice(e[i],1);this._myIndex.removeAll(e)}return t}removeAt(s){this._invertedIndex&&oi(this._invertedIndex,s);let t=this._docs.splice(s,1)[0];return this._myIndex.removeAt(s),t}getIndex(){return this._myIndex}search(s,t){let{limit:e=-1}=t||{},{includeMatches:i,includeScore:a,shouldSort:n,sortFn:r,ignoreFieldNorm:o}=this.options;if(J(s)&&!s.trim()){let d=this._docs.map((u,h)=>({item:u,refIndex:h}));return Ie(e)&&e>-1&&(d=d.slice(0,e)),d}let l=Ie(e)&&e>0&&J(s),c;if(l){let d=new ks(e);J(this._docs[0])?this._searchStringList(s,{heap:d,ignoreFieldNorm:o}):this._searchObjectList(s,{heap:d,ignoreFieldNorm:o}),c=d.extractSorted(r)}else c=J(s)?J(this._docs[0])?this._searchStringList(s):this._searchObjectList(s):this._searchLogical(s),Ia(c,{ignoreFieldNorm:o}),n&&c.sort(r),Ie(e)&&e>-1&&(c=c.slice(0,e));return Fa(c,this._docs,{includeMatches:i,includeScore:a})}_searchStringList(s,{heap:t,ignoreFieldNorm:e}={}){let i=this._getSearcher(s),{records:a}=this._myIndex,n=t?null:[];return a.forEach(({v:r,i:o,n:l})=>{if(!X(r))return;let{isMatch:c,score:d,indices:u}=i.searchIn(r);if(c){let h={item:r,idx:o,matches:[{score:d,value:r,norm:l,indices:u}]};t?(h.score=Ds(h.matches,{ignoreFieldNorm:e}),t.shouldInsert(h.score)&&t.insert(h)):n.push(h)}}),n}_searchLogical(s){let t=pi(s,this.options),e=(r,o,l)=>{if(!("children"in r)){let{keyId:h,searcher:p}=r,m;return h===null?(m=[],this._myIndex.keys.forEach((v,f)=>{m.push(...this._findMatches({key:v,value:o[f],searcher:p}))})):m=this._findMatches({key:this._keyStore.get(h),value:this._myIndex.getValueForItemAtKeyId(o,h),searcher:p}),m&&m.length?[{idx:l,item:o,matches:m}]:[]}let{children:c,operator:d}=r,u=[];for(let h=0,p=c.length;h{if(X(r)){let l=e(t,r,o);l.length&&(a.has(o)||(a.set(o,{idx:o,item:r,matches:[]}),n.push(a.get(o))),l.forEach(({matches:c})=>{a.get(o).matches.push(...c)}))}}),n}_searchObjectList(s,{heap:t,ignoreFieldNorm:e}={}){let i=this._getSearcher(s),{keys:a,records:n}=this._myIndex,r=t?null:[];return n.forEach(({$:o,i:l})=>{if(!X(o))return;let c=[],d=!1,u=!1;if(a.forEach((h,p)=>{let m=this._findMatches({key:h,value:o[p],searcher:i});m.length?(c.push(...m),m[0].hasInverse&&(u=!0)):d=!0}),!(u&&d)&&c.length){let h={idx:l,item:o,matches:c};t?(h.score=Ds(h.matches,{ignoreFieldNorm:e}),t.shouldInsert(h.score)&&t.insert(h)):r.push(h)}}),r}_findMatches({key:s,value:t,searcher:e}){if(!X(t))return[];let i=[];if(Dt(t))t.forEach(({v:a,i:n,n:r})=>{if(!X(a))return;let{isMatch:o,score:l,indices:c,hasInverse:d}=e.searchIn(a);o&&i.push({score:l,key:s,value:a,idx:n,norm:r,indices:c,hasInverse:d})});else{let{v:a,n}=t,{isMatch:r,score:o,indices:l,hasInverse:c}=e.searchIn(a);r&&i.push({score:o,key:s,value:a,norm:n,indices:l,hasInverse:c})}return i}},Cs=class{static condition(s,t){return t.useTokenSearch}constructor(s,t){this.options=t,this.analyzer=Ts({isCaseSensitive:t.isCaseSensitive,ignoreDiacritics:t.ignoreDiacritics});let e=this.analyzer.tokenize(s),i=t._invertedIndex,{df:a,fieldCount:n}=i;this.termSearchers=[],this.idfWeights=[];for(let r of e){this.termSearchers.push(new ae(r,{location:t.location,threshold:t.threshold,distance:t.distance,includeMatches:t.includeMatches,findAllMatches:t.findAllMatches,minMatchCharLength:t.minMatchCharLength,isCaseSensitive:t.isCaseSensitive,ignoreDiacritics:t.ignoreDiacritics,ignoreLocation:!0}));let o=a.get(r)||0,l=Math.log(1+(n-o+.5)/(o+.5));this.idfWeights.push(l)}}searchIn(s){if(!this.termSearchers.length)return{isMatch:!1,score:1};let t=[],e=0,i=0,a=0;for(let o=0;o0?1-e/i:0,r={isMatch:!0,score:Math.max(.001,n)};return this.options.includeMatches&&t.length&&(r.indices=Ms(t)),r}};st.version="7.3.0";st.createIndex=ui;st.parseIndex=ba;st.config=B;st.match=function(g,s,t){return Fe(g,{...B,...t}).searchIn(s)};st.parseQuery=pi;xs(ys);xs(Cs);st.use=function(...g){g.forEach(s=>xs(s))};var rt=require("obsidian");function Na(){return{title:"",author:"",status:"Plan to Read",rating:0,pagesRead:0,totalPages:0,chaptersRead:0,totalChapters:0,volumesRead:0,totalVolumes:0,coverUrl:"",googleBooksId:"",malId:"",vaultPage:"",dateStarted:null,dateFinished:null,releaseDate:null}}var We=class extends rt.FuzzySuggestModal{constructor(s,t,e){super(s),this.files=t,this.onPick=e,this.setPlaceholder("Pick a vault note...")}getItems(){return this.files}getItemText(s){return s.path}onChooseItem(s){this.onPick(s)}},Ot=class extends rt.Modal{constructor(t,e,i,a,n,r,o){var l;super(t);this.starsWrap=null;this.lookupResultsEl=null;this.formEl=null;this.openVaultBtn=null;this.linkVaultBtn=null;this.lookupSearchGen=0;this.selectGen=0;if(this.plugin=e,this.readingData=i,this.mode=a,this.state=r?{...r.state}:Na(),!r){let c=i.getSettings().defaultStatus;c&&(this.state.status=c)}this.existingId=(l=r==null?void 0:r.id)!=null?l:null,this.onSaved=n,this.prefillSearch=o!=null?o:""}onOpen(){var e;let t=(e=this.plugin.settings.colorTheme)!=null?e:"default";this.modalEl.setAttribute("data-theme",t),this.contentEl.setAttribute("data-theme",t),this.contentEl.addClass("wl-view"),this.contentEl.addClass("wl-reading-modal"),this.contentEl.addClass(this.mode==="book"?"wl-reading-modal-book":"wl-reading-modal-manga"),this.titleEl.setText(this.headerTitle()),this.buildUI()}onClose(){this.contentEl.empty()}headerTitle(){return this.existingId?this.mode==="book"?"Edit book":"Edit manga":this.mode==="book"?"Add book":"Add manga"}buildUI(){let t=this.contentEl;t.empty(),this.renderHeaderRow(t),this.renderLookupBar(t),t.createDiv({cls:"wl-reading-modal-divider"}),this.formEl=t.createDiv({cls:"wl-reading-form-wrap"}),this.renderForm(this.formEl),this.renderFooter(t)}renderHeaderRow(t){let e=t.createDiv({cls:"wl-reading-modal-header"}),i=e.createDiv({cls:"wl-reading-modal-header-left"});i.createSpan({cls:"wl-reading-modal-header-icon",text:this.mode==="book"?"\u{1F4D6}":"\u{1F4D3}"}),i.createSpan({cls:"wl-reading-modal-header-title",text:this.headerTitle()});let a=e.createDiv({cls:"wl-reading-modal-header-actions"});this.openVaultBtn=a.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Open"}),this.openVaultBtn.addEventListener("click",()=>this.openLinkedVaultPage()),this.refreshVaultButtons()}refreshVaultButtons(){if(this.linkVaultBtn&&(this.linkVaultBtn.textContent=this.state.vaultPage?"Change link":"Link",this.linkVaultBtn.title=this.state.vaultPage?`Linked: ${this.state.vaultPage}`:"Link a vault note to this entry"),this.openVaultBtn){let t=!!this.state.vaultPage;this.openVaultBtn.disabled=!t,this.openVaultBtn.toggleClass("is-hidden",!t)}}openLinkedVaultPage(){let t=this.state.vaultPage;if(!t)return;let e=this.plugin.app.vault.getAbstractFileByPath(t);e instanceof rt.TFile?(this.plugin.app.workspace.getLeaf("tab").openFile(e),this.close()):new rt.Notice("Linked vault page no longer exists.")}renderLookupBar(t){let e=t.createDiv({cls:"wl-reading-lookup"}),i=e.createEl("input",{cls:"wl-modal-input wl-reading-lookup-input",attr:{type:"text",placeholder:this.mode==="book"?"Search by title or ISBN...":"Search by title or MAL ID..."}}),a=e.createEl("button",{cls:"wl-btn wl-reading-lookup-btn",text:"Fetch"});this.lookupResultsEl=t.createDiv({cls:"wl-reading-lookup-results"}),this.prefillSearch&&(i.value=this.prefillSearch);let n=()=>{let r=i.value.trim();if(!r){new rt.Notice("Enter a search term first.");return}this.performLookup(r)};a.addEventListener("click",n),i.addEventListener("keydown",r=>{r.key==="Enter"&&(r.preventDefault(),n())})}async performLookup(t){let e=++this.lookupSearchGen;if(this.lookupResultsEl){this.lookupResultsEl.empty(),this.lookupResultsEl.createDiv({cls:"wl-reading-lookup-loading",text:"Searching..."});try{if(this.mode==="book"){if(!this.plugin.apiService.hasGoogleBooksKey()){if(e!==this.lookupSearchGen)return;this.lookupResultsEl.empty(),this.lookupResultsEl.createDiv({cls:"wl-reading-lookup-empty",text:"Google Books API key required \u2014 add one in Settings \u2192 API \u2192 Books."});return}let i=await this.plugin.apiService.searchGoogleBooks(t);if(e!==this.lookupSearchGen)return;this.renderBookLookupResults(i)}else{let i=/^\d+$/.test(t),a;if(i){let n=await this.plugin.apiService.getMangaByMalId(parseInt(t,10));if(e!==this.lookupSearchGen)return;a=n?[n]:[]}else if(a=await this.plugin.apiService.searchManga(t),e!==this.lookupSearchGen)return;this.renderMangaLookupResults(a)}}catch(i){if(e!==this.lookupSearchGen)return;this.lookupResultsEl.empty();let a=this.mode==="book"?Et(i):"Lookup failed. Check your connection.";this.lookupResultsEl.createDiv({cls:"wl-reading-lookup-empty",text:a})}}}renderBookLookupResults(t){if(this.lookupResultsEl){if(this.lookupResultsEl.empty(),t.length===0){this.lookupResultsEl.createDiv({cls:"wl-reading-lookup-empty",text:"No matches."});return}for(let e of t){let i=this.lookupResultsEl.createDiv({cls:"wl-reading-lookup-item"});this.renderLookupItemTitle(i,e.title,e.url);let a=[e.author||"Unknown author",e.year?String(e.year):""].filter(Boolean).join(" \xB7 ");i.createDiv({cls:"wl-reading-lookup-item-meta",text:a}),i.addEventListener("click",()=>this.applyBookResult(e))}}}renderLookupItemTitle(t,e,i){let a=t.createDiv({cls:"wl-reading-lookup-item-title-row"});if(a.createDiv({cls:"wl-reading-lookup-item-title",text:e||"(untitled)"}),i){let n=a.createEl("a",{cls:"wl-acc-link-icon",text:"\u{1F310}"});n.href=i,n.title="Open external link",n.target="_blank",n.rel="noopener noreferrer",n.addEventListener("click",r=>r.stopPropagation())}}renderMangaLookupResults(t){if(this.lookupResultsEl){if(this.lookupResultsEl.empty(),t.length===0){this.lookupResultsEl.createDiv({cls:"wl-reading-lookup-empty",text:"No matches."});return}for(let e of t){let i=this.lookupResultsEl.createDiv({cls:"wl-reading-lookup-item"});this.renderLookupItemTitle(i,e.title,e.url);let a=[e.author||"Unknown author",e.year?String(e.year):""].filter(Boolean).join(" \xB7 ");i.createDiv({cls:"wl-reading-lookup-item-meta",text:a}),i.addEventListener("click",()=>this.applyMangaResult(e))}}}applyBookResult(t){var i;let e=++this.selectGen;this.state.title=t.title,this.state.author=t.author,this.state.totalPages=t.totalPages,this.state.coverUrl=t.coverUrl,this.state.googleBooksId=t.googleBooksId,this.state.releaseDate=(i=t.releaseDate)!=null?i:"",this.refreshForm(),t.googleBooksId&&(async()=>{try{let a=await this.plugin.apiService.getGoogleBookById(t.googleBooksId);if(e!==this.selectGen||!a)return;a.totalPages>0&&(this.state.totalPages=a.totalPages),a.coverUrl&&(this.state.coverUrl=a.coverUrl),this.refreshForm()}catch(a){}})()}applyMangaResult(t){var i;let e=++this.selectGen;this.state.title=t.title,this.state.author=t.author,this.state.totalChapters=t.totalChapters,this.state.totalVolumes=t.totalVolumes,this.state.coverUrl=t.coverUrl,this.state.malId=t.malId>0?String(t.malId):"",this.state.releaseDate=(i=t.releaseDate)!=null?i:"",this.refreshForm(),t.malId>0&&(async()=>{let a=await this.plugin.apiService.getMangaByMalId(t.malId);e!==this.selectGen||!a||(this.state.totalChapters=a.totalChapters>0?a.totalChapters:this.state.totalChapters,this.state.totalVolumes=a.totalVolumes>0?a.totalVolumes:this.state.totalVolumes,this.refreshForm())})()}refreshForm(){this.formEl&&(this.formEl.empty(),this.renderForm(this.formEl))}renderForm(t){let e=t.createDiv({cls:"wl-reading-form"});this.renderTextField(e,"Title","Book title",this.state.title,i=>{this.state.title=i}),this.renderTextField(e,"Author","Author name",this.state.author,i=>{this.state.author=i}),this.renderStatusRatingRow(e),this.renderProgressOptionalGrid(e)}renderProgressOptionalGrid(t){let e=t.createDiv({cls:"wl-reading-form-grid"}),i=e.createDiv({cls:"wl-reading-form-grid-col"});i.createDiv({cls:"wl-reading-section-label",text:"Progress"}),this.mode==="book"?(this.renderTwoNumberRow(i,{label:"Pages read",value:this.state.pagesRead,onChange:n=>{this.state.pagesRead=n}},{label:"Chapters read",value:this.state.chaptersRead,onChange:n=>{this.state.chaptersRead=n}}),this.renderTwoNumberRow(i,{label:"Total pages",value:this.state.totalPages,onChange:n=>{this.state.totalPages=n}},{label:"Total chapters",value:this.state.totalChapters,onChange:n=>{this.state.totalChapters=n}})):(this.renderTwoNumberRow(i,{label:"Chapters read",value:this.state.chaptersRead,onChange:n=>{this.state.chaptersRead=n}},{label:"Volumes read",value:this.state.volumesRead,onChange:n=>{this.state.volumesRead=n}}),this.renderTwoNumberRow(i,{label:"Total chapters",value:this.state.totalChapters,onChange:n=>{this.state.totalChapters=n}},{label:"Total volumes",value:this.state.totalVolumes,onChange:n=>{this.state.totalVolumes=n}}));let a=e.createDiv({cls:"wl-reading-form-grid-col"});a.createDiv({cls:"wl-reading-section-label",text:"Optional"}),this.renderTextField(a,"Cover URL","https://...",this.state.coverUrl,n=>{this.state.coverUrl=n}),this.renderDateField(a,"Start date",this.state.dateStarted,n=>{this.state.dateStarted=n}),this.renderDateField(a,"Release date",this.state.releaseDate,n=>{this.state.releaseDate=n})}renderTextField(t,e,i,a,n){let r=t.createDiv({cls:"wl-reading-row"});r.createSpan({cls:"wl-reading-label",text:e});let o=r.createEl("input",{cls:"wl-modal-input",attr:{type:"text",placeholder:i}});o.value=a,o.addEventListener("input",()=>n(o.value))}renderStatusRatingRow(t){let e=t.createDiv({cls:"wl-reading-status-rating-row"}),i=e.createDiv({cls:"wl-reading-inline-group"});i.createSpan({cls:"wl-reading-label",text:"Status"});let a=i.createEl("select",{cls:"wl-select wl-reading-status-select"});for(let r of lt){let o=a.createEl("option",{text:r,value:r});r===this.state.status&&(o.selected=!0)}a.addEventListener("change",()=>{this.state.status=a.value});let n=e.createDiv({cls:"wl-reading-inline-group"});n.createSpan({cls:"wl-reading-label",text:"Rating"}),this.starsWrap=n.createDiv({cls:"wl-stars wl-reading-stars"}),this.renderStars()}renderStars(){if(this.starsWrap){this.starsWrap.empty();for(let t=1;t<=5;t++)this.starsWrap.createSpan({cls:`wl-star${this.state.rating>=t?" is-active":""}`,text:"\u2605"}).addEventListener("click",()=>{this.state.rating=this.state.rating===t?0:t,this.renderStars()})}}renderTwoNumberRow(t,e,i){let a=t.createDiv({cls:"wl-reading-row wl-reading-row-split"});this.renderNumberCol(a,e),this.renderNumberCol(a,i)}renderNumberCol(t,e){let i=t.createDiv({cls:"wl-reading-col"});i.createSpan({cls:"wl-reading-label",text:e.label});let a=i.createEl("input",{cls:"wl-modal-input wl-modal-input-sm",attr:{type:"number",min:"0"}});a.value=String(e.value),a.addEventListener("input",()=>{let n=parseInt(a.value,10);e.onChange(isNaN(n)||n<0?0:n)})}renderDateField(t,e,i,a){let n=t.createDiv({cls:"wl-reading-row"});n.createSpan({cls:"wl-reading-label",text:e});let r=n.createEl("input",{cls:"wl-modal-input",attr:{type:"text",placeholder:"DD/MM/YYYY",maxlength:"10"}});r.value=i?Q(i):"",r.addEventListener("change",()=>{let o=r.value.trim();if(!o){r.removeClass("wl-input-error"),a(null);return}let l=q(o);l?(r.removeClass("wl-input-error"),a(l)):r.addClass("wl-input-error")})}renderFooter(t){let e=t.createDiv({cls:"wl-reading-modal-footer"});e.createEl("button",{cls:"wl-btn",text:"Cancel"}).addEventListener("click",()=>this.close()),e.createEl("button",{cls:"wl-reading-add-btn wl-btn-success wl-reading-modal-save",text:this.saveButtonLabel()}).addEventListener("click",()=>void this.save())}saveButtonLabel(){return this.existingId?"Save changes":this.mode==="book"?"Add book":"Add manga"}async save(){let t=this.state.title.trim();if(!t){new rt.Notice("Please enter a title.");return}this.mode==="book"?await this.saveBook(t):await this.saveManga(t),this.onSaved(),this.close()}async saveBook(t){if(this.existingId){let e=this.readingData.getBook(this.existingId);if(!e){new rt.Notice("Original book no longer exists.");return}let i={...e,title:t,author:this.state.author.trim(),status:this.state.status,rating:this.state.rating,pagesRead:this.state.pagesRead,totalPages:this.state.totalPages,chaptersRead:this.state.chaptersRead,totalChapters:this.state.totalChapters,coverUrl:this.state.coverUrl.trim(),googleBooksId:this.state.googleBooksId.trim(),vaultPage:this.state.vaultPage,dateStarted:this.state.dateStarted,dateFinished:this.state.dateFinished,releaseDate:this.state.releaseDate};await this.readingData.updateBook(i)}else{let e=new Date().toISOString(),i={id:this.readingData.generateBookId(t),title:t,author:this.state.author.trim(),status:this.state.status,rating:this.state.rating,pagesRead:this.state.pagesRead,totalPages:this.state.totalPages,chaptersRead:this.state.chaptersRead,totalChapters:this.state.totalChapters,coverUrl:this.state.coverUrl.trim(),googleBooksId:this.state.googleBooksId.trim(),vaultPage:this.state.vaultPage,dateStarted:this.state.dateStarted,dateFinished:this.state.dateFinished,releaseDate:this.state.releaseDate,dateAdded:e,dateModified:e,customFields:{}};await this.readingData.addBook(i)}}async saveManga(t){if(this.existingId){let e=this.readingData.getManga(this.existingId);if(!e){new rt.Notice("Original manga no longer exists.");return}let i={...e,title:t,author:this.state.author.trim(),status:this.state.status,rating:this.state.rating,chaptersRead:this.state.chaptersRead,totalChapters:this.state.totalChapters,volumesRead:this.state.volumesRead,totalVolumes:this.state.totalVolumes,coverUrl:this.state.coverUrl.trim(),malId:this.state.malId.trim(),vaultPage:this.state.vaultPage,dateStarted:this.state.dateStarted,dateFinished:this.state.dateFinished,releaseDate:this.state.releaseDate};await this.readingData.updateManga(i)}else{let e=new Date().toISOString(),i={id:this.readingData.generateMangaId(t),title:t,author:this.state.author.trim(),status:this.state.status,rating:this.state.rating,chaptersRead:this.state.chaptersRead,totalChapters:this.state.totalChapters,volumesRead:this.state.volumesRead,totalVolumes:this.state.totalVolumes,coverUrl:this.state.coverUrl.trim(),malId:this.state.malId.trim(),vaultPage:this.state.vaultPage,dateStarted:this.state.dateStarted,dateFinished:this.state.dateFinished,releaseDate:this.state.releaseDate,dateAdded:e,dateModified:e,customFields:{}};await this.readingData.addManga(i)}}};var gi=require("obsidian"),Oe=class extends gi.Modal{constructor(t,e,i){super(t);this.draftText=e;this.onChoice=i}onOpen(){let{contentEl:t}=this;t.empty(),t.addClass("wl-draft-choice-modal"),this.titleEl.setText("Add draft"),t.createDiv({cls:"wl-draft-choice-subtitle",text:this.draftText});let e=t.createDiv({cls:"wl-draft-choice-grid"}),i=(a,n)=>{let r=e.createEl("button",{cls:"wl-draft-choice-btn"});r.createDiv({cls:"wl-draft-choice-label",text:a}),r.addEventListener("click",()=>{this.close(),this.onChoice(n)})};i("Add in Watchlist","watchlist"),i("Add book","book"),i("Add manga","manga")}onClose(){this.contentEl.empty()}};var ne=class g{constructor(s,t,e,i){this.eventRef=null;this.destroyed=!1;this.scanDebounceTimer=null;this.lastScanEntries=[];this.persistState={dismissed:[],added:[],firstSeen:{},titleDisplay:{}};this.containerEl=s,this.plugin=t,this.dataManager=e,this.onCountChange=i}async render(){if(this.destroyed=!1,await this.loadPersistState(),this.destroyed)return;let s=await this.scanVault();this.destroyed||(this.renderUI(s),this.registerChangeListener())}destroy(){this.destroyed=!0,this.scanDebounceTimer&&(window.clearTimeout(this.scanDebounceTimer),this.scanDebounceTimer=null),this.eventRef&&(this.plugin.app.metadataCache.offref(this.eventRef),this.eventRef=null)}async loadPersistState(){var e,i,a,n;let t=this.plugin.dataManager.getData().drafts;t&&(this.persistState={dismissed:(e=t.dismissed)!=null?e:[],added:(i=t.added)!=null?i:[],firstSeen:(a=t.firstSeen)!=null?a:{},titleDisplay:(n=t.titleDisplay)!=null?n:{}})}async savePersistState(){let s=this.plugin.dataManager.getData();s.drafts=this.persistState,this.plugin.dataManager.queueSave()}getTag(){var s;return(s=this.plugin.settings.draftsVaultTag)!=null?s:"#watchlog"}static async scanTaggedFiles(s,t){var a,n;let e=s.app.vault.getMarkdownFiles(),i=new Map;for(let r of e){let o=s.app.metadataCache.getFileCache(r);if((n=(a=o==null?void 0:o.tags)==null?void 0:a.some(c=>c.tag===t))!=null&&n)try{let d=(await s.app.vault.cachedRead(r)).split(`
+`);for(let u of d){if(!u.includes(t))continue;let h=u.indexOf(t),p=u.slice(h+t.length).trim();if(!p)continue;let m=p.split(",");for(let v of m){let f=v.trim();if(!f||f.length>100)continue;let y=f.toLowerCase();i.has(y)||i.set(y,{sources:new Set,displayTitle:f}),i.get(y).sources.add(r.basename)}}}catch(c){}}return i}async scanVault(){var n,r;let s=this.getTag(),t=await g.scanTaggedFiles(this.plugin,s),e=!1,i=new Date().toISOString();for(let[o,{displayTitle:l}]of t)this.persistState.firstSeen[o]||(this.persistState.firstSeen[o]=i,e=!0),this.persistState.titleDisplay[o]||(this.persistState.titleDisplay[o]=l,e=!0);e&&await this.savePersistState();let a=[];for(let[o,{sources:l}]of t)this.persistState.dismissed.includes(o)||a.push({titleKey:o,titleDisplay:(n=this.persistState.titleDisplay[o])!=null?n:o,sources:Array.from(l),firstSeen:(r=this.persistState.firstSeen[o])!=null?r:i,added:this.persistState.added.includes(o)});return a.sort((o,l)=>o.firstSeen.localeCompare(l.firstSeen)),this.lastScanEntries=a,a}triggerDebouncedRender(){this.scanDebounceTimer&&window.clearTimeout(this.scanDebounceTimer),this.scanDebounceTimer=window.setTimeout(()=>{this.scanDebounceTimer=null,this.render()},500)}registerChangeListener(){this.eventRef&&this.plugin.app.metadataCache.offref(this.eventRef),this.eventRef=this.plugin.app.metadataCache.on("changed",s=>{this.triggerDebouncedRender()})}static buildFuse(s){return new st(s,{keys:["title"],threshold:.35,includeScore:!0})}static fuzzyMatchesWatchlist(s,t){var i,a;let e=t.search(s);return e.length>0&&((a=(i=e[0])==null?void 0:i.score)!=null?a:1)<=.35}static async computePendingCount(s,t){var u,h,p,m,v;let e=(u=s.settings.draftsVaultTag)!=null?u:"#watchlog",i=await g.scanTaggedFiles(s,e),n=s.dataManager.getData().drafts,r=new Set((h=n==null?void 0:n.dismissed)!=null?h:[]),o=new Set((p=n==null?void 0:n.added)!=null?p:[]),l=(m=n==null?void 0:n.titleDisplay)!=null?m:{},c=g.buildFuse(t.getTitles()),d=0;for(let[f,{displayTitle:y}]of i){if(r.has(f)||o.has(f))continue;let w=(v=l[f])!=null?v:y;g.fuzzyMatchesWatchlist(w,c)||d++}return d}renderUI(s){var c;let t=this.containerEl;t.empty();let e=this.getTag(),i=this.dataManager.getTitles(),a=g.buildFuse(i),n=new Map;for(let d of s)d.added?n.set(d.titleKey,!1):n.set(d.titleKey,g.fuzzyMatchesWatchlist(d.titleDisplay,a));let r=s.filter(d=>d.added?!1:!n.get(d.titleKey)).length;this.onCountChange(r),this.plugin.settings.showHintBanners&&t.createDiv({cls:"wl-drafts-notice",text:`\u26A0 Write ${e} Movie Name in any note and it appears here automatically. Hit Add when you're ready to add it to your Watchlist.`});let o=t.createDiv({cls:"wl-list-title-wrap"});if(o.createSpan({cls:"wl-list-count",text:String(r)}),o.createSpan({cls:"wl-drafts-count-label",text:` Pending draft${r!==1?"s":""}`}),s.length===0){t.createDiv({cls:"wl-drafts-empty",text:`No drafts found. Add ${e} followed by a title in any vault note.`});return}s.sort((d,u)=>{var m,v;let h=(m=n.get(d.titleKey))!=null?m:!1,p=(v=n.get(u.titleKey))!=null?v:!1;return h!==p?h?1:-1:d.firstSeen.localeCompare(u.firstSeen)});let l=t.createDiv({cls:"wl-drafts-cards"});for(let d of s)this.renderCard(l,d,(c=n.get(d.titleKey))!=null?c:!1)}renderCard(s,t,e){let i="wl-drafts-card";e&&(i+=" wl-drafts-card-watchlist"),t.added&&(i+=" wl-drafts-card-added");let a=s.createDiv({cls:i});a.createDiv({cls:"wl-drafts-card-title",text:t.titleDisplay});let n=a.createDiv({cls:"wl-drafts-card-source"});if(t.sources.length>0){let c=t.sources[0];n.createSpan({cls:"wl-drafts-source-link",text:`[[${c}]]`}).addEventListener("click",u=>{u.stopPropagation(),this.plugin.app.workspace.openLinkText(c,"")}),t.sources.length>1&&n.createSpan({cls:"wl-drafts-source-count",text:` (${t.sources.length})`,attr:{title:t.sources.join(`
+`)}})}let r=a.createDiv({cls:"wl-drafts-card-dup"});e&&r.createSpan({text:"In Watchlist",attr:{title:"This title already exists in your Watchlist"}});let o=a.createDiv({cls:"wl-drafts-card-actions"});t.added||e?o.createSpan({cls:"wl-drafts-added-label",text:"Added"}):o.createEl("button",{cls:"wl-btn wl-btn-sm wl-btn-primary",text:"Add"}).addEventListener("click",()=>this.openAddModal(t)),o.createEl("button",{cls:"wl-btn wl-btn-sm wl-drafts-dismiss",text:"\u2715",attr:{title:"Dismiss"}}).addEventListener("click",()=>void this.dismissEntry(t.titleKey))}rerenderFromCache(){let s=this.lastScanEntries.filter(t=>!this.persistState.dismissed.includes(t.titleKey)).map(t=>({...t,added:this.persistState.added.includes(t.titleKey)}));this.renderUI(s)}async dismissEntry(s){this.persistState.dismissed.includes(s)||this.persistState.dismissed.push(s),this.persistState.added=this.persistState.added.filter(t=>t!==s),await this.savePersistState(),this.rerenderFromCache()}openAddModal(s){new Oe(this.plugin.app,s.titleDisplay,t=>{t==="watchlist"?this.openWatchlistAddModal(s):this.openReadingAddModal(s,t)}).open()}openWatchlistAddModal(s){new ht(this.plugin.app,this.plugin,this.dataManager,()=>void this.afterAdded(s.titleKey),{searchQuery:s.titleDisplay,title:s.titleDisplay,type:"Anime",episodes:0,duration:0,releaseDate:"",link:"",seasons:[]}).open()}openReadingAddModal(s,t){new Ot(this.plugin.app,this.plugin,this.plugin.readingDataManager,t,()=>void this.afterAdded(s.titleKey),void 0,s.titleDisplay).open()}async afterAdded(s){var e;((e=this.plugin.settings.draftsAfterAdding)!=null?e:"keep")==="remove"?await this.dismissEntry(s):(this.persistState.added.includes(s)||this.persistState.added.push(s),await this.savePersistState(),this.rerenderFromCache())}};var mt=require("obsidian");var it=require("obsidian");var Ne=require("obsidian");var Nt=class extends Ne.Modal{constructor(t,e,i,a,n){super(t);this.listEl=null;this.styleGroupEl=null;this.dragFromIndex=-1;this.plugin=e,this.readingData=i,this.kind=a,this.onChanged=n}onOpen(){var e;let t=(e=this.plugin.settings.colorTheme)!=null?e:"default";this.modalEl.setAttribute("data-theme",t),this.contentEl.setAttribute("data-theme",t),this.contentEl.addClass("wl-view"),this.contentEl.addClass("wl-reading-modal"),this.contentEl.addClass("wl-reading-manage-cols"),this.titleEl.setText(this.kind==="book"?"Manage book fields":"Manage manga fields"),this.build()}onClose(){this.contentEl.empty()}build(){let t=this.contentEl;t.empty();let e=t.createDiv({cls:"wl-reading-modal-header"});e.createSpan({cls:"wl-reading-modal-header-icon",text:this.kind==="book"?"\u{1F4D6}":"\u{1F4D3}"}),e.createSpan({cls:"wl-reading-modal-header-title",text:this.kind==="book"?"Manage book fields":"Manage manga fields"}),t.createDiv({cls:"wl-reading-manage-cols-desc",text:"Custom fields appear in the title detail modal. Changes save automatically."}),this.renderStyleToggle(t),this.listEl=t.createDiv({cls:"wl-reading-manage-cols-list"}),this.renderList(),t.createDiv({cls:"wl-reading-modal-divider"}),this.renderAddSection(t),t.createDiv({cls:"wl-reading-modal-footer"}).createEl("button",{cls:"wl-btn",text:"Close"}).addEventListener("click",()=>this.close())}renderStyleToggle(t){var n,r;let e=this.readingData.getSettings(),i=this.kind==="book"?(n=e.bookCustomFieldStyle)!=null?n:"fill":(r=e.mangaCustomFieldStyle)!=null?r:"fill",a=t.createDiv({cls:"wl-reading-cols-style-row"});a.createSpan({cls:"wl-reading-cols-style-label",text:"Display style:"}),this.styleGroupEl=a.createDiv({cls:"wl-reading-cols-style-group"});for(let o of["fill","border"]){let l=this.styleGroupEl.createEl("button",{cls:`wl-reading-cols-style-btn${i===o?" is-active":""}`,text:o.charAt(0).toUpperCase()+o.slice(1)});l.addEventListener("click",()=>{(async()=>{var d;let c=this.kind==="book"?"bookCustomFieldStyle":"mangaCustomFieldStyle";await this.readingData.updateSettings({[c]:o}),this.onChanged(),(d=this.styleGroupEl)==null||d.querySelectorAll(".wl-reading-cols-style-btn").forEach(u=>{u.removeClass("is-active")}),l.addClass("is-active")})()})}}getColumns(){return this.kind==="book"?this.readingData.getBookColumns():this.readingData.getMangaColumns()}renderList(){if(!this.listEl)return;this.listEl.empty();let t=this.getColumns();if(t.length===0){this.listEl.createDiv({cls:"wl-reading-manage-cols-empty",text:"No custom fields yet. Add one below."});return}t.forEach((e,i)=>{var u;let a=this.listEl.createDiv({cls:"wl-editcol-card wl-reading-col-card"}),n=a.createDiv({cls:"wl-editcol-card-handle",text:"\u283F"});n.title="Drag to reorder",n.addEventListener("mousedown",()=>a.setAttribute("draggable","true")),a.addEventListener("dragstart",h=>{var p;this.dragFromIndex=i,(p=h.dataTransfer)==null||p.setData("text/plain",String(i)),a.addClass("wl-cl-dragging")}),a.addEventListener("dragend",()=>{a.removeClass("wl-cl-dragging"),a.setAttribute("draggable","false")}),a.addEventListener("dragover",h=>{h.preventDefault(),a.addClass("wl-cl-drag-over")}),a.addEventListener("dragleave",()=>a.removeClass("wl-cl-drag-over")),a.addEventListener("drop",h=>{h.preventDefault(),a.removeClass("wl-cl-drag-over");let p=this.dragFromIndex,m=i;this.dragFromIndex=-1,!(p===m||p<0)&&this.reorder(p,m)});let r=a.createEl("input",{cls:"wl-modal-input wl-editcol-card-name",attr:{type:"text",placeholder:"Field name"}});r.value=e.name,r.addEventListener("change",()=>{let h=r.value.trim();if(!h){r.value=e.name;return}h!==e.name&&this.updateColumn({...e,name:h})});let o=a.createEl("select",{cls:"wl-select wl-editcol-card-type"});for(let h of["text","number","select"]){let p=o.createEl("option",{value:h,text:h.charAt(0).toUpperCase()+h.slice(1)});e.type===h&&(p.selected=!0)}let l=a.createEl("button",{cls:"wl-reading-col-color-dot"});l.style.backgroundColor=(u=e.color)!=null?u:It,l.title="Choose field color",l.addEventListener("click",h=>{h.stopPropagation(),this.openColorPalette(l,e)});let c=a.createEl("input",{cls:"wl-modal-input wl-reading-col-opts-input",attr:{type:"text",placeholder:"Comma-separated values"}});c.value=e.options.join(", "),c.style.visibility=e.type==="select"?"":"hidden",c.addEventListener("change",()=>{let h=c.value.split(",").map(p=>p.trim()).filter(Boolean);this.updateColumn({...e,options:h})}),o.addEventListener("change",()=>{let h=o.value;c.style.visibility=h==="select"?"":"hidden",this.updateColumn({...e,type:h})});let d=a.createEl("button",{cls:"wl-btn wl-btn-sm wl-editcol-card-del",text:"\u2715"});d.title="Delete this field",d.addEventListener("click",()=>{new H(this.plugin.app,`Delete field "${e.name}"? This removes its data from every ${this.kind==="book"?"book":"manga"} entry.`,()=>void this.deleteColumn(e.id)).open()})})}openColorPalette(t,e){var l;this.contentEl.querySelectorAll(".wl-reading-col-palette").forEach(c=>c.remove());let i=t.getBoundingClientRect(),a=this.contentEl.createDiv({cls:"wl-reading-col-palette"});a.style.top=`${i.bottom+4}px`,a.style.left=`${i.left}px`;let n=this.contentEl.ownerDocument,r=(l=e.color)!=null?l:It;for(let{color600:c,color50:d}of Ye){let u=a.createEl("button",{cls:"wl-reading-col-palette-swatch"});u.style.backgroundColor=d,u.style.borderColor=c,r===c&&u.addClass("is-active"),u.title=c,u.addEventListener("click",h=>{h.stopPropagation(),a.remove(),this.updateColumn({...e,color:c})})}let o=c=>{!a.contains(c.target)&&c.target!==t&&(a.remove(),n.removeEventListener("mousedown",o,!0))};window.setTimeout(()=>n.addEventListener("mousedown",o,!0),0)}renderAddSection(t){let e=t.createDiv({cls:"wl-reading-add-col"});e.createDiv({cls:"wl-reading-section-label",text:"Add field"});let i=e.createDiv({cls:"wl-reading-add-col-row"}),a=i.createEl("input",{cls:"wl-modal-input wl-reading-add-col-name",attr:{type:"text",placeholder:"Field name"}}),n=i.createEl("select",{cls:"wl-select wl-reading-add-col-type"});for(let c of["text","number","select"])n.createEl("option",{value:c,text:c.charAt(0).toUpperCase()+c.slice(1)});let r=i.createEl("input",{cls:"wl-modal-input wl-reading-add-col-opts",attr:{type:"text",placeholder:"Options (comma-separated)"}}),o=()=>{r.style.display=n.value==="select"?"":"none"};n.addEventListener("change",o),o(),i.createEl("button",{cls:"wl-reading-add-btn wl-btn-success wl-reading-add-col-btn",text:"Add"}).addEventListener("click",()=>{let c=a.value.trim();if(!c){new Ne.Notice("Enter a field name first.");return}let d=n.value,u=d==="select"?r.value.split(",").map(p=>p.trim()).filter(Boolean):[],h={id:this.readingData.generateColumnId(this.kind,c),name:c,type:d,options:u,color:It};this.addColumn(h).then(()=>{a.value="",r.value="",n.value="text",o()})})}async addColumn(t){await this.readingData.addColumn(this.kind,t),this.renderList(),this.onChanged()}async updateColumn(t){await this.readingData.updateColumn(this.kind,t),this.renderList(),this.onChanged()}async deleteColumn(t){await this.readingData.removeColumn(this.kind,t),this.renderList(),this.onChanged()}async reorder(t,e){await this.readingData.reorderColumns(this.kind,t,e),this.renderList(),this.onChanged()}};function Ha(g){var r,o,l;let s=g.match(/(^|\n)## Quotes[ \t]*\r?\n([\s\S]*?)(?=\n## |\n# |$)/);if(!s)return[];let t=(r=s[2])!=null?r:"",e=[],i=t.split(/\r?\n/),a=null,n=()=>{if(a){let c=a.body.join(`
+`).trim();c&&e.push({reference:a.reference,text:c})}a=null};for(let c of i){let d=c.match(/^>\s*\[!quote\](.*)$/i);if(d){n(),a={reference:((o=d[1])!=null?o:"").trim(),body:[]};continue}if(a){let u=c.match(/^>\s?(.*)$/);u?a.body.push((l=u[1])!=null?l:""):(c.trim(),n())}}return n(),e}var He=class extends it.Modal{constructor(t,e,i,a,n,r){super(t);this.starsWrapEl=null;this.plugin=e,this.readingData=i,this.mode=a,this.id=n,this.onChanged=r;let o=this.getItem();this.draft={pagesRead:o&&this.mode==="book"?o.pagesRead:0,chaptersRead:o?o.chaptersRead:0,volumesRead:o&&this.mode==="manga"?o.volumesRead:0},this.openSnapshot={...this.draft}}getItem(){return this.mode==="book"?this.readingData.getBook(this.id):this.readingData.getManga(this.id)}onOpen(){var e;let t=(e=this.plugin.settings.colorTheme)!=null?e:"default";this.modalEl.setAttribute("data-theme",t),this.contentEl.setAttribute("data-theme",t),this.contentEl.addClass("wl-view"),this.contentEl.addClass("wl-detail-modal"),this.contentEl.addClass("wl-reading-detail"),this.renderAll()}onClose(){this.commitOnClose(),this.contentEl.empty()}async commitOnClose(){if(!(this.draft.pagesRead!==this.openSnapshot.pagesRead||this.draft.chaptersRead!==this.openSnapshot.chaptersRead||this.draft.volumesRead!==this.openSnapshot.volumesRead))return;let e=this.getItem();if(e){if(this.mode==="book"){let a={...e,pagesRead:this.draft.pagesRead,chaptersRead:this.draft.chaptersRead};this.applyAutoComplete(a),await this.readingData.updateBook(a)}else{let a={...e,chaptersRead:this.draft.chaptersRead,volumesRead:this.draft.volumesRead};this.applyAutoComplete(a),await this.readingData.updateManga(a)}this.logProgressChange(e),this.onChanged()}}logProgressChange(t){var a,n,r,o;let e=this.openSnapshot,i=this.mode==="book"?"Book":"Manga";if(this.mode==="book"){let l=t;this.draft.pagesRead!==e.pagesRead&&l.totalPages>0&&((a=this.plugin.historyManager)==null||a.log(`${t.title} (${i}) At page ${this.draft.pagesRead} / ${l.totalPages}`,{source:"Reading",action:"watched",titleName:t.title})),this.draft.chaptersRead!==e.chaptersRead&&l.totalChapters>0&&((n=this.plugin.historyManager)==null||n.log(`${t.title} (${i}) At chapter ${this.draft.chaptersRead} / ${l.totalChapters}`,{source:"Reading",action:"watched",titleName:t.title}))}else{let l=t;this.draft.chaptersRead!==e.chaptersRead&&l.totalChapters>0&&((r=this.plugin.historyManager)==null||r.log(`${t.title} (${i}) At chapter ${this.draft.chaptersRead} / ${l.totalChapters}`,{source:"Reading",action:"watched",titleName:t.title})),this.draft.volumesRead!==e.volumesRead&&l.totalVolumes>0&&((o=this.plugin.historyManager)==null||o.log(`${t.title} (${i}) At volume ${this.draft.volumesRead} / ${l.totalVolumes}`,{source:"Reading",action:"watched",titleName:t.title}))}}renderAll(){this.contentEl.empty();let t=this.getItem();if(!t){this.contentEl.createDiv({text:"Item no longer exists."});return}this.renderHeader(t),this.renderProgressSection(t),this.renderDetailsSection(t),this.contentEl.createDiv({cls:"wl-reading-modal-divider"}),this.renderCustomFieldsSection(t),this.contentEl.createDiv({cls:"wl-reading-modal-divider"}),this.renderQuotesSection(t),this.renderFooter(t)}renderHeader(t){let e=this.contentEl.createDiv({cls:"wl-reading-detail-header"}),i=e.createDiv({cls:"wl-reading-detail-cover"});t.coverUrl?i.createEl("img",{cls:"wl-reading-detail-cover-img",attr:{src:t.coverUrl,alt:t.title}}):(i.style.backgroundColor=Ls(t.id),i.createSpan({cls:"wl-reading-detail-cover-icon",text:this.mode==="book"?"\u{1F4D6}":"\u{1F4D3}"}));let a=e.createDiv({cls:"wl-reading-detail-info"}),n=a.createDiv({cls:"wl-reading-detail-title-wrap"});this.renderTitleDisplay(n);let r=a.createDiv({cls:"wl-reading-detail-author-wrap"});this.renderAuthorDisplay(r);let o=a.createDiv({cls:"wl-reading-detail-meta-row"}),l=o.createSpan({cls:"wl-reading-detail-status-wrap"});this.renderStatusBadge(l),this.starsWrapEl=o.createDiv({cls:"wl-stars wl-reading-detail-stars"}),this.renderStars();let c=a.createDiv({cls:"wl-reading-detail-actions"});this.renderExternalLinkActions(c);let d=a.createDiv({cls:"wl-reading-detail-vault-row"});this.renderVaultRow(d)}renderTitleDisplay(t){var a;t.empty();let e=this.getItem(),i=t.createEl("h2",{cls:"wl-reading-detail-title wl-reading-detail-editable-text",text:(a=e==null?void 0:e.title)!=null?a:""});i.title="Click to edit",i.addEventListener("click",()=>this.editTitle(t))}editTitle(t){var n;t.empty();let e=this.getItem(),i=t.createEl("input",{cls:"wl-modal-input wl-reading-detail-title-input",attr:{type:"text"}});i.value=(n=e==null?void 0:e.title)!=null?n:"";let a=()=>{let r=i.value.trim();if(!r){this.renderTitleDisplay(t);return}(async()=>{let o=this.getItem();o&&r!==o.title&&(this.mode==="book"?await this.readingData.updateBook({...o,title:r}):await this.readingData.updateManga({...o,title:r}),this.onChanged()),this.renderTitleDisplay(t)})()};i.addEventListener("blur",a),i.addEventListener("keydown",r=>{r.key==="Enter"?(r.preventDefault(),i.blur()):r.key==="Escape"&&(r.preventDefault(),this.renderTitleDisplay(t))}),i.focus(),i.select()}renderAuthorDisplay(t){t.empty();let e=this.getItem(),i=t.createDiv({cls:"wl-reading-detail-author wl-reading-detail-editable-text",text:(e==null?void 0:e.author)||"\u2014"});i.title="Click to edit",i.addEventListener("click",()=>this.editAuthor(t))}editAuthor(t){var n;t.empty();let e=this.getItem(),i=t.createEl("input",{cls:"wl-modal-input wl-reading-detail-author-input",attr:{type:"text"}});i.value=(n=e==null?void 0:e.author)!=null?n:"";let a=()=>{let r=i.value.trim();(async()=>{let o=this.getItem();o&&r!==o.author&&(this.mode==="book"?await this.readingData.updateBook({...o,author:r}):await this.readingData.updateManga({...o,author:r}),this.onChanged()),this.renderAuthorDisplay(t)})()};i.addEventListener("blur",a),i.addEventListener("keydown",r=>{r.key==="Enter"?(r.preventDefault(),i.blur()):r.key==="Escape"&&(r.preventDefault(),this.renderAuthorDisplay(t))}),i.focus(),i.select()}renderStatusBadge(t){var n;t.empty();let e=this.getItem(),i=(n=e==null?void 0:e.status)!=null?n:"Plan to Read",a=t.createSpan({cls:"wl-reading-detail-status",text:i});a.style.backgroundColor=re(i),a.title="Click to change status",a.addEventListener("click",r=>{r.stopPropagation(),this.openStatusDropdown(a,t)})}openStatusDropdown(t,e){this.contentEl.querySelectorAll(".wl-reading-status-dropdown").forEach(o=>o.remove());let i=t.getBoundingClientRect(),a=this.contentEl.createDiv({cls:"wl-reading-status-dropdown"});a.style.top=`${i.bottom+4}px`,a.style.left=`${i.left}px`;let n=this.contentEl.ownerDocument;for(let o of lt){let l=a.createDiv({cls:"wl-reading-status-option"}),c=l.createSpan({cls:"wl-reading-status-option-dot"});c.style.backgroundColor=re(o),l.createSpan({text:o}),l.addEventListener("click",()=>{a.remove(),n.removeEventListener("mousedown",r,!0),this.saveStatus(o).then(()=>this.renderStatusBadge(e))})}let r=o=>{a.contains(o.target)||(a.remove(),n.removeEventListener("mousedown",r,!0))};window.setTimeout(()=>n.addEventListener("mousedown",r,!0),0)}async saveStatus(t){let e=this.getItem();if(!e)return;let i={status:t};if(t==="Completed"&&!e.dateFinished){let a=new Date,n=a.getFullYear(),r=String(a.getMonth()+1).padStart(2,"0"),o=String(a.getDate()).padStart(2,"0");i.dateFinished=`${n}-${r}-${o}`}this.mode==="book"?await this.readingData.updateBook({...e,...i}):await this.readingData.updateManga({...e,...i}),this.onChanged()}renderExternalLinkActions(t){var n;t.empty();let e=this.getItem();if(!e)return;let i=!!e.externalLink;if(t.createEl("button",{cls:"wl-btn wl-btn-sm",text:i?"Change link":"External link"}).addEventListener("click",()=>{var u;t.empty();let r=t.createDiv({cls:"wl-reading-external-link-wrap"}),o=r.createEl("input",{cls:"wl-modal-input wl-reading-external-link-input",attr:{type:"url",placeholder:"https://\u2026"}});o.value=(u=e.externalLink)!=null?u:"";let l=r.createEl("button",{cls:"wl-reading-add-btn wl-btn-success",text:"Save"}),c=r.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Cancel"}),d=()=>{let h=o.value.trim();(async()=>{let p=this.getItem();p&&(this.mode==="book"?await this.readingData.updateBook({...p,externalLink:h}):await this.readingData.updateManga({...p,externalLink:h}),this.onChanged(),this.renderExternalLinkActions(t))})()};l.addEventListener("click",d),c.addEventListener("click",()=>this.renderExternalLinkActions(t)),o.addEventListener("keydown",h=>{h.key==="Enter"?(h.preventDefault(),d()):h.key==="Escape"&&(h.preventDefault(),this.renderExternalLinkActions(t))}),o.focus()}),i){let r=t.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Open"});r.title=(n=e.externalLink)!=null?n:"",r.addEventListener("click",()=>{let o=this.getItem();o!=null&&o.externalLink&&activeWindow.open(o.externalLink,"_blank","noopener,noreferrer")})}}renderVaultRow(t){t.empty();let e=this.getItem();if(!e)return;let i=t.createEl("button",{cls:"wl-btn wl-btn-sm",text:e.vaultPage?"Change":"Link"});if(i.title=e.vaultPage?`Linked: ${e.vaultPage}`:"Link a vault note to this entry",i.addEventListener("click",()=>{let a=this.plugin.app.vault.getMarkdownFiles();new We(this.plugin.app,a,n=>{this.updateVaultPage(n.path).then(()=>this.renderVaultRow(t))}).open()}),e.vaultPage){let a=t.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Open"});a.title="Open the linked vault page in a new tab",a.addEventListener("click",()=>this.openLinkedVaultPage())}if(t.createSpan({cls:"wl-reading-detail-vault-label",text:"Vault page:"}),e.vaultPage){let a=e.vaultPage.split("/").pop()||e.vaultPage,n=t.createSpan({cls:"wl-reading-detail-vault-path",text:a});n.title=e.vaultPage}else t.createSpan({cls:"wl-reading-detail-vault-empty",text:"not linked"})}async updateVaultPage(t){let e=this.getItem();if(e)if(this.mode==="book"){let i={...e,vaultPage:t};await this.readingData.updateBook(i)}else{let i={...e,vaultPage:t};await this.readingData.updateManga(i)}}openLinkedVaultPage(){let t=this.getItem();if(!t||!t.vaultPage)return;let e=this.plugin.app.vault.getAbstractFileByPath(t.vaultPage);e instanceof it.TFile?(this.plugin.app.workspace.getLeaf("tab").openFile(e),this.close()):new it.Notice("Linked vault page no longer exists.")}renderStars(){if(!this.starsWrapEl)return;this.starsWrapEl.empty();let t=this.getItem();if(t)for(let e=1;e<=5;e++)this.starsWrapEl.createSpan({cls:`wl-star${t.rating>=e?" is-active":""}`,text:"\u2605"}).addEventListener("click",()=>{let a=this.getItem();if(!a)return;let n=a.rating===e?0:e;(async()=>{if(this.mode==="book"){let r={...a,rating:n};await this.readingData.updateBook(r)}else{let r={...a,rating:n};await this.readingData.updateManga(r)}this.renderStars()})()})}renderProgressSection(t){let e=this.contentEl.createDiv({cls:"wl-reading-detail-progress"});e.createDiv({cls:"wl-reading-section-label",text:"Progress"});let i=e.createDiv({cls:"wl-reading-arc-row"});this.mode==="book"?(this.renderArcGauge(i,"pages","#8b5cf6",()=>this.draft.pagesRead,a=>{this.draft.pagesRead=a},()=>{var a,n;return(n=(a=this.getItem())==null?void 0:a.totalPages)!=null?n:0},a=>this.commitTotal("totalPages",a)),this.renderArcGauge(i,"chapters","#06b6d4",()=>this.draft.chaptersRead,a=>{this.draft.chaptersRead=a},()=>{var a,n;return(n=(a=this.getItem())==null?void 0:a.totalChapters)!=null?n:0},a=>this.commitTotal("totalChapters",a))):(this.renderArcGauge(i,"chapters","#06b6d4",()=>this.draft.chaptersRead,a=>{this.draft.chaptersRead=a},()=>{var a,n;return(n=(a=this.getItem())==null?void 0:a.totalChapters)!=null?n:0},a=>this.commitTotal("totalChapters",a)),this.renderArcGauge(i,"volumes","#f59e0b",()=>this.draft.volumesRead,a=>{this.draft.volumesRead=a},()=>{var a,n;return(n=(a=this.getItem())==null?void 0:a.totalVolumes)!=null?n:0},a=>this.commitTotal("totalVolumes",a)))}async commitTotal(t,e){let i=this.getItem();i&&(this.mode==="book"?await this.readingData.updateBook({...i,[t]:e}):await this.readingData.updateManga({...i,[t]:e}),this.onChanged())}renderArcGauge(t,e,i,a,n,r,o){let l=t.createDiv({cls:"wl-reading-arc-col"}),c=100,d=8,u=(c-d)/2,h=c/2,p=c/2,m=l.createDiv({cls:"wl-reading-arc-svg-wrap"}),v=activeDocument.createElementNS("http://www.w3.org/2000/svg","svg");v.setAttribute("viewBox",`0 0 ${c} ${c}`),v.setAttribute("class","wl-reading-arc-svg"),m.appendChild(v);let f=Math.PI*u,y=activeDocument.createElementNS("http://www.w3.org/2000/svg","path"),w=`M ${h-u} ${p} A ${u} ${u} 0 1 1 ${h+u} ${p}`;y.setAttribute("d",w),y.setAttribute("fill","none"),y.setAttribute("stroke","#2a2a2a"),y.setAttribute("stroke-width",String(d)),y.setAttribute("stroke-linecap","round"),v.appendChild(y);let b=activeDocument.createElementNS("http://www.w3.org/2000/svg","path");b.setAttribute("d",w),b.setAttribute("fill","none"),b.setAttribute("stroke",i),b.setAttribute("stroke-width",String(d)),b.setAttribute("stroke-linecap","round"),b.setAttribute("stroke-dasharray",String(f)),v.appendChild(b);let S=activeDocument.createElementNS("http://www.w3.org/2000/svg","text");S.setAttribute("x",String(h)),S.setAttribute("y",String(p+4)),S.setAttribute("text-anchor","middle"),S.setAttribute("class","wl-reading-arc-pct"),v.appendChild(S);let D=A=>{let P=r();return isNaN(A)||A<0?0:P>0&&A>P?P:A},E=l.createDiv({cls:"wl-reading-arc-controls"}),k=E.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-step-btn",text:"\u22121"}),C=E.createEl("input",{cls:"wl-modal-input wl-modal-input-sm wl-reading-arc-input",attr:{type:"number",min:"0",max:String(r())}}),x=E.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-step-btn",text:"+1"}),T=l.createDiv({cls:"wl-reading-arc-label"}),M=()=>{let A=r(),P=a();C.value=String(P),C.setAttribute("max",String(A));let I=A>0?Math.min(1,P/A):0,F=f*(1-I);b.setAttribute("stroke-dashoffset",String(F)),S.textContent=`${Math.round(I*100)}%`},L=()=>{T.empty(),T.createSpan({text:"of "});let A=T.createSpan({cls:"wl-reading-arc-total wl-reading-detail-editable-text",text:String(r())});A.title="Click to edit",A.addEventListener("click",()=>j()),T.createSpan({text:` ${e}`})},j=()=>{T.empty(),T.createSpan({text:"of "});let A=T.createEl("input",{cls:"wl-modal-input wl-modal-input-sm wl-reading-arc-total-input",attr:{type:"number",min:"0"}});A.value=String(r()),T.createSpan({text:` ${e}`});let P=!1,I=()=>{if(P)return;P=!0;let F=parseInt(A.value,10),_=isNaN(F)||F<0?0:F;(async()=>(_!==r()&&(await o(_),M()),L()))()};A.addEventListener("blur",I),A.addEventListener("keydown",F=>{F.key==="Enter"?(F.preventDefault(),A.blur()):F.key==="Escape"&&(F.preventDefault(),P=!0,L())}),A.focus(),A.select()};L(),M(),C.addEventListener("input",()=>{let A=D(parseInt(C.value,10));n(A),M()}),k.addEventListener("click",()=>{n(D(a()-1)),M()}),x.addEventListener("click",()=>{n(D(a()+1)),M()})}renderDetailsSection(t){let e=this.contentEl.createDiv({cls:"wl-reading-detail-details"});e.createDiv({cls:"wl-reading-section-label",text:"Details"});let i=e.createDiv({cls:"wl-reading-detail-grid"});this.renderIdCell(i,t),this.renderEditableDateCell(i,"Started",()=>{var a,n;return(n=(a=this.getItem())==null?void 0:a.dateStarted)!=null?n:null},async a=>{let n=this.getItem();n&&(this.mode==="book"?await this.readingData.updateBook({...n,dateStarted:a}):await this.readingData.updateManga({...n,dateStarted:a}),this.onChanged())}),this.renderAddedReleaseCell(i,t),this.renderEditableDateCell(i,"Finished",()=>{var a,n;return(n=(a=this.getItem())==null?void 0:a.dateFinished)!=null?n:null},async a=>{let n=this.getItem();n&&(this.mode==="book"?await this.readingData.updateBook({...n,dateFinished:a}):await this.readingData.updateManga({...n,dateFinished:a}),this.onChanged())})}renderIdCell(t,e){let i=this.mode==="book"?"Google Books ID":"MAL ID",a=this.makeDetailCell(t,i);a.addClass("wl-reading-detail-id-value"),this.fillIdValue(a,e)}idText(t){return this.mode==="book"?t.googleBooksId:t.malId}resolveItemLink(t){if(t.externalLink)return t.externalLink;if(this.mode==="manga"){let i=t.malId;return i?`https://myanimelist.net/manga/${encodeURIComponent(i)}`:""}let e=t.googleBooksId;return e?`https://books.google.com/books?id=${encodeURIComponent(e)}`:""}fillIdValue(t,e){t.empty();let i=this.idText(e),a=this.resolveItemLink(e);i?a?t.createEl("a",{cls:"wl-reading-detail-link",text:i,attr:{href:a,target:"_blank",rel:"noopener noreferrer"}}):t.createSpan({text:i}):t.createSpan({text:"\u2014"}),t.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-detail-id-edit",text:"\u270E",attr:{title:this.mode==="book"?"Edit Google Books ID":"Edit MAL ID"}}).addEventListener("click",()=>this.editIdLink(t,e))}editIdLink(t,e){t.empty();let i=t.createDiv({cls:"wl-reading-external-link-wrap"}),a=i.createEl("input",{cls:"wl-modal-input wl-reading-external-link-input",attr:{type:"text",placeholder:this.mode==="book"?"Google Books volume ID":"MAL manga ID"}});a.value=this.idText(e);let n=i.createEl("button",{cls:"wl-reading-add-btn wl-btn-success",text:"Save"}),r=i.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Cancel"}),o=()=>{let c=this.getItem();c&&this.fillIdValue(t,c)},l=()=>{let c=a.value.trim();(async()=>{let d=this.getItem();if(d){if(c===this.idText(d)){o();return}this.mode==="book"?await this.readingData.updateBook({...d,googleBooksId:c}):await this.readingData.updateManga({...d,malId:c}),this.onChanged(),o(),c&&await this.refetchCoverFromId(c)}})()};n.addEventListener("click",l),r.addEventListener("click",o),a.addEventListener("keydown",c=>{c.key==="Enter"?(c.preventDefault(),l()):c.key==="Escape"&&(c.preventDefault(),o())}),a.focus()}async refetchCoverFromId(t){try{if(this.mode==="book"){if(!this.plugin.apiService.hasGoogleBooksKey()){new it.Notice("Google Books API key required \u2014 add one in Settings \u2192 API \u2192 Books.");return}let e=await this.plugin.apiService.getGoogleBookById(t);if(!e||!e.coverUrl){new it.Notice("This volume doesn't have a cover \u2014 try a different edition's ID.");return}let i=this.getItem();if(!i)return;await this.readingData.updateBook({...i,coverUrl:e.coverUrl})}else{let e=await this.plugin.apiService.getMangaByMalId(Number(t));if(!e||!e.coverUrl){new it.Notice("This manga doesn't have a cover \u2014 try a different ID.");return}let i=this.getItem();if(!i)return;await this.readingData.updateManga({...i,coverUrl:e.coverUrl})}this.onChanged()}catch(e){new it.Notice(this.mode==="book"?Et(e):"Failed to fetch cover.")}}renderAddedReleaseCell(t,e){var l,c;let i=t.createDiv({cls:"wl-reading-detail-cell wl-reading-detail-cell-pair"}),a=i.createDiv({cls:"wl-reading-detail-pair-col"});a.createDiv({cls:"wl-reading-detail-cell-label",text:"Added"}),a.createDiv({cls:"wl-reading-detail-cell-value",text:Q(e.dateAdded.slice(0,10))});let n=i.createDiv({cls:"wl-reading-detail-pair-col"});n.createDiv({cls:"wl-reading-detail-cell-label",text:"Release"});let r=n.createEl("input",{cls:"wl-reading-detail-date-input",attr:{type:"text",placeholder:"Dd/mm/yyyy",maxlength:"10"}}),o=(c=(l=this.getItem())==null?void 0:l.releaseDate)!=null?c:null;r.value=o?Q(o):"",r.addEventListener("change",()=>{let d=r.value.trim(),u=null;if(d&&(u=q(d),!u)){r.addClass("wl-input-error");return}r.removeClass("wl-input-error"),(async()=>{let h=this.getItem();h&&(this.mode==="book"?await this.readingData.updateBook({...h,releaseDate:u}):await this.readingData.updateManga({...h,releaseDate:u}),this.onChanged(),this.renderAll())})()})}makeDetailCell(t,e){let i=t.createDiv({cls:"wl-reading-detail-cell"});return i.createDiv({cls:"wl-reading-detail-cell-label",text:e}),i.createDiv({cls:"wl-reading-detail-cell-value"})}renderEditableDateCell(t,e,i,a){let n=t.createDiv({cls:"wl-reading-detail-cell"});n.createDiv({cls:"wl-reading-detail-cell-label",text:e});let r=n.createDiv({cls:"wl-reading-detail-cell-date"}),o=r.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-detail-today-btn",text:"Today",attr:{title:"Fill with today's date"}}),l=r.createEl("input",{cls:"wl-reading-detail-date-input",attr:{type:"text",placeholder:"Dd/mm/yyyy",maxlength:"10"}}),c=i();l.value=c?Q(c):"";let d=()=>{o.toggleClass("is-dimmed",!!l.value.trim())};d(),o.addEventListener("click",()=>{if(l.value.trim())return;let u=new Date,h=String(u.getDate()).padStart(2,"0"),p=String(u.getMonth()+1).padStart(2,"0");l.value=`${h}/${p}/${u.getFullYear()}`,d(),l.dispatchEvent(new Event("change"))}),l.addEventListener("change",()=>{let u=l.value.trim();if(!u){l.removeClass("wl-input-error"),d(),a(null);return}let h=q(u);if(!h){l.addClass("wl-input-error");return}l.removeClass("wl-input-error"),d(),a(h)})}renderFooter(t){let e=this.contentEl.createDiv({cls:"wl-reading-detail-footer"});e.createDiv({cls:"wl-reading-detail-footer-left"}).createEl("button",{cls:"wl-delete-btn wl-btn-danger wl-reading-detail-delete",text:"Delete"}).addEventListener("click",()=>{new H(this.plugin.app,`Delete "${t.title}"?`,()=>{(async()=>(this.mode==="book"?await this.readingData.removeBook(t.id):await this.readingData.removeManga(t.id),this.onChanged(),this.close()))()}).open()}),e.createDiv({cls:"wl-reading-detail-footer-right"}).createEl("button",{cls:"wl-reading-add-btn wl-btn-success",text:"Update progress"}).addEventListener("click",()=>void this.commitDraft())}getColumns(){return this.mode==="book"?this.readingData.getBookColumns():this.readingData.getMangaColumns()}openManageColumns(){new Nt(this.plugin.app,this.plugin,this.readingData,this.mode,()=>this.renderAll()).open()}renderCustomFieldsSection(t){var d,u;let e=this.contentEl.createDiv({cls:"wl-reading-detail-custom"}),i=e.createDiv({cls:"wl-reading-detail-custom-heading"});i.createSpan({cls:"wl-reading-section-label",text:"Custom fields"}),i.createEl("a",{cls:"wl-reading-detail-custom-manage",text:"Manage"}).addEventListener("click",h=>{h.preventDefault(),this.openManageColumns()});let n=this.getColumns();if(n.length===0){let h=e.createDiv({cls:"wl-reading-detail-custom-empty"});h.createSpan({text:"No custom fields \u2014 add some via "}),h.createEl("a",{cls:"wl-reading-detail-custom-manage",text:"Manage"}).addEventListener("click",m=>{m.preventDefault(),this.openManageColumns()}),h.createSpan({text:"."});return}let r=this.readingData.getSettings(),l=(this.mode==="book"?(d=r.bookCustomFieldStyle)!=null?d:"fill":(u=r.mangaCustomFieldStyle)!=null?u:"fill")==="border"?"wl-reading-custom-table is-border-mode":"wl-reading-custom-table",c=e.createDiv({cls:l});for(let h of n)this.renderCustomFieldRow(c,t,h)}renderCustomFieldRow(t,e,i){var u,h,p,m,v;let a=t.createDiv({cls:"wl-reading-custom-row"}),n=a.createDiv({cls:"wl-reading-custom-label",text:i.name}),r=a.createDiv({cls:"wl-reading-custom-value"}),o=this.readingData.getSettings(),l=this.mode==="book"?(u=o.bookCustomFieldStyle)!=null?u:"fill":(h=o.mangaCustomFieldStyle)!=null?h:"fill",c=(p=i.color)!=null?p:It;if(l==="fill"){let f=(m=$s[c])!=null?m:"#F1EFE8";n.style.backgroundColor=f,n.style.color="#1e1e1e"}else n.style.border=`1.5px solid ${c}`,n.style.boxSizing="border-box",r.style.border="0.5px solid var(--background-modifier-border)",r.style.boxSizing="border-box";let d=(v=e.customFields)==null?void 0:v[i.id];this.renderCustomValueDisplay(r,e,i,d)}renderCustomValueDisplay(t,e,i,a){t.empty();let n=a!=null&&a!=="";t.createSpan({cls:`wl-reading-custom-display${n?"":" is-placeholder"}`,text:n?String(a):"\u2014"}).addEventListener("click",()=>{this.renderCustomValueEditor(t,e,i,a)})}renderCustomValueEditor(t,e,i,a){if(t.empty(),i.type==="select"){let o=t.createEl("select",{cls:"wl-select wl-reading-custom-input"}),l=o.createEl("option",{value:"",text:"\u2014"});(a==null||a==="")&&(l.selected=!0);for(let d of i.options){let u=o.createEl("option",{value:d,text:d});String(a!=null?a:"")===d&&(u.selected=!0)}let c=()=>{let d=o.value;this.saveCustomField(e,i,d===""?null:d).then(()=>{var h;let u=this.getItem();this.renderCustomValueDisplay(t,u!=null?u:e,i,(h=u==null?void 0:u.customFields)==null?void 0:h[i.id])})};o.addEventListener("change",c),o.addEventListener("blur",c),o.focus();return}let n=t.createEl("input",{cls:"wl-modal-input wl-reading-custom-input",attr:{type:i.type==="number"?"number":"text",placeholder:"Enter value\u2026"}});a!=null&&(n.value=String(a));let r=()=>{let o=n.value.trim(),l;if(o==="")l=null;else if(i.type==="number"){let c=Number(o);l=isNaN(c)?null:c}else l=o;this.saveCustomField(e,i,l).then(()=>{var d;let c=this.getItem();this.renderCustomValueDisplay(t,c!=null?c:e,i,(d=c==null?void 0:c.customFields)==null?void 0:d[i.id])})};n.addEventListener("blur",r),n.addEventListener("keydown",o=>{var l;if(o.key==="Enter")o.preventDefault(),n.blur();else if(o.key==="Escape"){o.preventDefault();let c=this.getItem();this.renderCustomValueDisplay(t,c!=null?c:e,i,(l=c==null?void 0:c.customFields)==null?void 0:l[i.id])}}),n.focus(),n.select()}async saveCustomField(t,e,i){await this.readingData.setCustomField(this.mode,t.id,e.id,i)}async renderQuotesSection(t){let e=this.contentEl.createDiv({cls:"wl-reading-detail-quotes"}),i=e.createDiv({cls:"wl-reading-detail-quotes-heading"});i.createSpan({cls:"wl-reading-section-label",text:"Favorite quotes"});let a=i.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-detail-quotes-add",text:"Add quote"}),n=e.createDiv({cls:"wl-reading-quote-list"}),r=e.createDiv({cls:"wl-reading-quote-form-host"}),o=async()=>{n.empty();try{let l=await this.readingData.readReadingNote(this.mode,t),c=l?Ha(l):[];if(c.length===0)n.createDiv({cls:"wl-reading-quote-empty",text:"No quotes yet."});else for(let d of c)this.renderQuoteCard(n,d)}catch(l){console.warn("[WL] quotes read failed:",l)}};a.addEventListener("click",()=>{r.empty(),this.renderAddQuoteForm(r,t,async()=>{await o()})}),o()}renderQuoteCard(t,e){let i=t.createDiv({cls:"wl-reading-quote-card"});i.createDiv({cls:"wl-reading-quote-text",text:e.text}),e.reference&&i.createDiv({cls:"wl-reading-quote-ref",text:e.reference})}renderAddQuoteForm(t,e,i){t.empty();let a=t.createDiv({cls:"wl-reading-quote-form"}),n=a.createEl("textarea",{cls:"wl-modal-input wl-reading-quote-textarea",attr:{placeholder:"Quote text\u2026",rows:"3"}}),r=a.createEl("input",{cls:"wl-modal-input wl-reading-quote-ref-input",attr:{type:"text",placeholder:"p. 123 or ch. 5 (optional)"}}),o=a.createDiv({cls:"wl-reading-quote-form-actions"});o.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Cancel"}).addEventListener("click",()=>t.empty()),o.createEl("button",{cls:"wl-reading-add-btn wl-btn-success wl-reading-quote-save",text:"Save quote"}).addEventListener("click",()=>{let d=n.value.trim();if(!d){new it.Notice("Enter the quote text first.");return}(async()=>{try{await this.readingData.appendQuote(this.mode,e,d,r.value),t.empty(),await i()}catch(u){console.warn("[WL] appendQuote failed:",u),new it.Notice("Could not save quote.")}})()}),n.focus()}async commitDraft(){let t=this.getItem();if(t){if(this.mode==="book"){let i={...t,pagesRead:this.draft.pagesRead,chaptersRead:this.draft.chaptersRead};this.applyAutoComplete(i),await this.readingData.updateBook(i)}else{let i={...t,chaptersRead:this.draft.chaptersRead,volumesRead:this.draft.volumesRead};this.applyAutoComplete(i),await this.readingData.updateManga(i)}this.logProgressChange(t),this.openSnapshot={...this.draft},new it.Notice("Progress updated."),this.onChanged(),this.close()}}applyAutoComplete(t){if((this.mode==="book"?this.isBookComplete(t):this.isMangaComplete(t))&&t.status!=="Completed"&&(t.status="Completed"),t.status==="Completed"&&!t.dateFinished){let i=new Date,a=i.getFullYear(),n=String(i.getMonth()+1).padStart(2,"0"),r=String(i.getDate()).padStart(2,"0");t.dateFinished=`${a}-${n}-${r}`}}isBookComplete(t){return t.totalPages>0&&t.pagesRead>=t.totalPages||t.totalChapters>0&&t.chaptersRead>=t.totalChapters}isMangaComplete(t){return t.totalChapters>0&&t.chaptersRead>=t.totalChapters}};var mi="__has__",vi="__none__";function _a(g,s){var e;let t=(e=g.customFields)==null?void 0:e[s];return t!=null&&t!==""}function Rs(){return{statusExclude:[],ratingMode:"all",customExclude:{}}}function fi(){return{key:"dateAdded",dir:"desc",secondKey:"none",secondDir:"asc"}}function Ua(g){var t,e;let s=0;g.statusExclude.length>0&&s++,g.ratingMode!=="all"&&s++;for(let i of Object.keys(g.customExclude))((e=(t=g.customExclude[i])==null?void 0:t.length)!=null?e:0)>0&&s++;return s}function re(g){switch(g){case"Reading":return"#1D9E75";case"Completed":return"#7F77DD";case"Plan to Read":return"#E8873A";case"To be released":return"#3A86C8";case"Dropped":return"#E24B4A";default:return"#888780"}}var wi=["#3a4a6b","#4a3a6b","#6b3a4a","#6b5a3a","#3a6b5a","#5a6b3a","#3a5a6b","#6b3a5a"];function Ls(g){var t;let s=0;for(let e=0;e>>0;return(t=wi[s%wi.length])!=null?t:"#3a4a6b"}function yi(g){return g.totalPages>0?Math.min(100,g.pagesRead/g.totalPages*100):0}function bi(g){return g.totalChapters>0?Math.min(100,g.chaptersRead/g.totalChapters*100):0}var at=class at{constructor(s,t,e){this.searchQuery="";this.contentEl=null;this.filterBtn=null;this.sortBtn=null;this.resetBtn=null;this.savedFilterActive=!1;this.savedFilterBtnEl=null;this.openPopover=null;this.popoverCleanup=null;this.activeCleanups=[];this.selectionMode=!1;this.selectedIds=new Set;this.vsScrollContainer=null;this.vsScrollSpacer=null;this.vsGridEl=null;this.vsDisplayItems=[];this.vsScrollHandler=null;this.vsScrollRAF=null;this.vsResizeObserver=null;this.vsLastFirst=-1;this.vsLastLast=-1;this.vsLastScrollTop=0;this.vsPersistentScrollTop=0;this.vsRowHeight=0;this.coverObserver=null;this.observedCovers=new Set;this.coverFetchFailed=new Set;this.container=s,this.plugin=t,this.readingData=e,this.dataChangeListener=()=>this.renderContent(),this.readingData.onChange(this.dataChangeListener)}get activeSubTab(){var s;return at.sessionSubTab!==null?at.sessionSubTab:(s=this.readingData.getSettings().defaultSubTab)!=null?s:"books"}set activeSubTab(s){at.sessionSubTab=s}get filters(){return at.sessionFilters[this.activeSubTab]}get sort(){return at.sessionSort[this.activeSubTab]}activeColumns(){return this.activeSubTab==="books"?this.readingData.getBookColumns():this.readingData.getMangaColumns()}destroy(){this.readingData.offChange(this.dataChangeListener),this.closePopover(),this.destroyVirtualScroll();for(let s of this.activeCleanups)s();this.activeCleanups=[]}render(){this.container.empty(),this.container.addClass("wl-reading-tab"),this.renderSubTabs(),this.renderToolbar(),this.contentEl=this.container.createDiv({cls:"wl-reading-content"}),this.renderContent()}renderSubTabs(){let s=this.container.createDiv({cls:"wl-reading-subtabs"}),t=s.createEl("button",{cls:`wl-reading-subtab${this.activeSubTab==="books"?" is-active":""}`,text:"Books"}),e=s.createEl("button",{cls:`wl-reading-subtab${this.activeSubTab==="manga"?" is-active":""}`,text:"Manga"});t.addEventListener("click",()=>{this.activeSubTab!=="books"&&(this.activeSubTab="books",this.searchQuery="",this.render())}),e.addEventListener("click",()=>{this.activeSubTab!=="manga"&&(this.activeSubTab="manga",this.searchQuery="",this.render())})}renderToolbar(){let s=this.container.createDiv({cls:"wl-reading-toolbar"}),t=s.createDiv({cls:"wl-reading-toolbar-left"}),e=t.createDiv({cls:"wl-reading-search-wrap"}),i=this.activeSubTab==="books"?"Search books...":"Search manga...",a=e.createEl("input",{cls:"wl-reading-search-input",attr:{type:"text",placeholder:i}});a.value=this.searchQuery;let n=0;a.addEventListener("input",()=>{this.searchQuery=a.value,window.clearTimeout(n),n=window.setTimeout(()=>this.renderContent(),200)});let r=this.getSavedFilter();if(r){this.savedFilterActive=this.filtersMatchSaved(r);let u=t.createEl("button",{cls:`wl-btn wl-btn-sm${this.savedFilterActive?" wl-btn-preset-active":""}`,text:"Saved filter"});this.savedFilterBtnEl=u,u.addEventListener("click",()=>{at.sessionFilters[this.activeSubTab]={statusExclude:wt.filter(h=>!r.statusInclude.includes(h)),ratingMode:r.ratingMode,customExclude:{}},this.savedFilterActive=!0,u.addClass("wl-btn-preset-active"),this.refreshFilterButtonBadge(),this.render()})}else this.savedFilterBtnEl=null;if(this.filterBtn=t.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-filter-btn",text:"Filter"}),this.refreshFilterButtonBadge(),this.filterBtn.addEventListener("click",u=>{u.stopPropagation(),this.toggleFilterPopover()}),this.resetBtn=t.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-reset-btn",text:"Reset"}),this.resetBtn.addEventListener("click",()=>{var u;at.sessionFilters[this.activeSubTab]=Rs(),this.deactivateSavedFilter(),this.closePopover(),(u=this.filterBtn)==null||u.removeClass("is-popover-open"),this.refreshFilterButtonBadge(),this.renderContent()}),this.refreshFilterButtonBadge(),this.sortBtn=t.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-sort-btn",text:"Sort"}),this.sortBtn.addEventListener("click",u=>{u.stopPropagation(),this.toggleSortPopover()}),this.selectionMode&&this.selectedIds.size>0&&this.renderSelectionActionBar(t),this.selectionMode){let u=this.selectedIds.size>0,h=t.createEl("button",{cls:"wl-btn wl-btn-sm",text:u?"None":"All"});h.title=u?"Deselect all":"Select all visible",h.addEventListener("click",()=>{if(this.selectedIds.size>0)this.selectedIds.clear();else for(let p of this.vsDisplayItems)this.selectedIds.add(p.id);this.render()})}t.createEl("button",{cls:`wl-btn wl-btn-sm${this.selectionMode?" is-active":""}`,text:"Select"}).addEventListener("click",()=>{this.selectionMode=!this.selectionMode,this.selectedIds.clear(),this.render()});let l=s.createDiv({cls:"wl-reading-toolbar-right"});l.createEl("button",{cls:"wl-btn wl-btn-sm wl-reading-manage-btn",attr:{"aria-label":"Manage columns",title:"Manage columns"},text:"\u2699"}).addEventListener("click",()=>this.openManageColumns()),l.createEl("button",{cls:"wl-reading-add-btn wl-btn-success",text:this.activeSubTab==="books"?"+ Add book":"+ Add manga"}).addEventListener("click",()=>this.openAddModal())}renderSelectionActionBar(s){let t=s.createDiv({cls:"wl-reading-action-bar"}),e=t.createEl("button",{cls:"wl-group-action-btn wl-group-action-btn-delete wl-btn-danger",text:"\u2715"});e.title="Delete selected",e.addEventListener("click",a=>{a.stopPropagation();let n=this.selectedIds.size;new H(this.plugin.app,`Delete ${n} selected item${n!==1?"s":""}? This cannot be undone.`,()=>{(async()=>{let r=Array.from(this.selectedIds);this.activeSubTab==="books"?await this.readingData.removeBooksBatch(r):await this.readingData.removeMangaBatch(r),this.selectedIds.clear(),this.selectionMode=!1,this.render()})()}).open()});let i=t.createEl("select",{cls:"wl-select wl-select-sm"});i.createEl("option",{text:"Status\u2026",value:""});for(let a of lt)i.createEl("option",{text:a,value:a});i.addEventListener("change",()=>{(async()=>{let a=i.value;if(!a)return;let n=this.activeSubTab==="books";for(let r of this.selectedIds)if(n){let o=this.readingData.getBook(r);o&&this.readingData.updateBookSilent({...o,status:a})}else{let o=this.readingData.getManga(r);o&&this.readingData.updateMangaSilent({...o,status:a})}await this.readingData.saveAndNotify(),i.value="",this.selectedIds.clear(),this.selectionMode=!1,this.render()})()})}refreshFilterButtonBadge(){if(!this.filterBtn)return;let s=Ua(this.filters);this.filterBtn.empty(),this.filterBtn.createSpan({text:"Filter"}),s>0&&this.filterBtn.createSpan({cls:"wl-reading-filter-badge",text:String(s)}),this.resetBtn&&(this.resetBtn.style.display=s>0?"":"none")}closePopover(){this.openPopover&&(this.openPopover.remove(),this.openPopover=null),this.popoverCleanup&&(this.popoverCleanup(),this.popoverCleanup=null)}openPopoverAt(s,t,e){this.closePopover();let i=this.container.createDiv({cls:t}),a=s.getBoundingClientRect(),n=this.container.getBoundingClientRect();i.style.position="absolute",i.style.top=`${a.bottom-n.top+4}px`,i.style.left=`${a.left-n.left}px`,i.style.right="auto",e(i),this.openPopover=i;let r=l=>{i.contains(l.target)||this.closePopover()},o=l=>{l.key==="Escape"&&this.closePopover()};window.setTimeout(()=>activeDocument.addEventListener("click",r),0),activeDocument.addEventListener("keydown",o),this.popoverCleanup=()=>{activeDocument.removeEventListener("click",r),activeDocument.removeEventListener("keydown",o)}}toggleFilterPopover(){var s,t,e;if(this.openPopover&&((s=this.filterBtn)!=null&&s.hasClass("is-popover-open"))){this.closePopover(),this.filterBtn.removeClass("is-popover-open");return}(t=this.sortBtn)==null||t.removeClass("is-popover-open"),(e=this.filterBtn)==null||e.addClass("is-popover-open"),this.openPopoverAt(this.filterBtn,"wl-dropdown wl-filters-panel",i=>this.buildFilterPopover(i))}toggleSortPopover(){var s,t,e;if(this.openPopover&&((s=this.sortBtn)!=null&&s.hasClass("is-popover-open"))){this.closePopover(),this.sortBtn.removeClass("is-popover-open");return}(t=this.filterBtn)==null||t.removeClass("is-popover-open"),(e=this.sortBtn)==null||e.addClass("is-popover-open"),this.openPopoverAt(this.sortBtn,"wl-dropdown wl-sorting-panel",i=>this.buildSortPopover(i))}getSavedFilter(){var s,t;return(t=(s=this.readingData.getSettings().savedFilters)==null?void 0:s[this.activeSubTab])!=null?t:null}filtersMatchSaved(s){let t=this.filters;if(Object.values(t.customExclude).some(a=>a.length>0))return!1;let e=new Set(wt.filter(a=>!s.statusInclude.includes(a))),i=new Set(t.statusExclude);if(i.size!==e.size)return!1;for(let a of i)if(!e.has(a))return!1;return t.ratingMode===s.ratingMode}deactivateSavedFilter(){var s;this.savedFilterActive&&(this.savedFilterActive=!1,(s=this.savedFilterBtnEl)==null||s.removeClass("wl-btn-preset-active"))}buildFilterPopover(s){var i;let t={statusExclude:new Set(this.filters.statusExclude),ratingMode:this.filters.ratingMode,customExclude:new Map};for(let a of this.activeColumns())t.customExclude.set(a.id,new Set((i=this.filters.customExclude[a.id])!=null?i:[]));let e={};this.renderFilterPopoverBody(s,t,e)}addFilterSection(s,t,e,i,a){var c,d;let n=s.createDiv({cls:"wl-filter-section"}),r=n.createDiv({cls:"wl-filter-section-header"});r.createSpan({cls:"wl-filter-label",text:i});let o=r.createSpan({cls:"wl-filter-chevron",text:(c=t[e])==null||c?"\u25BC":"\u25B2"}),l=n.createDiv({cls:"wl-filter-section-content"});l.toggleClass("wl-hidden",(d=t[e])!=null?d:!0),r.addEventListener("click",u=>{var p;u.stopPropagation();let h=!((p=t[e])==null||p);t[e]=h,l.toggleClass("wl-hidden",h),o.textContent=h?"\u25BC":"\u25B2"}),a(l)}addMultiSelectBody(s,t,e){let i=[],a=s.createDiv({cls:"wl-filter-checkbox-row"}),n=a.createEl("input",{attr:{type:"checkbox"}});a.createSpan({cls:"wl-filter-all-toggle",text:"\u25C6 All"});let r=()=>{n.checked=i.length>0&&i.every(l=>l.checked)};for(let l of t){let c=s.createDiv({cls:"wl-filter-checkbox-row"}),d=c.createEl("input",{attr:{type:"checkbox"}});d.checked=!e.has(l),c.createSpan({text:l}),i.push(d),d.addEventListener("change",()=>{d.checked?e.delete(l):e.add(l),r()})}r();let o=()=>{if(i.length>0&&i.every(c=>c.checked)){for(let c of i)c.checked=!1;for(let c of t)e.add(c)}else{for(let c of i)c.checked=!0;e.clear()}r()};return n.addEventListener("click",l=>{l.stopPropagation(),o()}),a.addEventListener("click",l=>{l.target!==n&&(l.stopPropagation(),o())}),{selectAll:()=>{for(let l of i)l.checked=!0;e.clear(),r()},deselectAll:()=>{for(let l of i)l.checked=!1;for(let l of t)e.add(l);r()}}}addHasValueBody(s,t){let e=[];for(let[i,a]of[[mi,"Has value"],[vi,"No value"]]){let n=s.createDiv({cls:"wl-filter-checkbox-row"}),r=n.createEl("input",{attr:{type:"checkbox"}});r.checked=!t.has(i),n.createSpan({text:a}),r.addEventListener("change",()=>{r.checked?t.delete(i):t.add(i)}),e.push({key:i,cb:r})}return{selectAll:()=>{for(let i of e)i.cb.checked=!0,t.delete(i.key)},deselectAll:()=>{for(let i of e)i.cb.checked=!1,t.add(i.key)}}}renderFilterPopoverBody(s,t,e){var d;s.empty();let i=[],a=s.createDiv({cls:"wl-filter-global-btns"});a.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Select all"}).addEventListener("click",u=>{u.stopPropagation();for(let h of i)h.selectAll()}),a.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Deselect all"}).addEventListener("click",u=>{u.stopPropagation();for(let h of i)h.deselectAll()});let o=a.createEl("button",{cls:"wl-btn wl-btn-sm",text:this.getSavedFilter()?"Delete":"Save"});o.addEventListener("click",u=>{u.stopPropagation(),(async()=>{var p,m;let h={...(p=this.readingData.getSettings().savedFilters)!=null?p:{}};o.textContent==="Save"?(h[this.activeSubTab]={name:"Saved filter",statusInclude:wt.filter(v=>!t.statusExclude.has(v)),ratingMode:t.ratingMode},await this.readingData.updateSettings({savedFilters:h})):(delete h[this.activeSubTab],await this.readingData.updateSettings({savedFilters:h}),this.savedFilterActive=!1),this.closePopover(),(m=this.filterBtn)==null||m.removeClass("is-popover-open"),this.render()})()}),this.addFilterSection(s,e,"Status","Status",u=>{i.push(this.addMultiSelectBody(u,wt,t.statusExclude))}),this.addFilterSection(s,e,"Rating","Rating",u=>{let h=[{key:"all",label:"All"},{key:"has",label:"Has rating"},{key:"none",label:"No rating"}],p=`wl-reading-rating-${Date.now()}`;for(let m of h){let v=u.createDiv({cls:"wl-filter-checkbox-row"}),f=v.createEl("input",{attr:{type:"radio",name:p}});f.checked=t.ratingMode===m.key,v.createSpan({text:m.label}),f.addEventListener("change",()=>{f.checked&&(t.ratingMode=m.key)})}});for(let u of this.activeColumns()){let h=(d=t.customExclude.get(u.id))!=null?d:new Set;t.customExclude.set(u.id,h),this.addFilterSection(s,e,`custom:${u.id}`,u.name,p=>{u.type==="select"?i.push(this.addMultiSelectBody(p,u.options,h)):i.push(this.addHasValueBody(p,h))})}let l=s.createDiv();l.style.padding="6px 12px 2px",l.createEl("button",{cls:"wl-btn wl-btn-sm wl-filter-apply-btn",text:"Apply"}).addEventListener("click",u=>{var p;u.stopPropagation();let h={};for(let[m,v]of t.customExclude)v.size>0&&(h[m]=Array.from(v));at.sessionFilters[this.activeSubTab]={statusExclude:Array.from(t.statusExclude),ratingMode:t.ratingMode,customExclude:h},this.deactivateSavedFilter(),this.refreshFilterButtonBadge(),this.closePopover(),(p=this.filterBtn)==null||p.removeClass("is-popover-open"),this.renderContent()})}buildSortPopover(s){let t=[{key:"dateAdded",label:"Date added"},{key:"title",label:"Title"},{key:"author",label:"Author"},{key:"rating",label:"Rating"},{key:"progress",label:"Progress"},{key:"status",label:"Status"},{key:"dateStarted",label:"Date started"},{key:"dateFinished",label:"Date finished"}];for(let i of this.activeColumns())t.push({key:`custom:${i.id}`,label:i.name});let e=(i,a,n,r)=>{let o=s.createDiv({cls:"wl-filter-row"});o.createSpan({cls:"wl-filter-label",text:i});let l=o.createEl("select",{cls:"wl-select"});if(!r){let d=l.createEl("option",{text:"None",value:"none"});a==="none"&&(d.selected=!0)}for(let d of t){let u=l.createEl("option",{text:d.label,value:d.key});d.key===a&&(u.selected=!0)}let c=o.createEl("button",{cls:"wl-btn wl-btn-sm wl-sort-dir-btn",text:n==="asc"?"\u2191":"\u2193"});c.title=n==="asc"?"Ascending \u2014 click to switch to descending":"Descending \u2014 click to switch to ascending",l.addEventListener("change",()=>{var d;r?this.sort.key=l.value:this.sort.secondKey=l.value==="none"?"none":l.value,this.closePopover(),(d=this.sortBtn)==null||d.removeClass("is-popover-open"),this.renderContent()}),c.addEventListener("click",d=>{var u;d.stopPropagation(),r?this.sort.dir=this.sort.dir==="asc"?"desc":"asc":this.sort.secondDir=this.sort.secondDir==="asc"?"desc":"asc",this.closePopover(),(u=this.sortBtn)==null||u.removeClass("is-popover-open"),this.renderContent()})};e("Sort by",this.sort.key,this.sort.dir,!0),e("Then by",this.sort.secondKey,this.sort.secondDir,!1)}applyFilters(s){let t=this.filters,e=this.activeColumns();return s.filter(i=>{var a;if(t.statusExclude.includes(i.status)||t.ratingMode==="has"&&!(i.rating>0)||t.ratingMode==="none"&&i.rating>0)return!1;for(let n of e){let r=t.customExclude[n.id];if(!(!r||r.length===0))if(n.type==="select"){let o=(a=i.customFields)==null?void 0:a[n.id],l=o==null?"":String(o);if(l!==""&&r.includes(l))return!1}else{let o=_a(i,n.id);if(o&&r.includes(mi)||!o&&r.includes(vi))return!1}}return!0})}applySearch(s){let t=this.searchQuery.trim();return t?new st(s,{keys:["title","author"],threshold:.4,ignoreLocation:!0}).search(t).map(i=>i.item):s}compareBySortKey(s,t,e,i,a){var n,r,o,l;if(e.startsWith("custom:"))return this.compareCustom(s,t,e.slice(7),i);switch(e){case"title":return s.title.localeCompare(t.title)*i;case"author":return(s.author||"").localeCompare(t.author||"")*i;case"rating":return(s.rating-t.rating)*i;case"dateAdded":return s.dateAdded.localeCompare(t.dateAdded)*i;case"dateStarted":return((n=s.dateStarted)!=null?n:"").localeCompare((r=t.dateStarted)!=null?r:"")*i;case"dateFinished":return((o=s.dateFinished)!=null?o:"").localeCompare((l=t.dateFinished)!=null?l:"")*i;case"status":return s.status.localeCompare(t.status)*i;case"progress":{let c=a?yi(s):bi(s),d=a?yi(t):bi(t);return(c-d)*i}default:return 0}}compareCustom(s,t,e,i){var c,d;let a=this.activeColumns().find(u=>u.id===e),n=(c=s.customFields)==null?void 0:c[e],r=(d=t.customFields)==null?void 0:d[e],o=n==null||n==="",l=r==null||r==="";return o&&l?0:o?1:l?-1:(a==null?void 0:a.type)==="number"?(Number(n)-Number(r))*i:String(n).localeCompare(String(r))*i}applySort(s){let{key:t,dir:e,secondKey:i,secondDir:a}=this.sort,n=this.activeSubTab==="books",r=e==="asc"?1:-1,o=a==="asc"?1:-1;return[...s].sort((l,c)=>{let d=this.compareBySortKey(l,c,t,r,n);return d!==0||i==="none"?d:this.compareBySortKey(l,c,i,o,n)})}renderContent(){if(!this.contentEl)return;this.destroyVirtualScroll();for(let n of this.activeCleanups)n();this.activeCleanups=[],this.contentEl.empty();let t=this.activeSubTab==="books"?this.readingData.getBooks():this.readingData.getMangaList();if(t.length===0){this.renderEmptyState();return}let e=this.applyFilters(t),i=this.applySearch(e),a=this.applySort(i);if(a.length===0){let n=this.searchQuery.trim();this.contentEl.createDiv({cls:"wl-reading-empty-search",text:n?`No matches for "${n}".`:"No items match the current filters."});return}this.renderResultsCount(a.length),this.vsDisplayItems=a,this.renderVirtualGrid()}renderResultsCount(s){if(!this.contentEl)return;let t=this.contentEl.createDiv({cls:"wl-results-count"});t.textContent=`${s} title${s!==1?"s":""}`}destroyVirtualScroll(){this.destroyCoverObserver(),this.vsScrollRAF!==null&&(window.cancelAnimationFrame(this.vsScrollRAF),this.vsScrollRAF=null),this.vsResizeObserver&&(this.vsResizeObserver.disconnect(),this.vsResizeObserver=null),this.vsScrollContainer&&this.vsScrollHandler&&this.vsScrollContainer.removeEventListener("scroll",this.vsScrollHandler),this.vsScrollHandler=null,this.vsScrollContainer=null,this.vsScrollSpacer=null,this.vsGridEl=null,this.vsDisplayItems=[],this.vsLastFirst=-1,this.vsLastLast=-1,this.vsLastScrollTop=0,this.vsRowHeight=0}renderVirtualGrid(){if(!this.contentEl)return;let s=this.contentEl.createDiv({cls:"wl-reading-scroll-container"}),t=s.createDiv({cls:"wl-reading-scroll-spacer"}),e=t.createDiv({cls:"wl-reading-grid wl-reading-grid-virtual"});this.vsScrollContainer=s,this.vsScrollSpacer=t,this.vsGridEl=e,this.vsLastFirst=-1,this.vsLastLast=-1,this.vsScrollHandler=()=>{let i=s.scrollTop;this.vsPersistentScrollTop=i;let a=this.vsRowHeight>0?this.vsRowHeight/2:50;Math.abs(i-this.vsLastScrollTop){this.vsScrollRAF=null,this.renderVisibleCards()})))},s.addEventListener("scroll",this.vsScrollHandler,{passive:!0}),this.vsResizeObserver=new ResizeObserver(()=>{this.renderVisibleCards()}),this.vsResizeObserver.observe(s),this.vsPersistentScrollTop>0&&(this.renderVisibleCards(),s.scrollTop=this.vsPersistentScrollTop,this.vsLastScrollTop=this.vsPersistentScrollTop,this.renderVisibleCards())}getGridMetrics(s){let i=Math.max(1,Math.floor((s+12)/152)),a=(s-12*(i-1))/i,r=a*1.5+72,o=r+12;return{cols:i,cardWidth:a,cardHeight:r,rowHeight:o}}renderVisibleCards(){let s=this.vsScrollContainer,t=this.vsScrollSpacer,e=this.vsGridEl;if(!s||!t||!e)return;let i=s.scrollTop,a=s.clientHeight,n=s.clientWidth;if(n<=0||a<=0)return;let{cols:r,cardHeight:o,rowHeight:l}=this.getGridMetrics(n);this.vsRowHeight=l;let c=this.vsDisplayItems.length,d=Math.ceil(c/r),u=Math.max(0,Math.floor(i/l)-2),h=Math.min(d-1,Math.ceil((i+a)/l)+2),p=u*r,m=Math.min(c-1,(h+1)*r-1);t.style.height=`${d*l}px`,e.style.transform=`translateY(${u*l}px)`,e.style.setProperty("--wl-reading-cols",String(r)),e.style.setProperty("--wl-reading-card-height",`${o}px`);let v=this.vsLastFirst,f=this.vsLastLast;if(p===v&&m===f)return;this.vsLastFirst=p,this.vsLastLast=m,e.empty();let y=this.activeSubTab==="books",w=activeDocument.createDocumentFragment();for(let b=p;b<=m;b++){let S=this.vsDisplayItems[b];if(!S)continue;let D=activeDocument.createElement("div");y?this.renderBookCard(D,S):this.renderMangaCard(D,S);let E=D.firstElementChild;E&&w.appendChild(E)}e.appendChild(w),this.setupCoverObserver()}setupCoverObserver(){let s=this.vsScrollContainer,t=this.vsGridEl;!s||!t||(this.coverObserver||(this.coverObserver=new IntersectionObserver(e=>this.handleCoverIntersection(e),{root:s,rootMargin:"0px",threshold:0})),this.observedCovers.clear(),t.querySelectorAll('.wl-reading-card[data-needs-cover="true"]').forEach(e=>{let i=e;this.observedCovers.has(i)||(this.coverObserver.observe(i),this.observedCovers.add(i))}))}handleCoverIntersection(s){var e;let t=this.activeSubTab==="books"?"book":"manga";for(let i of s){if(!i.isIntersecting)continue;let a=i.target;(e=this.coverObserver)==null||e.unobserve(a),this.observedCovers.delete(a);let n=a.dataset.itemId;if(!n)continue;let r=t==="book"?this.readingData.getBook(n):this.readingData.getManga(n);if(!r||r.coverUrl||this.coverFetchFailed.has(n))continue;let o=a.querySelector(".wl-reading-card-cover");o==null||o.addClass("is-loading"),this.resolveCover(t,r).then(({url:l,sourceId:c})=>{o==null||o.removeClass("is-loading"),l?(c?this.readingData.updateCoverAndSource(t,n,l,c):this.readingData.updateCoverUrl(t,n,l),this.applyCoverToCard(a,l,r.title)):this.coverFetchFailed.add(n)})}}async resolveCover(s,t){var e,i;try{if(s==="book"){let o=t.googleBooksId;if(o){let u=await this.plugin.apiService.getGoogleBookById(o);return{url:(e=u==null?void 0:u.coverUrl)!=null?e:"",sourceId:""}}let l=t.author?`${t.title} ${t.author}`:t.title,d=(await this.plugin.apiService.searchGoogleBooks(l))[0];return!d||!d.coverUrl||!d.googleBooksId?{url:"",sourceId:""}:{url:d.coverUrl,sourceId:d.googleBooksId}}let a=t.malId;if(a){let o=await this.plugin.apiService.getMangaByMalId(Number(a));return{url:(i=o==null?void 0:o.coverUrl)!=null?i:"",sourceId:""}}let r=(await this.plugin.apiService.searchManga(t.title))[0];return!r||!r.coverUrl||!r.malId?{url:"",sourceId:""}:{url:r.coverUrl,sourceId:String(r.malId)}}catch(a){return{url:"",sourceId:""}}}applyCoverToCard(s,t,e){let i=s.querySelector(".wl-reading-card-cover");if(!i)return;let a=i.querySelector(".wl-reading-card-cover-icon"),n=activeDocument.createElement("img");n.className="wl-reading-card-cover-img",n.alt=e,n.loading="lazy",n.onload=()=>{i.style.backgroundColor="",a==null||a.remove()},n.onerror=()=>{n.remove()},i.insertBefore(n,i.firstChild),n.src=t,delete s.dataset.needsCover}destroyCoverObserver(){this.coverObserver&&(this.coverObserver.disconnect(),this.coverObserver=null),this.observedCovers.clear()}renderBookCard(s,t){let e=this.renderCardShell(s,t,"\u{1F4D6}"),i=t.totalPages>0?Math.min(100,Math.round(t.pagesRead/t.totalPages*100)):0;this.renderProgressBar(e,i,t.status);let a=e.createDiv({cls:"wl-reading-card-progress-text"});t.totalPages>0?a.textContent=`${t.pagesRead} / ${t.totalPages} pages`:t.pagesRead>0?a.textContent=`${t.pagesRead} pages`:a.textContent="No progress",this.bindCardClick(e,"book",t.id)}renderMangaCard(s,t){let e=this.renderCardShell(s,t,"\u{1F4D3}"),i=t.totalChapters>0?Math.min(100,Math.round(t.chaptersRead/t.totalChapters*100)):0;this.renderProgressBar(e,i,t.status);let a=e.createDiv({cls:"wl-reading-card-progress-text"}),n=t.totalChapters>0?`Ch. ${t.chaptersRead}/${t.totalChapters}`:t.chaptersRead>0?`Ch. ${t.chaptersRead}`:"",r=t.totalVolumes>0?`Vol. ${t.volumesRead}/${t.totalVolumes}`:t.volumesRead>0?`Vol. ${t.volumesRead}`:"";n&&a.createSpan({cls:"wl-reading-card-progress-piece",text:n}),r&&a.createSpan({cls:"wl-reading-card-progress-piece",text:r}),!n&&!r&&(a.textContent="No progress"),this.bindCardClick(e,"manga",t.id)}bindCardClick(s,t,e){this.selectionMode?(this.selectedIds.has(e)&&s.addClass("is-selected"),s.addEventListener("click",i=>{i.stopPropagation(),this.selectedIds.has(e)?(this.selectedIds.delete(e),s.removeClass("is-selected")):(this.selectedIds.add(e),s.addClass("is-selected")),this.render()})):s.addEventListener("click",()=>this.openDetailModal(t,e))}renderCardShell(s,t,e){let i=s.createDiv({cls:"wl-reading-card"}),a=i.createDiv({cls:"wl-reading-card-cover"});t.coverUrl?a.createEl("img",{cls:"wl-reading-card-cover-img",attr:{src:t.coverUrl,alt:t.title,loading:"lazy"}}):(a.style.backgroundColor=Ls(t.id),a.createSpan({cls:"wl-reading-card-cover-icon",text:e}),this.coverFetchFailed.has(t.id)||(i.dataset.needsCover="true",i.dataset.itemId=t.id));let n=a.createSpan({cls:"wl-reading-card-status-badge",text:t.status});n.style.backgroundColor=re(t.status);let r=i.createEl("button",{cls:"wl-reading-card-menu-btn",text:"\u22EE"});r.setAttr("aria-label","More actions"),r.addEventListener("click",c=>{c.stopPropagation(),this.openCardContextMenu(i,r,t)});let o=i.createDiv({cls:"wl-reading-card-title",text:t.title});o.title=t.title;let l=i.createDiv({cls:"wl-reading-card-author",text:t.author||"\u2014"});return l.title=t.author||"",i}openCardContextMenu(s,t,e){this.container.querySelectorAll(".wl-reading-card-context-menu").forEach(d=>d.remove());let i=s.createDiv({cls:"wl-reading-card-context-menu"}),a=i.createDiv({cls:"wl-reading-card-context-item",text:"Refresh cover"}),n=s.getBoundingClientRect(),r=this.container.getBoundingClientRect();n.right>r.right-180&&i.classList.add("is-right-aligned");let o=s.ownerDocument,l=()=>{i.remove(),o.removeEventListener("click",c,!0)},c=d=>{!i.contains(d.target)&&d.target!==t&&l()};window.setTimeout(()=>o.addEventListener("click",c,!0),0),this.activeCleanups.push(()=>o.removeEventListener("click",c,!0)),a.addEventListener("click",d=>{d.stopPropagation(),l(),this.refreshCover(e)})}refreshCover(s){if((this.activeSubTab==="books"?"book":"manga")==="book"){let i=s;if(!i.googleBooksId){new mt.Notice("No Google Books ID \u2014 cannot refresh cover.");return}if(!this.plugin.apiService.hasGoogleBooksKey()){new mt.Notice("Google Books API key required \u2014 add one in Settings \u2192 API \u2192 Books.");return}(async()=>{try{let a=await this.plugin.apiService.getGoogleBookById(i.googleBooksId);if(!a||!a.coverUrl){new mt.Notice("Could not fetch cover from API.");return}let n=this.readingData.getBook(i.id);if(!n)return;await this.readingData.updateBook({...n,coverUrl:a.coverUrl}),new mt.Notice("Cover refreshed.")}catch(a){new mt.Notice(Et(a))}})()}else{let i=s;if(!i.malId){new mt.Notice("No MAL ID \u2014 cannot refresh cover.");return}(async()=>{try{let a=await this.plugin.apiService.getMangaByMalId(Number(i.malId));if(!a||!a.coverUrl){new mt.Notice("Could not fetch cover from API.");return}let n=this.readingData.getManga(i.id);if(!n)return;await this.readingData.updateManga({...n,coverUrl:a.coverUrl}),new mt.Notice("Cover refreshed.")}catch(a){new mt.Notice("Failed to refresh cover.")}})()}}renderProgressBar(s,t,e){let a=s.createDiv({cls:"wl-reading-card-progress-bar"}).createDiv({cls:"wl-reading-card-progress-fill"});a.style.width=`${t}%`,a.style.backgroundColor=re(e)}renderEmptyState(){if(!this.contentEl)return;let s=this.contentEl.createDiv({cls:"wl-reading-empty"}),t=this.activeSubTab==="books";s.createSpan({cls:"wl-reading-empty-icon",text:t?"\u{1F4D6}":"\u{1F4D3}"});let e=s.createDiv({cls:"wl-reading-empty-msg"});e.createSpan({text:t?"No books yet \u2014 ":"No manga yet \u2014 "}),e.createEl("a",{cls:"wl-reading-empty-link",text:t?"add your first book":"add your first manga"}).addEventListener("click",a=>{a.preventDefault(),this.openAddModal()})}openAddModal(){new Ot(this.plugin.app,this.plugin,this.readingData,this.activeSubTab==="books"?"book":"manga",()=>this.renderContent()).open()}openManageColumns(){new Nt(this.plugin.app,this.plugin,this.readingData,this.activeSubTab==="books"?"book":"manga",()=>this.renderContent()).open()}openDetailModal(s,t){new He(this.plugin.app,this.plugin,this.readingData,s,t,()=>this.renderContent()).open()}};at.sessionSubTab=null,at.sessionFilters={books:Rs(),manga:Rs()},at.sessionSort={books:fi(),manga:fi()};var _e=at;var Va=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],Ga=["January","February","March","April","May","June","July","August","September","October","November","December"],Ka={completed:"#1D9E75",watched:"#1D9E75",added:"#378ADD",deleted:"#E24B4A",status:"#BA7517",rating:"#BA7517"},za={Reading:"#1D9E75",Watchlist:"#378ADD"};function Si(g){if(g.action)return g.action;let s=g.message.toLowerCase();return s.includes("was added")?"added":s.includes("was deleted")?"deleted":s.includes("was reviewed")||s.includes("rating changed")?"rating":s.includes("episode")&&s.includes("watched")?"watched":s.includes("was marked as watched")||s.includes("completed")?"completed":s.includes("status")?"status":"added"}function Ei(g){return g.source?g.source:"Watchlist"}function qa(g){let s=Si(g),t=g.message;switch(s){case"added":return"Added";case"deleted":return"Deleted";case"rating":{let e=t.match(/Rating → (\d+\/5)/);return e?`Rating \u2192 ${e[1]}`:"Rating changed"}case"status":{let e=t.match(/status changed to (.+)$/);return e?`Status \u2192 ${e[1]}`:"Status changed"}case"completed":return"Completed";case"watched":{let e=t.match(/At (page|chapter|volume) (\d+) \/ (\d+)/i);if(e)return`At ${e[1]} ${e[2]} / ${e[3]}`;let i=t.match(/episode (\d+)/i);return i?`Episode ${i[1]} watched`:"Watched"}}return"Updated"}function ja(g){let s=g.message.match(/\(([^)]+)\)/);return s?s[1]:""}function Qa(g){let s;if(g.titleName)s=g.titleName;else{let e=g.message.match(/^(.+?)\s*\(/);s=e?e[1]:g.message}let t=ja(g);return t?`${s} (${t})`:s}function Ya(g){let s=new Date(g+"T12:00:00");return`${Va[s.getDay()]}, ${s.getDate()} ${Ga[s.getMonth()]} ${s.getFullYear()}`}function Ja(g){let s=new Date(g);return`${String(s.getHours()).padStart(2,"0")}:${String(s.getMinutes()).padStart(2,"0")}`}function Xa(g){let s=new Date(g);return`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`}var As=32,Is=40,Ue=class{constructor(s,t){this.sourceFilter="all";this.scrollContainer=null;this.spacer=null;this.viewport=null;this.rows=[];this.rowOffsets=[];this.totalHeight=0;this.scrollHandler=null;this.scrollRAF=null;this.resizeObserver=null;this.lastFirst=-1;this.lastLast=-1;this.renderedNodes=new Map;this.container=s,this.plugin=t}destroy(){this.destroyVirtualScroll()}render(){var e,i;this.destroyVirtualScroll(),this.container.empty(),this.container.addClass("wl-log-tab"),this.renderFilterBar();let s=(i=(e=this.plugin.historyManager)==null?void 0:e.getEntries())!=null?i:[],t=this.sourceFilter==="all"?s:s.filter(a=>Ei(a)===this.sourceFilter);if(t.length===0){this.container.createDiv({cls:"wl-log-empty",text:this.sourceFilter==="all"?"No events yet. Actions from Watchlist and Reading will appear here.":`No ${this.sourceFilter} events yet.`});return}this.buildVirtualRows(t),this.renderVirtualTimeline()}renderFilterBar(){let s=this.container.createDiv({cls:"wl-log-filter-bar"}),t=[{label:"All",value:"all"},{label:"Watchlist",value:"Watchlist"},{label:"Reading",value:"Reading"}];for(let e of t)s.createEl("button",{cls:`wl-log-filter-btn${this.sourceFilter===e.value?" is-active":""}`,text:e.label}).addEventListener("click",()=>{this.sourceFilter!==e.value&&(this.sourceFilter=e.value,this.render())})}buildVirtualRows(s){this.rows=[],this.rowOffsets=[];let t=new Map;for(let a of s){let n=Xa(a.timestamp),r=t.get(n);r||(r=[],t.set(n,r)),r.push(a)}let e=[];for(let[a,n]of t)e.push({date:a,entries:n});let i=0;for(let a of e){this.rows.push({type:"header",date:a.date}),this.rowOffsets.push(i),i+=Is;for(let n=0;n{this.scrollRAF===null&&(this.scrollRAF=window.requestAnimationFrame(()=>{this.scrollRAF=null,this.renderVisibleRows()}))},s.addEventListener("scroll",this.scrollHandler,{passive:!0}),this.resizeObserver=new ResizeObserver(()=>{this.renderedNodes.clear(),this.viewport&&this.viewport.empty(),this.lastFirst=-1,this.lastLast=-1,this.renderVisibleRows()}),this.resizeObserver.observe(s),this.renderVisibleRows()}renderVisibleRows(){var r;let s=this.scrollContainer,t=this.viewport;if(!s||!t)return;let e=s.scrollTop,i=s.clientHeight;if(i<=0)return;let a=this.findFirstVisible(e),n=this.findLastVisible(e+i);if(!(a===this.lastFirst&&n===this.lastLast)){this.lastFirst=a,this.lastLast=n;for(let[o,l]of this.renderedNodes)(on)&&(l.remove(),this.renderedNodes.delete(o));for(let o=a;o<=n;o++){if(this.renderedNodes.has(o))continue;let l=this.rows[o];if(!l)continue;let c;l.type==="header"?(c=activeDocument.createElement("div"),c.className="wl-log-day-header",c.style.height=`${Is}px`,c.textContent=Ya(l.date)):c=this.buildEntryRow(l);let d=(r=this.rowOffsets[o])!=null?r:0;c.style.position="absolute",c.style.top=`${d}px`,c.style.left="0",c.style.right="0",t.appendChild(c),this.renderedNodes.set(o,c)}}}buildEntryRow(s){var f,y;let t=s.entry,e=Si(t),i=(f=Ka[e])!=null?f:"#888780",a=Ei(t),n=(y=za[a])!=null?y:"#888780",r=activeDocument.createElement("div");r.className="wl-log-entry",r.style.height=`${As}px`;let o=activeDocument.createElement("div");o.className="wl-log-dot-col";let l=activeDocument.createElement("div");if(l.className="wl-log-dot",l.style.backgroundColor=n,o.appendChild(l),!s.isLastInGroup){let w=activeDocument.createElement("div");w.className="wl-log-connector",o.appendChild(w)}r.appendChild(o);let c=activeDocument.createElement("div");c.className="wl-log-content";let d=activeDocument.createElement("span");d.className="wl-log-source",d.textContent=a,c.appendChild(d);let u=activeDocument.createElement("span");u.className="wl-log-sep",u.textContent=" \xB7 ",c.appendChild(u);let h=activeDocument.createElement("span");h.className="wl-log-title",h.textContent=Qa(t),c.appendChild(h);let p=activeDocument.createElement("span");p.className="wl-log-sep",p.textContent=" \u2014 ",c.appendChild(p);let m=activeDocument.createElement("span");m.className="wl-log-action",m.textContent=qa(t),m.style.color=i,c.appendChild(m),r.appendChild(c);let v=activeDocument.createElement("div");return v.className="wl-log-time",v.textContent=Ja(t.timestamp),r.appendChild(v),r}findFirstVisible(s){let t=0,e=this.rows.length-1;for(;t>>1;this.rowOffsets[i]+(this.rows[i].type==="header"?Is:As)<=s?t=i+1:e=i}return Math.max(0,t-2)}findLastVisible(s){let t=0,e=this.rows.length-1;for(;t>>1;this.rowOffsets[i]>=s?e=i-1:t=i}return Math.min(this.rows.length-1,t+2)}destroyVirtualScroll(){this.scrollRAF!==null&&(window.cancelAnimationFrame(this.scrollRAF),this.scrollRAF=null),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.scrollContainer&&this.scrollHandler&&this.scrollContainer.removeEventListener("scroll",this.scrollHandler),this.scrollHandler=null,this.scrollContainer=null,this.spacer=null,this.viewport=null,this.renderedNodes.clear(),this.lastFirst=-1,this.lastLast=-1}};var oe="watchlog-view",Di=["wl-dashboard","wl-list","wl-airtime","wl-reading-tab","wl-custom-lists","wl-log-tab"],kt=class extends Ht.ItemView{constructor(t,e,i){super(t);this.dashboardTab=null;this.listTab=null;this.airtimeTab=null;this.readingTab=null;this.customListsTab=null;this.draftsTab=null;this.logTab=null;this.airtimeBtn=null;this.draftsBtn=null;this.tabButtons={};this.tabContentEl=null;this.mobileResizeObserver=null;this.mobileSizerRaf=null;this.lastAppliedTabHeight=-1;this.editPinActive=!1;this.smallestEditHeight=-1;this.pinReleaseTimer=null;this.focusListenersBound=!1;this.overflowObserver=null;this.plugin=e,this.dataManager=i,this.activeTab="dashboard",this.dataChangeListener=()=>{this.refreshActiveTab()}}getViewType(){return oe}getDisplayText(){return"Watchlog"}getIcon(){return"tv"}async onOpen(){await super.onOpen(),this.dataManager.onChange(this.dataChangeListener),this.buildUI(),Ht.Platform.isMobile&&this.setupTabContentSizer()}setupTabContentSizer(){var t;(t=this.mobileResizeObserver)==null||t.disconnect(),this.mobileResizeObserver=new ResizeObserver(()=>{this.mobileSizerRaf===null&&(this.mobileSizerRaf=window.requestAnimationFrame(()=>{this.mobileSizerRaf=null,this.applyTabContentHeight()}))}),this.mobileResizeObserver.observe(this.contentEl),this.focusListenersBound||(this.registerDomEvent(this.contentEl,"focusin",e=>this.onScrollerFocusIn(e)),this.registerDomEvent(this.contentEl,"focusout",e=>this.onScrollerFocusOut(e)),this.focusListenersBound=!0)}isEditorInScroller(t){let e=t;if(!e||typeof e.tagName!="string")return!1;let i=this.contentEl.querySelector(":scope > .wl-tab-content");if(!i||!i.contains(e))return!1;let a=e.tagName.toLowerCase();return a==="input"||a==="textarea"||e.isContentEditable===!0}onScrollerFocusIn(t){!Ht.Platform.isMobile||!this.isEditorInScroller(t.target)||(this.pinReleaseTimer!==null&&(window.clearTimeout(this.pinReleaseTimer),this.pinReleaseTimer=null),this.editPinActive||(this.smallestEditHeight=-1),this.editPinActive=!0,this.lastAppliedTabHeight=-1,this.applyTabContentHeight())}onScrollerFocusOut(t){this.isEditorInScroller(t.target)&&(this.pinReleaseTimer!==null&&window.clearTimeout(this.pinReleaseTimer),this.pinReleaseTimer=window.setTimeout(()=>{this.pinReleaseTimer=null;let e=this.contentEl.ownerDocument.activeElement;this.isEditorInScroller(e)||this.releaseEditPin()},250))}releaseEditPin(){if(!this.editPinActive)return;this.editPinActive=!1,this.smallestEditHeight=-1;let t=this.contentEl.querySelector(":scope > .wl-tab-content");t==null||t.style.removeProperty("height"),this.lastAppliedTabHeight=-1,this.applyTabContentHeight()}applyTabContentHeight(){let t=this.contentEl;if(!t)return;let e=t.querySelector(":scope > .wl-tab-bar"),i=t.querySelector(":scope > .wl-tab-content");if(!i)return;let a=t.clientHeight,n=e?e.offsetHeight:0,r=a-n;if(!(r<=0)){if(this.editPinActive){if(!this.isEditorInScroller(this.contentEl.ownerDocument.activeElement)){this.releaseEditPin();return}(this.smallestEditHeight<0||r{this.activeTab!=="dashboard"&&(this.destroyDraftsTab(),this.activeTab="dashboard",l.forEach(c=>c.removeClass("is-active")),i.addClass("is-active"),this.renderTabContent())}),a.addEventListener("click",()=>{this.activeTab!=="watchlist"&&(this.destroyDraftsTab(),this.activeTab="watchlist",l.forEach(c=>c.removeClass("is-active")),a.addClass("is-active"),this.renderTabContent())}),this.airtimeBtn.addEventListener("click",()=>{this.activeTab!=="upcoming"&&(this.destroyDraftsTab(),this.activeTab="upcoming",l.forEach(c=>c.removeClass("is-active")),this.airtimeBtn.addClass("is-active"),this.renderTabContent())}),n.addEventListener("click",()=>{this.activeTab!=="reading"&&(this.destroyDraftsTab(),this.activeTab="reading",l.forEach(c=>c.removeClass("is-active")),n.addClass("is-active"),this.renderTabContent())}),r.addEventListener("click",()=>{this.activeTab!=="custom-lists"&&(this.destroyDraftsTab(),this.activeTab="custom-lists",l.forEach(c=>c.removeClass("is-active")),r.addClass("is-active"),this.renderTabContent())}),this.draftsBtn.addEventListener("click",()=>{this.activeTab!=="drafts"&&(this.activeTab="drafts",l.forEach(c=>c.removeClass("is-active")),this.draftsBtn.addClass("is-active"),this.renderTabContent())}),o.addEventListener("click",()=>{this.activeTab!=="log"&&(this.destroyDraftsTab(),this.activeTab="log",l.forEach(c=>c.removeClass("is-active")),o.addClass("is-active"),this.renderTabContent())}),this.tabContentEl=t.createDiv({cls:"wl-tab-content"}),this.renderTabContent(),this.updateUpcomingBadge(),this.updateDraftsBadge(),this.registerEvent(this.plugin.app.metadataCache.on("resolved",()=>{this.updateDraftsBadge()}))}setActiveTab(t){var e;if(this.activeTab!==t){if(!this.tabContentEl){this.activeTab=t;return}t!=="drafts"&&this.destroyDraftsTab(),this.activeTab=t;for(let i of Object.values(this.tabButtons))i==null||i.removeClass("is-active");(e=this.tabButtons[t])==null||e.addClass("is-active"),this.renderTabContent()}}renderTabContent(){var t,e,i,a,n;if(this.tabContentEl){(t=this.customListsTab)==null||t.destroy(),this.activeTab!=="reading"&&((e=this.readingTab)==null||e.destroy(),this.readingTab=null),this.activeTab!=="log"&&((i=this.logTab)==null||i.destroy(),this.logTab=null),this.tabContentEl.empty();for(let r of Di)this.tabContentEl.removeClass(r);this.activeTab==="dashboard"?(this.dashboardTab=new fe(this.tabContentEl,this.plugin,this.dataManager,this.plugin.readingDataManager),this.dashboardTab.render()):this.activeTab==="watchlist"?((a=this.listTab)==null||a.destroy(),this.listTab=new Ee(this.tabContentEl,this.plugin,this.dataManager),this.listTab.render()):this.activeTab==="upcoming"?(this.airtimeTab=new vt(this.tabContentEl,this.plugin,this.dataManager,r=>{this.airtimeBtn&&(this.airtimeBtn.textContent=r>0?`Upcoming (${r})`:"Upcoming")}),this.airtimeTab.render()):this.activeTab==="reading"?((n=this.readingTab)==null||n.destroy(),this.readingTab=new _e(this.tabContentEl,this.plugin,this.plugin.readingDataManager),this.readingTab.render()):this.activeTab==="drafts"?(this.draftsTab=new ne(this.tabContentEl,this.plugin,this.dataManager,r=>this.setDraftsBadge(r)),this.draftsTab.render()):this.activeTab==="log"?(this.logTab=new Ue(this.tabContentEl,this.plugin),this.logTab.render()):(this.customListsTab=new Re(this.tabContentEl,this.plugin,this.dataManager),this.customListsTab.render()),this.guardOverflow(),this.refreshMobileLayout()}}refreshActiveTab(){if(this.applyColorTheme(this.contentEl),this.tabContentEl)for(let t of Di)this.tabContentEl.removeClass(t);if(this.activeTab==="watchlist"&&this.listTab)this.listTab.render();else if(this.activeTab==="dashboard"&&this.dashboardTab)this.dashboardTab.render();else if(this.activeTab==="upcoming"&&this.airtimeTab)this.airtimeTab.render();else if(this.activeTab==="reading"&&this.readingTab)this.readingTab.render();else if(this.activeTab==="custom-lists"&&this.customListsTab)this.customListsTab.render();else if(this.activeTab==="drafts"&&this.draftsTab)this.draftsTab.render();else if(this.activeTab==="log"&&this.logTab)this.logTab.render();else{this.renderTabContent();return}this.guardOverflow(),this.updateUpcomingBadge()}guardOverflow(){var i;if(!this.tabContentEl)return;(i=this.overflowObserver)==null||i.disconnect();let t=this.tabContentEl,e=Date.now();this.overflowObserver=new MutationObserver(()=>{if(Date.now()-e>300)return;window.getComputedStyle(t).overflow==="hidden"&&(t.style.overflow="auto")}),this.overflowObserver.observe(t,{attributes:!0,attributeFilter:["style","class"]})}updateUpcomingBadge(){if(!this.airtimeBtn)return;let t=vt.getAiredDueCount(this.dataManager,this.plugin.readingDataManager)+vt.getMaybeDueCount(this.dataManager);this.airtimeBtn.textContent=t>0?`Upcoming (${t})`:"Upcoming"}setDraftsBadge(t){this.draftsBtn&&(this.draftsBtn.textContent=t>0?`Drafts (${t})`:"Drafts")}async updateDraftsBadge(){if(!this.draftsBtn)return;let t=await ne.computePendingCount(this.plugin,this.dataManager);this.setDraftsBadge(t)}};var R=require("obsidian");var Tt=require("obsidian"),ki=["title","type","status","priority","rating","totalEpisodes","episodeDuration","dateStarted","dateFinished","releaseDate","dateAdded","externalLink","notes"];function Za(g){let s=String(g!=null?g:"");return s.includes(",")||s.includes('"')||s.includes(`
+`)?`"${s.replace(/"/g,'""')}"`:s}function tn(g){let s=ki.join(","),t=g.map(e=>ki.map(i=>Za(e[i])).join(","));return[s,...t].join(`
+`)}function en(g){let s=[],t=[],e="",i=!1,a=g.replace(/\r\n?/g,`
+`);for(let n=0;n0||t.length>0)&&(t.push(e),s.push(t));s.length>0&&s[s.length-1].every(n=>n==="");)s.pop();return s}var Ti={jan:1,feb:2,mar:3,apr:4,may:5,jun:6,jul:7,aug:8,sep:9,oct:10,nov:11,dec:12};function Bs(g){let s=g.trim();if(!s)return null;try{if(/^\d{4}-\d{2}-\d{2}$/.test(s))return s;let t=s.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);if(t){let r=parseInt(t[1]),o=parseInt(t[2]),l=parseInt(t[3]),c,d;return r>12?(c=r,d=o):o>12?(d=r,c=o):(c=r,d=o),d<1||d>12||c<1||c>31?null:`${l}-${String(d).padStart(2,"0")}-${String(c).padStart(2,"0")}`}let e=s.match(/^(\d{1,2})\s+([A-Za-z]{3,})\s+(\d{4})$/);if(e){let r=Ti[e[2].toLowerCase().slice(0,3)],o=parseInt(e[1]),l=parseInt(e[3]);if(r)return`${l}-${String(r).padStart(2,"0")}-${String(o).padStart(2,"0")}`}let i=s.match(/^([A-Za-z]{3,})\s+(\d{1,2}),?\s+(\d{4})$/);if(i){let r=Ti[i[1].toLowerCase().slice(0,3)],o=parseInt(i[2]),l=parseInt(i[3]);if(r)return`${l}-${String(r).padStart(2,"0")}-${String(o).padStart(2,"0")}`}let a=s.match(/^(\d{1,2})[.-](\d{1,2})[.-](\d{4})$/);if(a){let r=parseInt(a[1]),o=parseInt(a[2]),l=parseInt(a[3]);if(o>=1&&o<=12&&r>=1&&r<=31)return`${l}-${String(o).padStart(2,"0")}-${String(r).padStart(2,"0")}`}let n=new Date(s);if(!isNaN(n.getTime())){let r=n.getDate(),o=n.getMonth()+1;return`${n.getFullYear()}-${String(o).padStart(2,"0")}-${String(r).padStart(2,"0")}`}return null}catch(t){return null}}var Ps=[{key:"title",label:"Name",autoDetect:["title","name"]},{key:"type",label:"Type",autoDetect:["type"]},{key:"status",label:"Status",autoDetect:["status"]},{key:"priority",label:"Priority",autoDetect:["priority"]},{key:"rating",label:"Rating",autoDetect:["rating","score"]},{key:"totalEpisodes",label:"Episodes",autoDetect:["episodes","totalepisodes","total episodes","episode count","ep count"]},{key:"episodeDuration",label:"Duration (min)",autoDetect:["duration","episodeduration","episode duration","minutes","runtime"]},{key:"dateStarted",label:"Date Started",autoDetect:["started","datestarted","date started","date_started","start date"]},{key:"dateFinished",label:"Date Finished",autoDetect:["finished","datefinished","date finished","date_finished","end date","finish date","completed date"]},{key:"releaseDate",label:"Release Date",autoDetect:["releasedate","release date","release_date","air date","airdate"]},{key:"externalLink",label:"Link",autoDetect:["link","externallink","external link","external_link","url"]},{key:"notes",label:"Notes",autoDetect:["notes","note","comment","comments"]}];function sn(g){let s={};for(let t of Ps){let e=g.find(i=>t.autoDetect.includes(i.toLowerCase()));s[t.key]=e!=null?e:""}return s}function an(g,s,t){var a;if(g.length<2)return[];let e=((a=g[0])!=null?a:[]).map(n=>n.trim()),i=t.getTitles();return g.slice(1).filter(n=>n.some(r=>r.trim())).map(n=>{var E;let r=k=>{var T;let C=s[k];if(!C)return"";let x=e.indexOf(C);return x>=0?((T=n[x])!=null?T:"").trim():""},o={},l=r("title");l&&(o.title=l);let c=r("type");c&&(o.type=c);let d=r("status");d&&(o.status=d);let u=r("priority");u&&(o.priority=u);let h=r("rating");if(h){let k=parseFloat(h)||0;o.rating=Math.max(0,Math.min(5,k))}let p=r("totalEpisodes");p&&(o.totalEpisodes=parseInt(p)||0);let m=r("episodeDuration");m&&(o.episodeDuration=parseInt(m)||0);let v=r("dateStarted");if(v){let k=Bs(v);k&&(o.dateStarted=k)}let f=r("dateFinished");if(f){let k=Bs(f);k&&(o.dateFinished=k)}let y=r("releaseDate");if(y){let k=Bs(y);k&&(o.releaseDate=k)}let w=r("externalLink");w&&(o.externalLink=w);let b=r("notes");if(b&&(o.notes=b),!((E=o.title)!=null&&E.trim())&&!o.type&&!o.status&&!o.rating&&!o.totalEpisodes&&!o.episodeDuration&&!o.dateStarted&&!o.dateFinished&&!o.releaseDate&&!o.externalLink&&!o.notes)return null;let D=!!i.find(k=>{var C;return k.title.toLowerCase()===((C=o.title)!=null?C:"").toLowerCase()});return{entry:o,isDuplicate:D}}).filter(n=>n!==null)}var Ve="__create__",Ci={default:["#6366F1","#8B5CF6","#EC4899","#14B8A6","#F59E0B","#10B981","#3B82F6","#EF4444"],nightfall:["#7B2CBF","#9D4EDD","#C77DFF","#5A189A","#E040FB","#B388FF","#CE93D8","#9C27B0"],bluez:["#2C7DA0","#468FAF","#61A5C2","#1B6A8A","#0077B6","#00B4D8","#48CAE4","#90E0EF"]};function nn(g){var t,e;let s=(t=Ci[g])!=null?t:Ci.default;return(e=s[Math.floor(Math.random()*s.length)])!=null?e:"#888780"}var le=class extends Tt.Modal{constructor(t,e,i,a){super(t);this.selectedIds=new Set;this.csvRows=[];this.csvHeaders=[];this.columnMapping={};this.importRows=[];this.statusValueMap={};this.typeValueMap={};this.ratingValueMap={};this.stepUpload=null;this.stepMapping=null;this.stepValueMap=null;this.stepPreview=null;this.importBtn=null;this.cancelBtn=null;this.isImporting=!1;this.importCancelled=!1;this.progressWrap=null;this.progressBarFill=null;this.progressText=null;this.plugin=e,this.dataManager=i,this.mode=a}onOpen(){let{contentEl:t}=this;t.empty(),t.addClass("wl-csv-modal"),this.mode==="export"?this.renderExport(t):this.renderImport(t)}onClose(){this.contentEl.empty()}renderExport(t){t.createEl("h2",{cls:"wl-modal-title",text:"Export to CSV"});let e=this.dataManager.getTitles();e.forEach(p=>this.selectedIds.add(p.id));let i=t.createDiv({cls:"wl-csv-ctrl-row"}),a=i.createEl("button",{cls:"wl-btn",text:"Select all"}),n=i.createEl("button",{cls:"wl-btn",text:"Select none"}),r=i.createSpan({cls:"wl-csv-count",text:`${e.length} selected`}),o=t.createDiv({cls:"wl-csv-list"}),l=[],c=()=>{r.textContent=`${this.selectedIds.size} selected`};for(let p of e){let m=o.createDiv({cls:"wl-csv-row"}),v=m.createEl("input",{attr:{type:"checkbox"}});v.checked=!0,l.push(v),m.createSpan({cls:"wl-csv-row-title",text:p.title}),m.createSpan({cls:"wl-csv-row-meta",text:`${p.type} \xB7 ${p.status}`}),v.addEventListener("change",()=>{v.checked?this.selectedIds.add(p.id):this.selectedIds.delete(p.id),c()})}a.addEventListener("click",()=>{e.forEach(p=>this.selectedIds.add(p.id)),l.forEach(p=>{p.checked=!0}),c()}),n.addEventListener("click",()=>{this.selectedIds.clear(),l.forEach(p=>{p.checked=!1}),c()});let d=t.createDiv({cls:"wl-modal-btn-row wl-csv-btn-row"}),u=d.createEl("button",{cls:"wl-btn",text:"Cancel"}),h=d.createEl("button",{cls:"wl-btn wl-btn-primary",text:"Export CSV"});u.addEventListener("click",()=>this.close()),h.addEventListener("click",()=>{let p=e.filter(S=>this.selectedIds.has(S.id));if(p.length===0){new Tt.Notice("No titles selected.");return}let m=tn(p),v=new Blob([m],{type:"text/csv;charset=utf-8;"}),f=URL.createObjectURL(v),y=activeDocument.createElement("a"),w=new Date,b=`${w.getFullYear()}-${String(w.getMonth()+1).padStart(2,"0")}-${String(w.getDate()).padStart(2,"0")}`;y.href=f,y.download=`watchlog-export-${b}.csv`,y.click(),URL.revokeObjectURL(f),new Tt.Notice(`Exported ${p.length} titles.`),this.close()})}renderImport(t){t.createEl("h2",{cls:"wl-modal-title",text:"Import from CSV"}),this.stepUpload=t.createDiv({cls:"wl-csv-step"});let e=this.stepUpload.createDiv({cls:"wl-csv-drop-zone"});e.createDiv({cls:"wl-csv-drop-label",text:"Select a CSV file to import"});let i=e.createEl("input",{attr:{type:"file",accept:".csv"},cls:"wl-csv-file-input"});this.stepMapping=t.createDiv({cls:"wl-csv-step"}),this.stepMapping.hide(),this.stepValueMap=t.createDiv({cls:"wl-csv-step"}),this.stepValueMap.hide(),this.stepPreview=t.createDiv({cls:"wl-csv-step"}),this.stepPreview.hide();let a=t.createDiv({cls:"wl-modal-btn-row wl-csv-btn-row"});this.progressWrap=a.createDiv({cls:"wl-csv-progress-wrap"}),this.progressWrap.hide(),this.progressWrap.createSpan({cls:"wl-csv-bg-note",text:"You can close this window \u2014 import will continue in the background."});let n=this.progressWrap.createDiv({cls:"wl-csv-progress-track"});this.progressBarFill=n.createDiv({cls:"wl-csv-progress-fill"}),this.progressText=this.progressWrap.createSpan({cls:"wl-csv-progress-text",text:"0 / 0"}),this.cancelBtn=a.createEl("button",{cls:"wl-btn",text:"Cancel"}),this.importBtn=a.createEl("button",{cls:"wl-btn wl-btn-primary",text:"Import selected"}),this.importBtn.disabled=!0,this.importBtn.hide(),this.cancelBtn.addEventListener("click",()=>{this.isImporting?(this.importCancelled=!0,this.cancelBtn&&(this.cancelBtn.disabled=!0,this.cancelBtn.textContent="Cancelling\u2026")):this.close()}),this.importBtn.addEventListener("click",()=>void this.doImport()),i.addEventListener("change",()=>{var l;let r=(l=i.files)==null?void 0:l[0];if(!r)return;let o=new FileReader;o.onload=c=>{var u,h;let d=(u=c.target)==null?void 0:u.result;this.csvRows=en(d),this.csvHeaders=((h=this.csvRows[0])!=null?h:[]).map(p=>p.trim()),this.columnMapping=sn(this.csvHeaders),this.showMappingStep()},o.readAsText(r)})}showMappingStep(){!this.stepUpload||!this.stepMapping||(this.stepUpload.hide(),this.stepMapping.show(),this.renderMappingStep(this.stepMapping))}renderMappingStep(t){var r;t.empty(),t.createDiv({cls:"wl-csv-step-title",text:"Map your columns"}),t.createDiv({cls:"wl-csv-step-desc",text:'Match each WatchLog field to a column from your CSV. Select "Skip" to leave a field empty.'}),t.createDiv({cls:"wl-csv-info-note",text:"Rows with empty fields will be skipped automatically. If you see missing titles, check that your CSV has no empty rows at the top."});let e="",i=t.createDiv({cls:"wl-csv-map-grid"}),a={};for(let o of Ps){let l=i.createDiv({cls:"wl-csv-map-row"});l.createSpan({cls:"wl-csv-map-label",text:o.label});let c=l.createEl("select",{cls:"wl-select wl-csv-map-select"});a[o.key]=c,c.createEl("option",{value:e,text:"\u2014 skip \u2014"});for(let d of this.csvHeaders)c.createEl("option",{value:d,text:d});c.value=(r=this.columnMapping[o.key])!=null?r:e}t.createEl("button",{cls:"wl-btn wl-btn-primary wl-csv-preview-btn",text:"Next \u2192"}).addEventListener("click",()=>{var v,f,y,w,b,S,D;for(let E of Ps)this.columnMapping[E.key]=(f=(v=a[E.key])==null?void 0:v.value)!=null?f:"";this.importRows=an(this.csvRows,this.columnMapping,this.dataManager).map(E=>({...E,selected:!E.isDuplicate}));let o=new Set(this.plugin.settings.statuses.map(E=>E.name)),l=new Set(this.plugin.settings.types.map(E=>E.name)),c=[],d=[],u=[],h=new Set,p=new Set,m=new Set;for(let E of this.importRows){let k=(y=E.entry.status)==null?void 0:y.trim();k&&!o.has(k)&&!h.has(k)&&(c.push(k),h.add(k));let C=(w=E.entry.type)==null?void 0:w.trim();C&&!l.has(C)&&!p.has(C)&&(d.push(C),p.add(C));let x=this.columnMapping.rating;if(x){let M=((b=this.csvRows[0])!=null?b:[]).map(L=>L.trim()).indexOf(x);if(M>=0){let L=this.importRows.indexOf(E),A=((D=((S=this.csvRows[L+1])!=null?S:[])[M])!=null?D:"").trim();A&&isNaN(parseFloat(A))&&!m.has(A)&&(u.push(A),m.add(A))}}}c.length>0||d.length>0||u.length>0?this.showValueMapStep(c,d,u):this.showPreviewStep()})}showValueMapStep(t,e,i=[]){!this.stepMapping||!this.stepValueMap||(this.stepMapping.hide(),this.stepValueMap.show(),this.renderValueMapStep(this.stepValueMap,t,e,i))}renderValueMapStep(t,e,i,a=[]){t.empty(),t.createDiv({cls:"wl-csv-step-title",text:"Map unknown values"}),t.createDiv({cls:"wl-csv-step-desc",text:"Some values in your CSV were not recognized. Map each to an existing value, or choose how to handle it."});let n=this.plugin.settings.statuses.map(p=>p.name),r=this.plugin.settings.types.map(p=>p.name),o={},l={},c={};if(e.length>0){t.createDiv({cls:"wl-csv-valmap-section-title",text:"Status"});for(let p of e){let m=t.createDiv({cls:"wl-csv-valmap-row"});m.createSpan({cls:"wl-csv-valmap-orig",text:`"${p}"`}),m.createSpan({cls:"wl-csv-valmap-arrow",text:"\u2192"});let v=m.createEl("select",{cls:"wl-select wl-csv-valmap-select"});o[p]=v,v.createEl("option",{value:"",text:"\u2014 leave blank \u2014"});for(let w of n)v.createEl("option",{value:w,text:w});let f=p.toLowerCase(),y=n.find(w=>w.toLowerCase()===f);y&&(v.value=y)}}if(i.length>0){t.createDiv({cls:"wl-csv-valmap-section-title",text:"Type"});for(let p of i){let m=t.createDiv({cls:"wl-csv-valmap-row"});m.createSpan({cls:"wl-csv-valmap-orig",text:`"${p}"`}),m.createSpan({cls:"wl-csv-valmap-arrow",text:"\u2192"});let v=m.createEl("select",{cls:"wl-select wl-csv-valmap-select"});l[p]=v,v.createEl("option",{value:"",text:"\u2014 leave blank \u2014"});for(let w of r)v.createEl("option",{value:w,text:w});v.createEl("option",{value:Ve,text:"+ create new type"});let f=p.toLowerCase(),y=r.find(w=>w.toLowerCase()===f);y?v.value=y:v.value=Ve}}if(a.length>0){t.createDiv({cls:"wl-csv-valmap-section-title",text:"Rating"});for(let p of a){let m=t.createDiv({cls:"wl-csv-valmap-row"});m.createSpan({cls:"wl-csv-valmap-orig",text:`"${p}"`}),m.createSpan({cls:"wl-csv-valmap-arrow",text:"\u2192"});let v=m.createEl("select",{cls:"wl-select wl-csv-valmap-select"});c[p]=v,v.createEl("option",{value:"",text:"\u2014 leave blank \u2014"});for(let f=1;f<=5;f++)v.createEl("option",{value:String(f),text:`${f}/5`})}}let d=t.createDiv({cls:"wl-csv-valmap-btn-row"}),u=d.createEl("button",{cls:"wl-btn",text:"\u2190 back"}),h=d.createEl("button",{cls:"wl-btn wl-btn-primary",text:"Preview \u2192"});u.addEventListener("click",()=>{!this.stepValueMap||!this.stepMapping||(this.stepValueMap.hide(),this.stepMapping.show())}),h.addEventListener("click",()=>{var p,m,v,f,y,w;this.statusValueMap={};for(let b of e)this.statusValueMap[b]=(m=(p=o[b])==null?void 0:p.value)!=null?m:"";this.typeValueMap={};for(let b of i)this.typeValueMap[b]=(f=(v=l[b])==null?void 0:v.value)!=null?f:"";this.ratingValueMap={};for(let b of a)this.ratingValueMap[b]=(w=(y=c[b])==null?void 0:y.value)!=null?w:"";this.applyValueMapsToRows(),this.showPreviewStep()})}applyValueMapsToRows(){var t,e,i,a,n,r,o;for(let l of this.importRows){let c=(e=(t=l.entry.status)==null?void 0:t.trim())!=null?e:"";if(c&&Object.prototype.hasOwnProperty.call(this.statusValueMap,c)){let h=(i=this.statusValueMap[c])!=null?i:"";l.entry.status=h||void 0}let d=(n=(a=l.entry.type)==null?void 0:a.trim())!=null?n:"";if(d&&Object.prototype.hasOwnProperty.call(this.typeValueMap,d)){let h=(r=this.typeValueMap[d])!=null?r:"";h===Ve?l.entry.type=d:l.entry.type=h||void 0}let u=Object.keys(this.ratingValueMap).find(h=>{var w,b,S;let p=this.columnMapping.rating;if(!p)return!1;let v=((w=this.csvRows[0])!=null?w:[]).map(D=>D.trim()).indexOf(p);if(v<0)return!1;let f=this.importRows.indexOf(l);return((S=((b=this.csvRows[f+1])!=null?b:[])[v])!=null?S:"").trim()===h});if(u!==void 0){let h=(o=this.ratingValueMap[u])!=null?o:"";l.entry.rating=h?parseInt(h):0}l.isDuplicate=!!this.dataManager.getTitles().find(h=>{var p;return h.title.toLowerCase()===((p=l.entry.title)!=null?p:"").toLowerCase()})}}showPreviewStep(){!this.stepValueMap||!this.stepMapping||!this.stepPreview||!this.importBtn||(this.stepValueMap.hide(),this.stepMapping.hide(),this.stepPreview.show(),this.importBtn.show(),this.importBtn.disabled=this.importRows.filter(t=>t.selected).length===0,this.renderPreviewStep(this.stepPreview))}renderPreviewStep(t){var u;t.empty();let e=this.importRows.filter(h=>h.isDuplicate).length;e>0&&t.createDiv({cls:"wl-csv-dupe-warning",text:`${e} duplicate${e!==1?"s":""} found (highlighted). These are deselected by default.`});let i=t.createDiv({cls:"wl-csv-ctrl-row"}),a=i.createEl("button",{cls:"wl-btn",text:"\u2190 back"}),n=i.createEl("button",{cls:"wl-btn",text:"Select all"}),r=i.createEl("button",{cls:"wl-btn",text:"Select none"}),o=i.createSpan({cls:"wl-csv-count",text:""});a.addEventListener("click",()=>{if(!this.stepPreview||!this.stepValueMap||!this.stepMapping||!this.importBtn)return;this.stepPreview.hide(),this.importBtn.hide(),this.importBtn.disabled=!0,Object.keys(this.statusValueMap).length>0||Object.keys(this.typeValueMap).length>0||Object.keys(this.ratingValueMap).length>0?this.stepValueMap.show():this.stepMapping.show()});let l=t.createDiv({cls:"wl-csv-list"}),c=[],d=()=>{let h=this.importRows.filter(p=>p.selected).length;o.textContent=`${h} of ${this.importRows.length} selected`,this.importBtn&&(this.importBtn.disabled=h===0)};for(let h=0;h{this.importRows[y].selected=v.checked,d()})}n.addEventListener("click",()=>{this.importRows.forEach(h=>{h.selected=!0}),c.forEach(h=>{h.checked=!0}),d()}),r.addEventListener("click",()=>{this.importRows.forEach(h=>{h.selected=!1}),c.forEach(h=>{h.checked=!1}),d()}),d()}async doImport(){var c,d,u,h,p,m,v,f,y,w,b,S;let t=this.importRows.filter(D=>{var E;return D.selected&&((E=D.entry.title)==null?void 0:E.trim())});if(t.length===0){new Tt.Notice("No titles selected to import.");return}this.isImporting=!0,this.importCancelled=!1,this.importBtn&&(this.importBtn.disabled=!0),this.cancelBtn&&(this.cancelBtn.textContent="Cancel import"),this.progressWrap&&this.progressWrap.show(),this.progressText&&(this.progressText.textContent=`0 / ${t.length}`),this.progressBarFill&&(this.progressBarFill.style.width="0%");let e=()=>{this.importCancelled=!0};this.plugin.importProgress={current:0,total:t.length,cancel:e};let i=(c=this.plugin.settings.colorTheme)!=null?c:"default",a=new Set(this.plugin.settings.types.map(D=>D.name.toLowerCase())),n=new Set;for(let D of t){let E=(d=D.entry.type)==null?void 0:d.trim();E&&!a.has(E.toLowerCase())&&n.add(E)}for(let[D,E]of Object.entries(this.typeValueMap))E===Ve&&!a.has(D.toLowerCase())&&n.add(D);if(n.size>0){for(let D of n)this.plugin.settings.types.push({name:D,color:nn(i)});await this.plugin.saveSettings()}let r=10,o=0;try{for(let D=0;D0&&(L.watchedEpisodes=Array.from({length:L.totalEpisodes},(j,A)=>A+1)),k.push(L)}if(k.length===0)break;await this.dataManager.addTitleBatch(k),o+=k.length,this.plugin.importProgress={current:o,total:t.length,cancel:e};let C=Math.round(o/t.length*100);this.progressBarFill&&(this.progressBarFill.style.width=`${C}%`),this.progressText&&(this.progressText.textContent=`${o} / ${t.length}`)}}finally{this.isImporting=!1,this.plugin.importProgress=null,this.dataManager.notifyChange()}let l=n.size>0?` (${n.size} new type${n.size!==1?"s":""} created)`:"";this.importCancelled?(this.progressText&&(this.progressText.textContent=`Cancelled \u2014 ${o} / ${t.length} imported`),this.cancelBtn&&(this.cancelBtn.disabled=!1,this.cancelBtn.textContent="Close"),new Tt.Notice(`Import cancelled. ${o} title${o!==1?"s":""} imported.`)):(this.progressText&&(this.progressText.textContent=`Done \u2014 ${o} imported`),this.cancelBtn&&(this.cancelBtn.disabled=!1,this.cancelBtn.textContent="Close"),new Tt.Notice(`Imported ${o} title${o!==1?"s":""}${l}.`),this.close())}};var Mi=require("obsidian"),ce=class extends Mi.Modal{constructor(t,e,i){super(t);this.mode=e;this.onChoice=i}onOpen(){let{contentEl:t}=this;t.empty(),t.addClass("wl-draft-choice-modal"),this.titleEl.setText(this.mode==="export"?"Export to CSV":"Import from CSV"),t.createDiv({cls:"wl-draft-choice-subtitle",text:this.mode==="export"?"Which library do you want to export?":"Which library do you want to import into?"});let e=t.createDiv({cls:"wl-draft-choice-grid"}),i=(a,n)=>{let r=e.createEl("button",{cls:"wl-draft-choice-btn"});r.createDiv({cls:"wl-draft-choice-label",text:a}),r.addEventListener("click",()=>{this.close(),this.onChoice(n)})};i("Books","book"),i("Manga","manga")}onClose(){this.contentEl.empty()}};var Ct=require("obsidian");function rn(g){let s=String(g!=null?g:"");return s.includes(",")||s.includes('"')||s.includes(`
+`)?`"${s.replace(/"/g,'""')}"`:s}function on(g){let s=[],t=[],e="",i=!1,a=g.replace(/\r\n?/g,`
+`);for(let n=0;n0||t.length>0)&&(t.push(e),s.push(t));s.length>0&&s[s.length-1].every(n=>n==="");)s.pop();return s}var xi={jan:1,feb:2,mar:3,apr:4,may:5,jun:6,jul:7,aug:8,sep:9,oct:10,nov:11,dec:12};function ln(g){let s=g.trim();if(!s)return null;try{if(/^\d{4}-\d{2}-\d{2}$/.test(s))return s;let t=s.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);if(t){let r=parseInt(t[1]),o=parseInt(t[2]),l=parseInt(t[3]),c,d;return r>12?(c=r,d=o):o>12?(d=r,c=o):(c=r,d=o),d<1||d>12||c<1||c>31?null:`${l}-${String(d).padStart(2,"0")}-${String(c).padStart(2,"0")}`}let e=s.match(/^(\d{1,2})\s+([A-Za-z]{3,})\s+(\d{4})$/);if(e){let r=xi[e[2].toLowerCase().slice(0,3)],o=parseInt(e[1]),l=parseInt(e[3]);if(r)return`${l}-${String(r).padStart(2,"0")}-${String(o).padStart(2,"0")}`}let i=s.match(/^([A-Za-z]{3,})\s+(\d{1,2}),?\s+(\d{4})$/);if(i){let r=xi[i[1].toLowerCase().slice(0,3)],o=parseInt(i[2]),l=parseInt(i[3]);if(r)return`${l}-${String(r).padStart(2,"0")}-${String(o).padStart(2,"0")}`}let a=s.match(/^(\d{1,2})[.-](\d{1,2})[.-](\d{4})$/);if(a){let r=parseInt(a[1]),o=parseInt(a[2]),l=parseInt(a[3]);if(o>=1&&o<=12&&r>=1&&r<=31)return`${l}-${String(o).padStart(2,"0")}-${String(r).padStart(2,"0")}`}let n=new Date(s);if(!isNaN(n.getTime())){let r=n.getDate(),o=n.getMonth()+1;return`${n.getFullYear()}-${String(o).padStart(2,"0")}-${String(r).padStart(2,"0")}`}return null}catch(t){return null}}var cn=["title","author","status","rating","pagesRead","totalPages","chaptersRead","totalChapters","dateStarted","dateFinished","releaseDate","dateAdded","externalLink","googleBooksId"],dn=["title","author","status","rating","chaptersRead","totalChapters","volumesRead","totalVolumes","dateStarted","dateFinished","releaseDate","dateAdded","externalLink","malId"],Li=[{key:"title",label:"Title",coercion:"string",autoDetect:["title","name"]},{key:"author",label:"Author",coercion:"string",autoDetect:["author","writer","by"]},{key:"status",label:"Status",coercion:"string",autoDetect:["status"]},{key:"rating",label:"Rating",coercion:"rating",autoDetect:["rating","score"]}],Ri=[{key:"dateStarted",label:"Date Started",coercion:"date",autoDetect:["started","datestarted","date started","date_started","start date"]},{key:"dateFinished",label:"Date Finished",coercion:"date",autoDetect:["finished","datefinished","date finished","date_finished","end date","finish date","completed date"]},{key:"releaseDate",label:"Release Date",coercion:"date",autoDetect:["releasedate","release date","release_date","published","publish date"]},{key:"externalLink",label:"Link",coercion:"string",autoDetect:["link","externallink","external link","external_link","url"]}],un=[...Li,{key:"pagesRead",label:"Pages Read",coercion:"int",autoDetect:["pagesread","pages read","read pages","page"]},{key:"totalPages",label:"Total Pages",coercion:"int",autoDetect:["totalpages","total pages","pages","page count"]},{key:"chaptersRead",label:"Chapters Read",coercion:"int",autoDetect:["chaptersread","chapters read","read chapters"]},{key:"totalChapters",label:"Total Chapters",coercion:"int",autoDetect:["totalchapters","total chapters","chapters","chapter count"]},...Ri,{key:"googleBooksId",label:"Google Books ID",coercion:"string",autoDetect:["googlebooksid","google books id","gbid","volumeid"]}],hn=[...Li,{key:"chaptersRead",label:"Chapters Read",coercion:"int",autoDetect:["chaptersread","chapters read","read chapters"]},{key:"totalChapters",label:"Total Chapters",coercion:"int",autoDetect:["totalchapters","total chapters","chapters","chapter count"]},{key:"volumesRead",label:"Volumes Read",coercion:"int",autoDetect:["volumesread","volumes read","read volumes"]},{key:"totalVolumes",label:"Total Volumes",coercion:"int",autoDetect:["totalvolumes","total volumes","volumes","volume count"]},...Ri,{key:"malId",label:"MAL ID",coercion:"string",autoDetect:["malid","mal id","myanimelist id","mal"]}];function pn(g){return g==="book"?{exportColumns:cn,importFields:un}:{exportColumns:dn,importFields:hn}}function gn(g,s){let t={};for(let e of g){let i=s.find(a=>e.autoDetect.includes(a.toLowerCase()));t[e.key]=i!=null?i:""}return t}var de=class extends Ct.Modal{constructor(t,e,i,a){super(t);this.selectedIds=new Set;this.csvRows=[];this.csvHeaders=[];this.columnMapping={};this.importRows=[];this.statusValueMap={};this.ratingValueMap={};this.stepUpload=null;this.stepMapping=null;this.stepValueMap=null;this.stepPreview=null;this.importBtn=null;this.cancelBtn=null;this.isImporting=!1;this.importCancelled=!1;this.progressWrap=null;this.progressBarFill=null;this.progressText=null;this.plugin=e,this.kind=i,this.mode=a,this.schema=pn(i)}get kindLabel(){return this.kind==="book"?"Books":"Manga"}get itemNoun(){return this.kind==="book"?"book":"manga"}getItems(){return this.kind==="book"?this.plugin.readingDataManager.getBooks():this.plugin.readingDataManager.getMangaList()}onOpen(){let{contentEl:t}=this;t.empty(),t.addClass("wl-csv-modal"),this.mode==="export"?this.renderExport(t):this.renderImport(t)}onClose(){this.contentEl.empty()}itemsToCsv(t){let e=this.schema.exportColumns,i=e.join(","),a=t.map(n=>e.map(r=>rn(n[r])).join(","));return[i,...a].join(`
+`)}renderExport(t){t.createEl("h2",{cls:"wl-modal-title",text:`Export ${this.kindLabel} to CSV`});let e=this.getItems();e.forEach(p=>this.selectedIds.add(p.id));let i=t.createDiv({cls:"wl-csv-ctrl-row"}),a=i.createEl("button",{cls:"wl-btn",text:"Select all"}),n=i.createEl("button",{cls:"wl-btn",text:"Select none"}),r=i.createSpan({cls:"wl-csv-count",text:`${e.length} selected`}),o=t.createDiv({cls:"wl-csv-list"}),l=[],c=()=>{r.textContent=`${this.selectedIds.size} selected`};for(let p of e){let m=o.createDiv({cls:"wl-csv-row"}),v=m.createEl("input",{attr:{type:"checkbox"}});v.checked=!0,l.push(v),m.createSpan({cls:"wl-csv-row-title",text:p.title});let f=[p.author,p.status].filter(Boolean).join(" \xB7 ");f&&m.createSpan({cls:"wl-csv-row-meta",text:f}),v.addEventListener("change",()=>{v.checked?this.selectedIds.add(p.id):this.selectedIds.delete(p.id),c()})}a.addEventListener("click",()=>{e.forEach(p=>this.selectedIds.add(p.id)),l.forEach(p=>{p.checked=!0}),c()}),n.addEventListener("click",()=>{this.selectedIds.clear(),l.forEach(p=>{p.checked=!1}),c()});let d=t.createDiv({cls:"wl-modal-btn-row wl-csv-btn-row"}),u=d.createEl("button",{cls:"wl-btn",text:"Cancel"}),h=d.createEl("button",{cls:"wl-btn wl-btn-primary",text:"Export CSV"});u.addEventListener("click",()=>this.close()),h.addEventListener("click",()=>{let p=e.filter(S=>this.selectedIds.has(S.id));if(p.length===0){new Ct.Notice(`No ${this.itemNoun} selected.`);return}let m=this.itemsToCsv(p),v=new Blob([m],{type:"text/csv;charset=utf-8;"}),f=URL.createObjectURL(v),y=activeDocument.createElement("a"),w=new Date,b=`${w.getFullYear()}-${String(w.getMonth()+1).padStart(2,"0")}-${String(w.getDate()).padStart(2,"0")}`;y.href=f,y.download=`watchlog-${this.itemNoun==="book"?"books":"manga"}-export-${b}.csv`,y.click(),URL.revokeObjectURL(f),new Ct.Notice(`Exported ${p.length} ${this.kindLabel.toLowerCase()}.`),this.close()})}renderImport(t){t.createEl("h2",{cls:"wl-modal-title",text:`Import ${this.kindLabel} from CSV`}),this.stepUpload=t.createDiv({cls:"wl-csv-step"});let e=this.stepUpload.createDiv({cls:"wl-csv-drop-zone"});e.createDiv({cls:"wl-csv-drop-label",text:"Select a CSV file to import"});let i=e.createEl("input",{attr:{type:"file",accept:".csv"},cls:"wl-csv-file-input"});this.stepMapping=t.createDiv({cls:"wl-csv-step"}),this.stepMapping.hide(),this.stepValueMap=t.createDiv({cls:"wl-csv-step"}),this.stepValueMap.hide(),this.stepPreview=t.createDiv({cls:"wl-csv-step"}),this.stepPreview.hide();let a=t.createDiv({cls:"wl-modal-btn-row wl-csv-btn-row"});this.progressWrap=a.createDiv({cls:"wl-csv-progress-wrap"}),this.progressWrap.hide(),this.progressWrap.createSpan({cls:"wl-csv-bg-note",text:"You can close this window \u2014 import will continue in the background."});let n=this.progressWrap.createDiv({cls:"wl-csv-progress-track"});this.progressBarFill=n.createDiv({cls:"wl-csv-progress-fill"}),this.progressText=this.progressWrap.createSpan({cls:"wl-csv-progress-text",text:"0 / 0"}),this.cancelBtn=a.createEl("button",{cls:"wl-btn",text:"Cancel"}),this.importBtn=a.createEl("button",{cls:"wl-btn wl-btn-primary",text:"Import selected"}),this.importBtn.disabled=!0,this.importBtn.hide(),this.cancelBtn.addEventListener("click",()=>{this.isImporting?(this.importCancelled=!0,this.cancelBtn&&(this.cancelBtn.disabled=!0,this.cancelBtn.textContent="Cancelling\u2026")):this.close()}),this.importBtn.addEventListener("click",()=>void this.doImport()),i.addEventListener("change",()=>{var l;let r=(l=i.files)==null?void 0:l[0];if(!r)return;let o=new FileReader;o.onload=c=>{var u,h;let d=(u=c.target)==null?void 0:u.result;this.csvRows=on(d),this.csvHeaders=((h=this.csvRows[0])!=null?h:[]).map(p=>p.trim()),this.columnMapping=gn(this.schema.importFields,this.csvHeaders),this.showMappingStep()},o.readAsText(r)})}showMappingStep(){!this.stepUpload||!this.stepMapping||(this.stepUpload.hide(),this.stepMapping.show(),this.renderMappingStep(this.stepMapping))}renderMappingStep(t){var r;t.empty(),t.createDiv({cls:"wl-csv-step-title",text:"Map your columns"}),t.createDiv({cls:"wl-csv-step-desc",text:`Match each ${this.itemNoun==="book"?"Book":"Manga"} field to a column from your CSV. Select "\u2014 skip \u2014" to leave a field empty.`}),t.createDiv({cls:"wl-csv-info-note",text:"Rows with empty fields will be skipped automatically. If you see missing entries, check that your CSV has no empty rows at the top."});let e="",i=t.createDiv({cls:"wl-csv-map-grid"}),a={};for(let o of this.schema.importFields){let l=i.createDiv({cls:"wl-csv-map-row"});l.createSpan({cls:"wl-csv-map-label",text:o.label});let c=l.createEl("select",{cls:"wl-select wl-csv-map-select"});a[o.key]=c,c.createEl("option",{value:e,text:"\u2014 skip \u2014"});for(let d of this.csvHeaders)c.createEl("option",{value:d,text:d});c.value=(r=this.columnMapping[o.key])!=null?r:e}t.createEl("button",{cls:"wl-btn wl-btn-primary wl-csv-preview-btn",text:"Next \u2192"}).addEventListener("click",()=>{var v,f,y,w,b,S;for(let D of this.schema.importFields)this.columnMapping[D.key]=(f=(v=a[D.key])==null?void 0:v.value)!=null?f:"";this.importRows=this.applyMapping().map(D=>({...D,selected:!D.isDuplicate}));let o=new Set(wt),l=[],c=[],d=new Set,u=new Set,h=((y=this.csvRows[0])!=null?y:[]).map(D=>D.trim()),p=this.columnMapping.rating,m=p?h.indexOf(p):-1;for(let D of this.importRows){let E=(w=D.entry.status)==null?void 0:w.trim();if(E&&!o.has(E)&&!d.has(E)&&(l.push(E),d.add(E)),m>=0){let k=this.importRows.indexOf(D),x=((S=((b=this.csvRows[k+1])!=null?b:[])[m])!=null?S:"").trim();x&&isNaN(parseFloat(x))&&!u.has(x)&&(c.push(x),u.add(x))}}l.length>0||c.length>0?this.showValueMapStep(l,c):this.showPreviewStep()})}applyMapping(){var i;if(this.csvRows.length<2)return[];let t=((i=this.csvRows[0])!=null?i:[]).map(a=>a.trim()),e=this.getItems();return this.csvRows.slice(1).filter(a=>a.some(n=>n.trim())).map(a=>{let n=l=>{var u;let c=this.columnMapping[l];if(!c)return"";let d=t.indexOf(c);return d>=0?((u=a[d])!=null?u:"").trim():""},r={};for(let l of this.schema.importFields){let c=n(l.key);if(c)switch(l.coercion){case"string":r[l.key]=c;break;case"int":r[l.key]=parseInt(c)||0;break;case"rating":{let d=parseFloat(c)||0;r.rating=Math.max(0,Math.min(5,d));break}case"date":{let d=ln(c);d&&(r[l.key]=d);break}}}if(!r.title||!r.title.trim())return null;let o=!!e.find(l=>{var c;return l.title.toLowerCase()===((c=r.title)!=null?c:"").toLowerCase()});return{entry:r,isDuplicate:o}}).filter(a=>a!==null)}showValueMapStep(t,e){!this.stepMapping||!this.stepValueMap||(this.stepMapping.hide(),this.stepValueMap.show(),this.renderValueMapStep(this.stepValueMap,t,e))}renderValueMapStep(t,e,i){t.empty(),t.createDiv({cls:"wl-csv-step-title",text:"Map unknown values"}),t.createDiv({cls:"wl-csv-step-desc",text:"Some values in your CSV were not recognized. Map each to an existing value, or leave it blank."});let a={},n={};if(e.length>0){t.createDiv({cls:"wl-csv-valmap-section-title",text:"Status"});for(let c of e){let d=t.createDiv({cls:"wl-csv-valmap-row"});d.createSpan({cls:"wl-csv-valmap-orig",text:`"${c}"`}),d.createSpan({cls:"wl-csv-valmap-arrow",text:"\u2192"});let u=d.createEl("select",{cls:"wl-select wl-csv-valmap-select"});a[c]=u,u.createEl("option",{value:"",text:"\u2014 leave blank \u2014"});for(let m of lt)u.createEl("option",{value:m,text:m});let h=c.toLowerCase(),p=lt.find(m=>m.toLowerCase()===h);p&&(u.value=p)}}if(i.length>0){t.createDiv({cls:"wl-csv-valmap-section-title",text:"Rating"});for(let c of i){let d=t.createDiv({cls:"wl-csv-valmap-row"});d.createSpan({cls:"wl-csv-valmap-orig",text:`"${c}"`}),d.createSpan({cls:"wl-csv-valmap-arrow",text:"\u2192"});let u=d.createEl("select",{cls:"wl-select wl-csv-valmap-select"});n[c]=u,u.createEl("option",{value:"",text:"\u2014 leave blank \u2014"});for(let h=1;h<=5;h++)u.createEl("option",{value:String(h),text:`${h}/5`})}}let r=t.createDiv({cls:"wl-csv-valmap-btn-row"}),o=r.createEl("button",{cls:"wl-btn",text:"\u2190 back"}),l=r.createEl("button",{cls:"wl-btn wl-btn-primary",text:"Preview \u2192"});o.addEventListener("click",()=>{!this.stepValueMap||!this.stepMapping||(this.stepValueMap.hide(),this.stepMapping.show())}),l.addEventListener("click",()=>{var c,d,u,h;this.statusValueMap={};for(let p of e)this.statusValueMap[p]=(d=(c=a[p])==null?void 0:c.value)!=null?d:"";this.ratingValueMap={};for(let p of i)this.ratingValueMap[p]=(h=(u=n[p])==null?void 0:u.value)!=null?h:"";this.applyValueMapsToRows(),this.showPreviewStep()})}applyValueMapsToRows(){var a,n,r,o,l,c,d;let t=((a=this.csvRows[0])!=null?a:[]).map(u=>u.trim()),e=this.columnMapping.rating,i=e?t.indexOf(e):-1;for(let u of this.importRows){let h=(r=(n=u.entry.status)==null?void 0:n.trim())!=null?r:"";if(h&&Object.prototype.hasOwnProperty.call(this.statusValueMap,h)){let p=(o=this.statusValueMap[h])!=null?o:"";u.entry.status=p||void 0}if(i>=0){let p=this.importRows.indexOf(u),v=((c=((l=this.csvRows[p+1])!=null?l:[])[i])!=null?c:"").trim();if(v&&Object.prototype.hasOwnProperty.call(this.ratingValueMap,v)){let f=(d=this.ratingValueMap[v])!=null?d:"";u.entry.rating=f?parseInt(f):0}}}}showPreviewStep(){!this.stepValueMap||!this.stepMapping||!this.stepPreview||!this.importBtn||(this.stepValueMap.hide(),this.stepMapping.hide(),this.stepPreview.show(),this.importBtn.show(),this.importBtn.disabled=this.importRows.filter(t=>t.selected).length===0,this.renderPreviewStep(this.stepPreview))}renderPreviewStep(t){var u;t.empty();let e=this.importRows.filter(h=>h.isDuplicate).length;e>0&&t.createDiv({cls:"wl-csv-dupe-warning",text:`${e} duplicate${e!==1?"s":""} found (highlighted). These are deselected by default.`});let i=t.createDiv({cls:"wl-csv-ctrl-row"}),a=i.createEl("button",{cls:"wl-btn",text:"\u2190 back"}),n=i.createEl("button",{cls:"wl-btn",text:"Select all"}),r=i.createEl("button",{cls:"wl-btn",text:"Select none"}),o=i.createSpan({cls:"wl-csv-count",text:""});a.addEventListener("click",()=>{if(!this.stepPreview||!this.stepValueMap||!this.stepMapping||!this.importBtn)return;this.stepPreview.hide(),this.importBtn.hide(),this.importBtn.disabled=!0,Object.keys(this.statusValueMap).length>0||Object.keys(this.ratingValueMap).length>0?this.stepValueMap.show():this.stepMapping.show()});let l=t.createDiv({cls:"wl-csv-list"}),c=[],d=()=>{let h=this.importRows.filter(p=>p.selected).length;o.textContent=`${h} of ${this.importRows.length} selected`,this.importBtn&&(this.importBtn.disabled=h===0)};for(let h=0;h{this.importRows[y].selected=v.checked,d()})}n.addEventListener("click",()=>{this.importRows.forEach(h=>{h.selected=!0}),c.forEach(h=>{h.checked=!0}),d()}),r.addEventListener("click",()=>{this.importRows.forEach(h=>{h.selected=!1}),c.forEach(h=>{h.checked=!1}),d()}),d()}buildBook(t,e){var n,r,o,l,c,d,u,h,p,m,v;let i=t.title.trim(),a=new Date().toISOString();return{id:this.plugin.readingDataManager.generateBookId(i),title:i,author:(n=t.author)!=null?n:"",status:t.status||e,rating:(r=t.rating)!=null?r:0,pagesRead:(o=t.pagesRead)!=null?o:0,totalPages:(l=t.totalPages)!=null?l:0,chaptersRead:(c=t.chaptersRead)!=null?c:0,totalChapters:(d=t.totalChapters)!=null?d:0,coverUrl:"",googleBooksId:(u=t.googleBooksId)!=null?u:"",externalLink:(h=t.externalLink)!=null?h:"",vaultPage:"",dateStarted:(p=t.dateStarted)!=null?p:null,dateFinished:(m=t.dateFinished)!=null?m:null,releaseDate:(v=t.releaseDate)!=null?v:null,dateAdded:a,dateModified:a,customFields:{}}}buildManga(t,e){var n,r,o,l,c,d,u,h,p,m,v;let i=t.title.trim(),a=new Date().toISOString();return{id:this.plugin.readingDataManager.generateMangaId(i),title:i,author:(n=t.author)!=null?n:"",status:t.status||e,rating:(r=t.rating)!=null?r:0,chaptersRead:(o=t.chaptersRead)!=null?o:0,totalChapters:(l=t.totalChapters)!=null?l:0,volumesRead:(c=t.volumesRead)!=null?c:0,totalVolumes:(d=t.totalVolumes)!=null?d:0,coverUrl:"",malId:(u=t.malId)!=null?u:"",externalLink:(h=t.externalLink)!=null?h:"",vaultPage:"",dateStarted:(p=t.dateStarted)!=null?p:null,dateFinished:(m=t.dateFinished)!=null?m:null,releaseDate:(v=t.releaseDate)!=null?v:null,dateAdded:a,dateModified:a,customFields:{}}}async doImport(){var r;let t=this.importRows.filter(o=>{var l;return o.selected&&((l=o.entry.title)==null?void 0:l.trim())});if(t.length===0){new Ct.Notice(`No ${this.itemNoun} selected to import.`);return}this.isImporting=!0,this.importCancelled=!1,this.importBtn&&(this.importBtn.disabled=!0),this.cancelBtn&&(this.cancelBtn.textContent="Cancel import"),this.progressWrap&&this.progressWrap.show(),this.progressText&&(this.progressText.textContent=`0 / ${t.length}`),this.progressBarFill&&(this.progressBarFill.style.width="0%");let e=this.plugin.readingDataManager,i=(r=e.getSettings().defaultStatus)!=null?r:"Plan to Read",a=10,n=0;try{for(let o=0;o{this.textSaveTimer=null,this.plugin.saveSettings()},500)}display(){let{containerEl:t}=this;t.empty(),t.addClass("wl-settings");let e=t.createDiv({cls:"wl-settings-nav"}),i=[{key:"general",label:"General"},{key:"api",label:"API"},{key:"customize",label:"Customize"},{key:"watchlist",label:"Watchlist"},{key:"drafts",label:"Drafts"},{key:"custom-lists",label:"Custom Lists"},{key:"reading",label:"Reading"},{key:"widgets",label:"Widgets"},{key:"quick-info",label:"Quick Info"}],a=new Map;for(let r of i){let o=e.createEl("button",{cls:`wl-settings-nav-btn${this.activeSection===r.key?" is-active":""}`,text:r.label});a.set(r.key,o),o.addEventListener("click",()=>{this.activeSection=r.key,a.forEach((l,c)=>{c===r.key?l.addClass("is-active"):l.removeClass("is-active")}),n.empty(),this.renderSection(n)})}let n=t.createDiv({cls:"wl-settings-body"});this.renderSection(n)}renderSection(t){switch(t.empty(),this.activeSection){case"general":this.renderGeneral(t);break;case"api":this.renderApi(t);break;case"customize":this.renderCustomize(t);break;case"watchlist":this.renderWatchlist(t);break;case"drafts":this.renderDrafts(t);break;case"custom-lists":this.renderCustomLists(t);break;case"reading":this.renderReading(t);break;case"widgets":this.renderWidgets(t);break;case"quick-info":this.renderQuickInfo(t);break}}renderGeneral(t){new R.Setting(t).setName("Dashboard card style").setDesc("Choose between ring charts or rectangular stat cards on the dashboard.").addDropdown(o=>{var l;return o.addOptions({circles:"Circles",rectangles:"Rectangles"}).setValue((l=this.plugin.settings.dashboardCardStyle)!=null?l:"circles").onChange(async c=>{this.plugin.settings.dashboardCardStyle=c,await this.plugin.saveSettings(),activeDocument.dispatchEvent(new CustomEvent("watchlog-data-changed"))})}),new R.Setting(t).setName("Set finish date automatically").setDesc("Record today's date as the finish date when a watch title or reading entry is completed.").addToggle(o=>o.setValue(this.plugin.settings.setFinishDateAutomatically).onChange(async l=>{this.plugin.settings.setFinishDateAutomatically=l,await this.plugin.saveSettings()})),new R.Setting(t).setName("Re-fetch all posters").setDesc("Clears all cached poster and reading cover URLs. Watch posters reload lazily when you scroll the Cards view; reading covers can be re-fetched from each card's \u22EE menu.").addButton(o=>o.setButtonText("Re-fetch posters").onClick(()=>{let l=this.plugin.dataManager.getTitles(),c=this.plugin.readingDataManager.getBooks().length+this.plugin.readingDataManager.getMangaList().length;new H(this.app,`This will clear cached images for all ${l.length} watch titles and ${c} reading entries. Items without API access will show placeholder cards. Continue?`,()=>{this.refetchAllPosters()}).open()})),new R.Setting(t).setName("Show hint banners").setDesc("Show informational hint banners in Upcoming, Custom Lists, and Drafts tabs.").addToggle(o=>o.setValue(this.plugin.settings.showHintBanners).onChange(async l=>{this.plugin.settings.showHintBanners=l,await this.plugin.saveSettings(),activeDocument.dispatchEvent(new CustomEvent("watchlog-data-changed"))})),new R.Setting(t).setName("Show Upcoming count in status bar").setDesc('Show a "N due" counter in the status bar when Upcoming entries are due. Click it to open the Upcoming tab. (Desktop only.)').addToggle(o=>o.setValue(this.plugin.settings.showUpcomingStatusBar).onChange(async l=>{this.plugin.settings.showUpcomingStatusBar=l,await this.plugin.saveSettings(),this.plugin.updateStatusBar()})),t.createDiv({cls:"wl-settings-section-title",text:"Backup & Restore"}),new R.Setting(t).setName("Export backup").setDesc("Export all watchlog data \u2014 watchlist, reading (books & manga) and activity log \u2014 into a single timestamped .JSON file.").addButton(o=>o.setButtonText("Export backup").onClick(()=>void this.exportBackup())),new R.Setting(t).setName("Restore from backup").setDesc("Restore from a .JSON backup file. This replaces your current watchlist, reading and activity-log data with the contents of the backup. Older watch-only backups restore the watchlist only and leave reading and activity log untouched.").addButton(o=>o.setButtonText("Restore from backup").onClick(()=>this.openRestoreDialog()));let e={wrap:null,fill:null,text:null};new R.Setting(t).setName("Regenerate note files").setDesc("Creates missing .md files for titles that don't have one. Existing files are not modified.").addButton(o=>o.setButtonText("Regenerate").onClick(()=>{let{wrap:l,fill:c,text:d}=e;l&&c&&d&&this.runRegenerate(l,c,d)}));let i=t.createDiv({cls:"wl-regen-progress-wrap"});i.hide(),i.createSpan({cls:"wl-csv-bg-note",text:"You can close settings \u2014 regeneration continues in the background."});let n=i.createDiv({cls:"wl-csv-progress-track"}).createDiv({cls:"wl-csv-progress-fill"}),r=i.createSpan({cls:"wl-csv-progress-text",text:"0 / 0"});e.wrap=i,e.fill=n,e.text=r}async exportBackup(){var c;let t=new Date,i=`watchlog-backup-${`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`}.json`,a={version:2,exportedAt:t.toISOString(),watch:(c=this.plugin.dataManager.getData())!=null?c:{},reading:this.plugin.readingDataManager.getData(),history:this.plugin.historyManager.exportEntries()},n=JSON.stringify(a,null,2);if(R.Platform.isMobile){await this.exportBackupToVault(i,n);return}let r=new Blob([n],{type:"application/json"}),o=URL.createObjectURL(r),l=activeDocument.createElement("a");l.href=o,l.download=i,activeDocument.body.appendChild(l),l.click(),activeDocument.body.removeChild(l),URL.revokeObjectURL(o),new R.Notice(`Backup exported as ${i}`)}async exportBackupToVault(t,e){let i=this.app.vault.adapter,a=(0,R.normalizePath)("WatchLog/backups");if(!await i.exists(a))try{await i.mkdir(a)}catch(c){}let n=t.lastIndexOf("."),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n),l=(0,R.normalizePath)(`${a}/${t}`);if(await i.exists(l)){let c=2;for(;await i.exists((0,R.normalizePath)(`${a}/${r}-${c}${o}`));)c++;l=(0,R.normalizePath)(`${a}/${r}-${c}${o}`)}await i.write(l,e),new R.Notice(`Backup saved to ${l}`)}openRestoreDialog(){let t=activeDocument.createElement("input");t.type="file",t.accept=".json",t.hide(),activeDocument.body.appendChild(t);let e=!1,i=()=>{if(t.parentElement)try{activeDocument.body.removeChild(t)}catch(n){}},a=()=>{window.setTimeout(()=>{e||i(),window.removeEventListener("focus",a)},200)};window.addEventListener("focus",a),t.addEventListener("change",()=>{var o;e=!0;let n=(o=t.files)==null?void 0:o[0];if(!n){i();return}let r=new FileReader;r.onload=l=>{var h;i();let c=(h=l.target)==null?void 0:h.result,d;try{d=JSON.parse(c)}catch(p){new R.Notice("Invalid backup file \u2014 could not parse JSON.");return}if(typeof d!="object"||d===null){new R.Notice("Invalid backup file \u2014 not an object.");return}let u=d;if(u.version===2){let p=u.watch,m=u.reading,v=u.history;if(!this.isValidWatchData(p)){new R.Notice("Invalid backup file \u2014 watch data missing required fields (titles, groups, settings).");return}if(!this.isValidReadingData(m)){new R.Notice("Invalid backup file \u2014 reading data missing required fields (books, manga).");return}if(!Array.isArray(v)){new R.Notice("Invalid backup file \u2014 activity log is not a list.");return}new H(this.app,"This will replace ALL current watchlog data \u2014 watchlist, reading (books & manga) and activity log. Continue?",()=>{(async()=>(await this.plugin.saveData(p),await this.plugin.loadSettings(),await this.plugin.dataManager.load(),await this.plugin.readingDataManager.restore(m),await this.plugin.historyManager.restore(v),activeDocument.dispatchEvent(new CustomEvent("watchlog-data-changed")),new R.Notice("Backup restored successfully.")))()}).open();return}if(!this.isValidWatchData(d)){new R.Notice("Invalid backup file \u2014 missing required fields (titles, groups, settings).");return}new H(this.app,"This is a legacy (watchlist-only) backup. It will replace your current watchlist. Reading and activity-log data are left untouched. Continue?",()=>{this.plugin.saveData(d).then(async()=>{await this.plugin.loadSettings(),await this.plugin.dataManager.load(),activeDocument.dispatchEvent(new CustomEvent("watchlog-data-changed")),new R.Notice("Legacy backup restored \u2014 watchlist only; reading and activity log left untouched.")})}).open()},r.readAsText(n)}),t.click()}isValidWatchData(t){if(typeof t!="object"||t===null)return!1;let e=t;return Array.isArray(e.titles)&&Array.isArray(e.groups)&&typeof e.settings=="object"&&e.settings!==null}isValidReadingData(t){if(typeof t!="object"||t===null)return!1;let e=t;return Array.isArray(e.books)&&Array.isArray(e.manga)&&typeof e.settings=="object"&&e.settings!==null}async refetchAllPosters(){let t=this.plugin.dataManager.getTitles();for(let i of t)i.posterUrl="";await this.plugin.dataManager.save();let e=this.plugin.readingDataManager;for(let i of e.getBooks())i.coverUrl="";for(let i of e.getMangaList())i.coverUrl="";await e.saveAndNotify(),new R.Notice("Cached posters and covers cleared. Watch posters reload as you browse; refresh reading covers from each card's \u22EE menu.")}async runRegenerate(t,e,i){let a=this.plugin.dataManager.getTitles(),n=this.plugin.readingDataManager,r=n.getBooks(),o=n.getMangaList(),l=a.length+r.length+o.length;if(t.show(),l===0){i.textContent="Done \u2014 no missing files found";return}e.style.width="0%",i.textContent=`0 / ${l}`;let c=0,d=0,u=0,h=p=>{p?c++:d++,u++;let m=Math.round(u/l*100);e.style.width=`${m}%`,i.textContent=`${u} / ${l}`};for(let p of a)h(await this.plugin.dataManager.createMarkdownFileIfMissing(p));for(let p of r)h(await n.createReadingNoteIfMissing("book",p));for(let p of o)h(await n.createReadingNoteIfMissing("manga",p));c===0?i.textContent="Done \u2014 no missing files found":i.textContent=`Done \u2014 ${c} file${c!==1?"s":""} created, ${d} already existed`}apiCallout(t,e,i){let a=t.createDiv({cls:`wl-api-callout wl-api-callout--${i}`});return a.createDiv({cls:"wl-api-callout-title",text:e}),a.createDiv({cls:"wl-api-callout-body"})}renderApi(t){let e=this.apiCallout(t,"Movies & TV Shows","movies");new R.Setting(e).setName("Active API").setDesc("Which API to use for movies and tv shows.").addDropdown(u=>{var h;return u.addOptions({OMDb:"OMDb",TMDB:"TMDB"}).setValue((h=this.plugin.settings.activeApi)!=null?h:"OMDb").onChange(async p=>{this.plugin.settings.activeApi=p,await this.plugin.saveSettings()})});let i=e.createDiv({cls:"wl-settings-info"});i.createSpan({text:"Get a free OMDb API key at "}),i.createEl("a",{text:"omdbapi.com/apikey.aspx",href:"https://www.omdbapi.com/apikey.aspx",attr:{target:"_blank",rel:"noopener noreferrer"}}),i.createSpan({text:"."});let a;new R.Setting(e).setName("Omdb API key").addText(u=>(u.setPlaceholder("Paste your omdb key here").setValue(this.plugin.settings.omdbApiKey).onChange(h=>{this.plugin.settings.omdbApiKey=h,this.plugin.apiService.setOmdbKey(h),this.debouncedSaveSettings()}),u.inputEl.type="password",u)).addButton(u=>u.setButtonText("Test").onClick(async()=>{let h=await this.plugin.apiService.checkOmdbConnection();a.textContent=h?"\u2713 Connected":"\u2717 Failed \u2014 check your key",a.className=h?"wl-status-ok":"wl-status-error"})),a=e.createDiv({cls:"wl-status-indicator",text:this.plugin.settings.omdbApiKey?"(not tested)":"Not set"});let n=e.createDiv({cls:"wl-settings-info"});n.createSpan({text:"Get a free TMDB read access token at "}),n.createEl("a",{text:"themoviedb.org/settings/api",href:"https://www.themoviedb.org/settings/api",attr:{target:"_blank",rel:"noopener noreferrer"}}),n.createSpan({text:"."});let r;new R.Setting(e).setName("Tmdb API read access token").addText(u=>{var h;return u.setPlaceholder("Paste your tmdb key here").setValue((h=this.plugin.settings.tmdbApiKey)!=null?h:"").onChange(p=>{this.plugin.settings.tmdbApiKey=p,this.plugin.apiService.setTmdbKey(p),this.debouncedSaveSettings()}),u.inputEl.type="password",u}).addButton(u=>u.setButtonText("Test").onClick(async()=>{let h=await this.plugin.apiService.checkTmdbConnection();r.textContent=h?"\u2713 Connected":"\u2717 Failed \u2014 check your key",r.className=h?"wl-status-ok":"wl-status-error"})),r=e.createDiv({cls:"wl-status-indicator",text:this.plugin.settings.tmdbApiKey?"(not tested)":"Not set"});let o=this.apiCallout(t,"Anime","anime");new R.Setting(o).setName("Anime API source").setDesc("Which API to use for new anime titles.").addDropdown(u=>{var h;return u.addOptions({jikan:"Jikan (MyAnimeList)",anilist:"AniList"}).setValue((h=this.plugin.settings.animeApiSource)!=null?h:"jikan").onChange(async p=>{this.plugin.settings.animeApiSource=p,await this.plugin.saveSettings()})}),o.createDiv({cls:"wl-settings-info",text:"Jikan uses MyAnimeList's database (larger catalog, no API key needed). AniList has its own database with more precise airing schedules and a GraphQL API. This setting only affects new titles \u2014 existing titles keep their original API source."});let l=this.apiCallout(t,"Books","books"),c;new R.Setting(l).setName("Google Books API key").addText(u=>{var h;return u.setPlaceholder("Paste your Google Books key here").setValue((h=this.plugin.settings.googleBooksApiKey)!=null?h:"").onChange(p=>{this.plugin.settings.googleBooksApiKey=p,this.plugin.apiService.setGoogleBooksKey(p),this.debouncedSaveSettings()}),u.inputEl.type="password",u}).addButton(u=>u.setButtonText("Test").onClick(async()=>{c.textContent="Testing...",c.className="wl-status-indicator";try{let h=await this.plugin.apiService.checkGoogleBooksConnection();c.textContent=h?"\u2713 Connected":"\u2717 Failed",c.className=h?"wl-status-ok":"wl-status-error"}catch(h){c.textContent=`\u2717 ${Et(h)}`,c.className="wl-status-error"}})),c=l.createDiv({cls:"wl-status-indicator",text:this.plugin.settings.googleBooksApiKey?"(not tested)":"Not set"});let d=l.createDiv({cls:"wl-settings-info"});d.createSpan({text:"Get a free key in the Google Cloud Console: create a project, enable the Books API, then create an API key under "}),d.createEl("a",{text:"APIs & Services \u2192 Credentials",href:"https://console.cloud.google.com/apis/credentials",attr:{target:"_blank",rel:"noopener noreferrer"}}),d.createSpan({text:"."}),this.renderTypeApiMapping(t)}renderTypeApiMapping(t){var l,c,d;let e=t.createDiv({cls:"wl-settings-card"});e.createDiv({cls:"wl-settings-card-title",text:"API routing by type in Watchlist"}),e.createDiv({cls:"wl-settings-card-desc",text:"Choose which API group to use for each title type. Anime, Movie, and TV Show are locked to their default APIs."});let i=e.createDiv({cls:"wl-type-api-list"}),a=(l=this.plugin.settings.types)!=null?l:[],n=(c=this.plugin.settings.typeApiMapping)!=null?c:{},r=new Set(a.map(u=>u.name)),o=!1;for(let u of Object.keys(n))(!r.has(u)||u==="Anime"||u==="Movie"||u==="TV Show"||u==="TvShow")&&(delete n[u],o=!0);o&&this.plugin.saveSettings();for(let u of a){let h=i.createDiv({cls:"wl-type-api-row"});if(h.createSpan({cls:"wl-type-api-name",text:u.name}),u.name==="Anime")h.createSpan({cls:"wl-type-api-locked",text:"Jikan / AniList (locked)"});else if(u.name==="Movie"||u.name==="TV Show"||u.name==="TvShow")h.createSpan({cls:"wl-type-api-locked",text:"OMDb / TMDB (locked)"});else{let p=h.createEl("select",{cls:"wl-select wl-type-api-select"}),m=(d=n[u.name])!=null?d:"",v=[{value:"",label:"\u2014 Not set \u2014"},{value:"anime",label:"Anime API (Jikan / AniList)"},{value:"movie",label:"Movie / TV API (OMDb / TMDB)"}];for(let f of v){let y=p.createEl("option",{text:f.label,value:f.value});m===f.value&&(y.selected=!0)}p.addEventListener("change",()=>{let f=p.value;this.plugin.settings.typeApiMapping||(this.plugin.settings.typeApiMapping={}),f===""?delete this.plugin.settings.typeApiMapping[u.name]:this.plugin.settings.typeApiMapping[u.name]=f,this.plugin.saveSettings()})}}}renderCustomize(t){this.renderThemePicker(t);let e=[{key:"types",label:"Type"},{key:"statuses",label:"Status"},{key:"priorities",label:"Priority"}];for(let i of e)this.renderTagSection(t,i.label,i.key);this.renderSeasonColors(t),this.renderReadingColors(t)}renderReadingColors(t){let e=t.createDiv({cls:"wl-tag-section"});e.createDiv({cls:"wl-settings-section-title",text:"Reading"});let i=[{label:"Manga",kind:"manga"},{label:"Book",kind:"book"}],a=e.createDiv({cls:"wl-tags-list"});for(let{label:n,kind:r}of i){let o=a.createDiv({cls:"wl-tag-pill"}),l=o.createDiv({cls:"wl-tag-dot-wrap"}),c=l.createSpan({cls:"wl-tag-dot"});c.style.backgroundColor=this.plugin.settings.readingTypeColors[r];let d=l.createEl("input",{cls:"wl-tag-color-picker",attr:{type:"color",value:this.plugin.settings.readingTypeColors[r]}});c.addEventListener("click",()=>d.click()),d.addEventListener("change",()=>{(async()=>(this.plugin.settings.readingTypeColors[r]=d.value,c.style.backgroundColor=d.value,await this.plugin.saveSettings()))()}),o.createSpan({cls:"wl-tag-name",text:n})}}renderThemePicker(t){var o;let e=t.createDiv({cls:"wl-tag-section"});e.createDiv({cls:"wl-settings-section-title",text:"Theme"});let i=[{key:"default",label:"Default",colors:["#1D9E75","#378ADD","#BA7517","#7F77DD","#E24B4A"]},{key:"nightfall",label:"Nightfall",colors:["#10002B","#3C096C","#7B2CBF","#C77DFF","#E0AAFF"]},{key:"bluez",label:"Bluez",colors:["#012A4A","#01497C","#2C7DA0","#61A5C2","#A9D6E5"]}],a=(o=this.plugin.settings.colorTheme)!=null?o:"default",n=e.createDiv({cls:"wl-theme-cards"}),r=[];for(let l of i){let c=n.createDiv({cls:`wl-theme-card${a===l.key?" is-selected":""}`});r.push(c),c.createDiv({cls:"wl-theme-card-name",text:l.label});let d=c.createDiv({cls:"wl-theme-strip"});for(let u of l.colors){let h=d.createDiv({cls:"wl-theme-swatch"});h.style.backgroundColor=u}c.addEventListener("click",()=>{(async()=>{this.plugin.settings.colorTheme=l.key,await this.plugin.saveSettings();for(let h of r)h.removeClass("is-selected");c.addClass("is-selected"),activeDocument.dispatchEvent(new CustomEvent("watchlog-data-changed"));let u=this.plugin.app.workspace.getLeavesOfType("watchlog-view");for(let h of u)h.view instanceof kt&&h.view.refreshUI()})()})}}renderSeasonColors(t){let e=t.createDiv({cls:"wl-tag-section"});e.createDiv({cls:"wl-settings-section-title",text:"Season colors"}),e.createDiv({cls:"wl-settings-info",text:"Colors cycle through seasons in order (Season 1 = color 1, etc.)."});let i=e.createDiv({cls:"wl-season-colors-list"}),a=()=>{i.empty(),this.plugin.settings.seasonPalette.forEach((l,c)=>{let d=i.createDiv({cls:"wl-season-color-pill"}),u=d.createEl("input",{cls:"wl-color-input",attr:{type:"color",value:l}});u.addEventListener("change",()=>{(async()=>(this.plugin.settings.seasonPalette[c]=u.value,await this.plugin.saveSettings()))()}),d.createSpan({cls:"wl-season-color-label",text:`Season ${c+1}`}),d.createSpan({cls:"wl-tag-del",text:"\xD7"}).addEventListener("click",()=>{(async()=>(this.plugin.settings.seasonPalette.splice(c,1),await this.plugin.saveSettings(),a()))()})})};a();let n=e.createDiv({cls:"wl-tag-add-row"}),r=n.createEl("input",{cls:"wl-color-input",attr:{type:"color",value:"#888780"}});n.createEl("button",{cls:"wl-btn wl-btn-success",text:"+ add color"}).addEventListener("click",()=>{(async()=>(this.plugin.settings.seasonPalette.push(r.value),await this.plugin.saveSettings(),r.value="#888780",a()))()})}renderTagSection(t,e,i){let a=i==="statuses",n=i==="types",r=t.createDiv({cls:"wl-tag-section"});r.createDiv({cls:"wl-settings-section-title",text:e}),a&&r.createDiv({cls:"wl-settings-info",text:"Status names are locked. Only the color can be changed."}),n&&r.createDiv({cls:"wl-settings-info",text:"Built-in types are locked. Custom types can be added and removed."});let o=r.createDiv({cls:"wl-tags-list"}),l=()=>{o.empty();for(let c of this.plugin.settings[i])this.renderTagPill(o,c,i,l)};if(l(),!a){let c=r.createDiv({cls:"wl-tag-add-row"}),d=c.createEl("input",{cls:"wl-modal-input",attr:{type:"text",placeholder:"Name"}}),u=c.createEl("input",{cls:"wl-color-input",attr:{type:"color",value:"#888780"}});c.createEl("button",{cls:"wl-btn wl-btn-success",text:"+ add"}).addEventListener("click",()=>{(async()=>{let p=d.value.trim();p&&(this.plugin.settings[i].push({name:p,color:u.value}),await this.plugin.saveSettings(),d.value="",u.value="#888780",l())})()})}}renderTagPill(t,e,i,a){let n=t.createDiv({cls:"wl-tag-pill"}),r=n.createDiv({cls:"wl-tag-dot-wrap"}),o=r.createSpan({cls:"wl-tag-dot"});o.style.backgroundColor=e.color;let l=r.createEl("input",{cls:"wl-tag-color-picker",attr:{type:"color",value:e.color}});o.addEventListener("click",()=>l.click()),l.addEventListener("change",()=>{(async()=>(e.color=l.value,o.style.backgroundColor=l.value,await this.plugin.saveSettings()))()}),n.createSpan({cls:"wl-tag-name",text:e.name});let c=i==="types"&&Ke.LOCKED_TYPES.includes(e.name);i!=="statuses"&&!c&&n.createSpan({cls:"wl-tag-del",text:"\xD7"}).addEventListener("click",()=>{(async()=>(this.plugin.settings[i]=this.plugin.settings[i].filter(u=>u.name!==e.name),await this.plugin.saveSettings(),a(),new R.Notice(`Removed "${e.name}".`)))()})}renderWatchlist(t){new R.Setting(t).setName("Default watchlist view").setDesc("Which sub-tab opens by default when entering the Watchlist.").addDropdown(a=>{var n;return a.addOptions({list:"List",cards:"Cards"}).setValue((n=this.plugin.settings.defaultWatchlistView)!=null?n:"cards").onChange(async r=>{this.plugin.settings.defaultWatchlistView=r,await this.plugin.saveSettings()})}),new R.Setting(t).setName("Episode numbering").setDesc("Absolute: episodes numbered 1\u2192n across all seasons. Per season: each season restarts from 1 (display only, data is unchanged).").addDropdown(a=>{var n;return a.addOptions({absolute:"Absolute","per-season":"Per season"}).setValue((n=this.plugin.settings.episodeNumbering)!=null?n:"absolute").onChange(async r=>{this.plugin.settings.episodeNumbering=r,await this.plugin.saveSettings(),activeDocument.dispatchEvent(new CustomEvent("watchlog-data-changed"))})}),new R.Setting(t).setName("Auto-complete on last episode").setDesc("Mark status as completed when all episodes are watched.").addToggle(a=>a.setValue(this.plugin.settings.autoCompleteOnLastEpisode).onChange(async n=>{this.plugin.settings.autoCompleteOnLastEpisode=n,await this.plugin.saveSettings()})),new R.Setting(t).setName("Colored type badges").setDesc("Show type and status badges with their configured colors. Disable for a plain text style.").addToggle(a=>a.setValue(this.plugin.settings.coloredTypeBadges).onChange(async n=>{this.plugin.settings.coloredTypeBadges=n,await this.plugin.saveSettings()})),t.createDiv({cls:"wl-settings-section-title",text:"Import / Export CSV"}),new R.Setting(t).setName("Export to CSV").setDesc("Export selected titles as a CSV file.").addButton(a=>a.setButtonText("Export CSV").onClick(()=>{new le(this.app,this.plugin,this.plugin.dataManager,"export").open()})),new R.Setting(t).setName("Import from CSV").setDesc("Import titles from a CSV file. Duplicates are highlighted before import.").addButton(a=>a.setButtonText("Import CSV").onClick(()=>{new le(this.app,this.plugin,this.plugin.dataManager,"import").open()})),t.createDiv({cls:"wl-settings-section-title",text:"Folders"}),new R.Setting(t).setName("Root folder name").setDesc("Vault folder where watchlog Markdown files are stored.").addText(a=>a.setValue(this.plugin.settings.rootFolder).onChange(async n=>{this.plugin.settings.rootFolder=n,await this.plugin.saveSettings()})),new R.Setting(t).setName("Auto-create folders").setDesc("Automatically create type subfolders on plugin load.").addToggle(a=>a.setValue(this.plugin.settings.autoCreateFolders).onChange(async n=>{this.plugin.settings.autoCreateFolders=n,await this.plugin.saveSettings()})),t.createDiv({cls:"wl-settings-section-title",text:"Type subfolders"});let e=t.createDiv({cls:"wl-folder-list"});(()=>{e.empty();for(let a of this.plugin.settings.types){let n=e.createDiv({cls:"wl-folder-row"});n.createSpan({cls:"wl-folder-type",text:a.name}),n.createSpan({cls:"wl-folder-path",text:`${this.plugin.settings.rootFolder}/${a.name}`})}})(),new R.Setting(t).setName("Create folders now").setDesc("Manually trigger folder creation for all current types.").addButton(a=>a.setButtonText("Create").onClick(async()=>{await this.plugin.dataManager.ensureFolders(),new R.Notice("Folders created.")}))}renderDrafts(t){t.createDiv({cls:"wl-settings-section-title",text:"Drafts"}),new R.Setting(t).setName("Vault tag to monitor").setDesc("Lines containing this tag followed by a title will appear in the drafts tab.").addText(e=>{var i;return e.setPlaceholder("#watchlog").setValue((i=this.plugin.settings.draftsVaultTag)!=null?i:"#watchlog").onChange(async a=>{this.plugin.settings.draftsVaultTag=a.trim()||"#watchlog",await this.plugin.saveSettings()})}),new R.Setting(t).setName("After adding a title").setDesc("What happens to a draft entry once you hit add and confirm.").addDropdown(e=>{var i;return e.addOptions({keep:"Keep as Added",remove:"Remove"}).setValue((i=this.plugin.settings.draftsAfterAdding)!=null?i:"keep").onChange(async a=>{this.plugin.settings.draftsAfterAdding=a,await this.plugin.saveSettings()})})}renderCustomLists(t){t.createDiv({cls:"wl-settings-section-title",text:"Custom Lists"}),new R.Setting(t).setName("Custom lists folder path").setDesc("Vault folder where custom list files are stored.").addText(e=>{var i;return e.setValue((i=this.plugin.settings.customListsFolder)!=null?i:"WatchLog/CustomLists").onChange(async a=>{this.plugin.settings.customListsFolder=a,await this.plugin.saveSettings()})}),new R.Setting(t).setName("Default columns").setDesc("Columns pre-populated when creating a new list. The name column is always included.").addButton(e=>{var a;let i=((a=this.plugin.settings.defaultCustomColumns)!=null?a:[]).length;e.setButtonText(`Edit (${i} column${i!==1?"s":""})`),e.onClick(()=>{new Le(this.app,this.plugin).open()})}),new R.Setting(t).setName("Create folder now").setDesc("Manually create the custom lists folder in the vault.").addButton(e=>e.setButtonText("Create").onClick(async()=>{await new Wt(this.app,this.plugin).ensureFolder(),new R.Notice("Custom lists folder created.")}))}renderReading(t){let e=this.plugin.readingDataManager,i=e.getSettings();t.createDiv({cls:"wl-settings-section-title",text:"Reading"}),new R.Setting(t).setName("Reading folder path").setDesc("Vault folder where reading note files are generated. Changing this will not move existing files.").addText(a=>a.setPlaceholder("WatchLog/Reading").setValue(i.defaultFolder).onChange(n=>{let r=n.trim()||"WatchLog/Reading";e.updateSettings({defaultFolder:r})})),new R.Setting(t).setName("Default status for new entries").setDesc("Status pre-selected in the Add book / Add manga modal.").addDropdown(a=>{let n={};for(let r of lt)n[r]=r;a.addOptions(n).setValue(i.defaultStatus).onChange(r=>{e.updateSettings({defaultStatus:r})})}),new R.Setting(t).setName("Default sub-tab").setDesc("Which sub-tab opens by default when you switch to the Reading tab.").addDropdown(a=>{var n;a.addOptions({books:"Books",manga:"Manga"}).setValue((n=i.defaultSubTab)!=null?n:"books").onChange(r=>{e.updateSettings({defaultSubTab:r})})}),t.createDiv({cls:"wl-settings-section-title",text:"Import / Export CSV"}),new R.Setting(t).setName("Export to CSV").setDesc("Export your Books or Manga library as a CSV file.").addButton(a=>a.setButtonText("Export CSV").onClick(()=>{new ce(this.app,"export",n=>{new de(this.app,this.plugin,n,"export").open()}).open()})),new R.Setting(t).setName("Import from CSV").setDesc("Import Books or Manga from a CSV file, with column mapping and duplicate detection.").addButton(a=>a.setButtonText("Import CSV").onClick(()=>{new ce(this.app,"import",n=>{new de(this.app,this.plugin,n,"import").open()}).open()}))}renderQuickInfo(t){t.createDiv({cls:"wl-settings-section-title",text:"Quick Info"});let e=[{title:"\u{1F4CA} Dashboard",items:[{heading:"Please note",body:"Statuses To be released or Dropped (time left) are not included in any calculations, only Watching, Completed and Dropped (time watched)."},{heading:"Time Watched",body:"Calculated as: episodes watched \xD7 episode duration, summed across all titles with status Watching and Completed."},{heading:"Time Remaining",body:"Calculated as: episodes remaining \xD7 episode duration, summed across all titles with status Watching and Plan to Watch."},{heading:"Dropped",body:"Calculated as: episodes watched \xD7 episode duration, summed across all titles with status Dropped."}]},{title:"\u{1F4CB} Watchlist",items:[{heading:"Why Groups exist",body:"Groups let you combine related titles under one entry; for example, a movie and its sequel, a TV show and its anime adaptation, or an anime series and its movie. They appear as a single collapsible row in your Watchlist."},{heading:"Group Rating",body:"A group's rating is automatically calculated as the average of all individual title ratings within it."},{heading:"Pin to Top",body:"Pinning a title moves it to the top of your Watchlist regardless of sorting. Pinned titles also appear in the Now Watching widget, so you can quickly see what you're currently watching inside your notes or Homepage."},{heading:"How to add a new season",body:'Open the title and click Edit at the bottom. In the Edit modal, scroll down to the Seasons field and add a new line in the format "Season Name: N" (e.g. "Season 3: 10" or "Season name: 10").'}]},{title:"\u{1F50D} API & Search",items:[{heading:"Which API is used for what",body:`WatchLog uses a different API per content type:
+\u2022 Anime \u2014 Jikan (MyAnimeList data) or AniList, whichever you set under Settings \u2192 API \u2192 Anime. No key is needed for either.
+\u2022 Movies & TV Shows \u2014 OMDb or TMDB, whichever is set as active under Settings \u2192 API. Both need a free key/token.
+\u2022 Books (Reading) \u2014 Google Books. A free API key is required; add it under Settings \u2192 API \u2192 Books.
+\u2022 Manga (Reading) \u2014 Jikan (MyAnimeList data). No key needed.
-Note: APIs are there to help, not mandatory; you can manually add a new title.
+The anime API choice only affects new titles \u2014 existing titles keep the source they were added with.
-Note: The "Add from URL" button also uses an API.`}]},{title:"\u{1F4C5} Upcoming",items:[{heading:"How Upcoming works",body:"Any title with a release date set in the future is automatically marked as To be released and appears in this tab. This status cannot be set manually; it is determined solely by the release date you enter. If a title has already started airing but only some episodes are out (e.g. an ongoing series), it will not appear here automatically; you'll need to manually add it through the Add button in the Upcoming tab."}]}];for(let i of s){let a=t.createDiv({cls:"wl-qi-section"}),l=a.createDiv({cls:"wl-qi-section-header"}),n=l.createSpan({cls:"wl-qi-chevron",text:"\u25B6"});l.createSpan({cls:"wl-qi-section-label",text:i.title});let r=a.createDiv({cls:"wl-qi-section-body wl-qi-section-body-hidden"});for(let o of i.items){let c=r.createDiv({cls:"wl-qi-banner"});c.createDiv({cls:"wl-qi-banner-heading",text:o.heading}),c.createDiv({cls:"wl-qi-banner-body",text:o.body})}l.addEventListener("click",()=>{let o=!r.hasClass("wl-qi-section-body-hidden");o?r.addClass("wl-qi-section-body-hidden"):r.removeClass("wl-qi-section-body-hidden"),n.textContent=o?"\u25B6":"\u25BC"})}}renderWidgets(t){t.createDiv({cls:"wl-settings-info",text:'Insert widgets into notes using the "WatchLog: Insert widget" command. Configure the keyboard shortcut in Obsidian Settings \u2192 Hotkeys.'});let s=[{name:"wl-todo",desc:'Track a specific title inline \u2014 shows type, status, progress, and a "next episode" checkbox.',syntax:"```wl-todo\nTitle Name\nmini\n```"},{name:"wl-todo (full)",desc:"Track a specific title inline (full card).",syntax:"```wl-todo\nTitle Name\n```"},{name:"wl-stat: completed",desc:"Completed titles count \u2014 compact inline stat card.",syntax:"```wl-stat\ncompleted\n```"},{name:"wl-stat: time",desc:"Time watched & remaining combined (mini).",syntax:"```wl-stat\ntime\n```"},{name:"wl-upcoming: next",desc:"Next upcoming title with name, type badge, release date, and countdown.",syntax:"```wl-upcoming\nnext\n```"},{name:"wl-nowwatching",desc:"Currently pinned title with name, type badge, and progress bar.",syntax:"```wl-nowwatching\n```"},{name:"wl-stat: time completed full",desc:"Full-width triple card: Time Watched \xB7 Time Remaining \xB7 Completed.",syntax:"```wl-stat\ntime completed full\n```"},{name:"wl-now-next",desc:"Full-width double card: Now Watching \xB7 Up Next.",syntax:"```wl-now-next\n```"}];for(let i of s){let a=t.createDiv({cls:"wl-widget-info-section"});a.createDiv({cls:"wl-widget-info-name",text:i.name}),a.createDiv({cls:"wl-widget-info-desc",text:i.desc});let l=a.createDiv({cls:"wl-widget-info-code-row"});l.createEl("code",{cls:"wl-widget-info-code",text:i.syntax});let n=l.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Copy"});n.addEventListener("click",()=>{navigator.clipboard.writeText(i.syntax).then(()=>{n.textContent="Copied!",window.setTimeout(()=>{n.textContent="Copy"},1500)})})}}};Xt.LOCKED_TYPES=["Anime","Movie","TV Show"];var Jt=Xt;var Zt=class{constructor(e,t){this.widgetRegistry=new Map;this.widgetMiniRegistry=new Map;this.plugin=e,this.dataManager=t,this.plugin.registerMarkdownCodeBlockProcessor("watchlog",(s,i,a)=>this.process(s,i,a)),this.plugin.registerMarkdownCodeBlockProcessor("wl-todo",(s,i,a)=>this.processWlTodo(s,i)),this.plugin.registerMarkdownCodeBlockProcessor("wl-stat",(s,i,a)=>this.processWlStat(s,i)),this.plugin.registerMarkdownCodeBlockProcessor("wl-upcoming",(s,i,a)=>this.processWlUpcoming(s,i)),this.plugin.registerMarkdownCodeBlockProcessor("wl-nowwatching",(s,i,a)=>this.processWlNowWatching(s,i)),this.plugin.registerMarkdownCodeBlockProcessor("wl-now-next",(s,i,a)=>this.processWlNowNext(s,i)),this.dataChangedListener=()=>this.onDataChanged(),activeDocument.addEventListener("watchlog-data-changed",this.dataChangedListener),e.register(()=>{activeDocument.removeEventListener("watchlog-data-changed",this.dataChangedListener)})}onDataChanged(){var t;let e=[];for(let[s,i]of this.widgetRegistry){if(!s.isConnected){e.push(s);continue}let a=this.dataManager.getTitle(i);if(!a){e.push(s);continue}((t=this.widgetMiniRegistry.get(s))!=null?t:!1)?this.renderWidgetMinimal(s,a):this.renderWidget(s,a)}for(let s of e)this.widgetRegistry.delete(s),this.widgetMiniRegistry.delete(s)}process(e,t,s){var n;let i=e.match(/id:\s*(.+)/),a=(n=i==null?void 0:i[1])==null?void 0:n.trim();if(!a){t.createDiv({cls:"wl-widget-error",text:"WatchLog: missing id field."});return}let l=this.dataManager.getTitle(a);if(!l){t.createDiv({cls:"wl-widget-error",text:`WatchLog: title "${a}" not found.`});return}this.widgetRegistry.set(t,a),this.renderWidget(t,l)}renderWidget(e,t){this.renderWidgetFull(e,t)}renderWidgetFull(e,t){var m;e.empty(),e.addClass("wl-widget");let s=e.createDiv({cls:"wl-widget-main-row"}),i=s.createEl("input",{cls:"wl-widget-cb",attr:{type:"checkbox"}});i.title="Personal task marker (does not affect watchlog data)",i.addEventListener("change",()=>{let f=s.querySelector(".wl-widget-title");f&&(f.style.textDecoration=i.checked?"line-through":"",f.style.opacity=i.checked?"0.5":"")}),s.createSpan({cls:"wl-widget-title",text:t.title}),this.sep(s);let a=this.getTagDef(t.type,this.plugin.settings.types),l=this.plugin.settings.coloredTypeBadges,n=s.createSpan({cls:l?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.type});l&&a&&(n.style.backgroundColor=_(t.type,a.color,this.plugin.settings.colorTheme)),this.sep(s);let r=this.getTagDef(t.status,this.plugin.settings.statuses),o=s.createSpan({cls:l?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.status});l&&r&&(o.style.backgroundColor=_(t.status,r.color,this.plugin.settings.colorTheme)),this.sep(s);let c=this.dataManager.getProgress(t),d=s.createDiv({cls:"wl-widget-bar-wrap"});d.createDiv({cls:"wl-progress-bar"}).style.width=`${c}%`,this.sep(s),s.createSpan({cls:"wl-widget-percent",text:`${c}%`}),this.sep(s);let u=(m=t.dateStarted)!=null?m:t.dateAdded,g=t.dateStarted?`since ${this.formatShortDate(u)}`:`added ${this.formatShortDate(u)}`;if(s.createSpan({cls:"wl-widget-date",text:g}),t.type==="Movie"){let f=e.createDiv({cls:"wl-widget-next-row"}),v=t.watchedEpisodes.includes(1),w=f.createEl("input",{cls:"wl-widget-next-cb",attr:{type:"checkbox"}});w.checked=v,w.title="Mark movie as watched",w.addEventListener("change",()=>{this.dataManager.markEpisodeWatched(t.id,1,w.checked).then(()=>{let y=this.dataManager.getTitle(t.id);y&&this.renderWidget(e,y)})}),f.createSpan({cls:"wl-widget-next-label",text:v?"Watched":"Mark as watched"})}else{let f=this.dataManager.getNextUnwatchedEpisode(t),v=e.createDiv({cls:"wl-widget-next-row"});if(f!==null){let w=v.createEl("input",{cls:"wl-widget-next-cb",attr:{type:"checkbox"}});w.checked=!1,w.title=`Mark Episode ${f} as watched`,w.addEventListener("change",()=>{this.dataManager.markEpisodeWatched(t.id,f,!0).then(()=>{let y=this.dataManager.getTitle(t.id);y&&this.renderWidget(e,y)})}),v.createSpan({cls:"wl-widget-next-label",text:`Next up: Ep ${f}`})}else v.createSpan({cls:"wl-widget-next-label",text:"\u2713 All episodes watched"})}}renderWidgetMinimal(e,t){e.empty(),e.addClass("wl-widget-minimal");let s=e.createDiv({cls:"wl-widget-minimal-row"}),i=s.createEl("input",{cls:"wl-widget-cb",attr:{type:"checkbox"}});i.title="Personal task marker (does not affect watchlog data)",i.addEventListener("change",()=>{let u=s.querySelector(".wl-widget-title");u&&(u.style.textDecoration=i.checked?"line-through":"",u.style.opacity=i.checked?"0.5":"")}),s.createSpan({cls:"wl-widget-title",text:t.title}),this.sep(s);let a=this.getTagDef(t.type,this.plugin.settings.types),l=this.plugin.settings.coloredTypeBadges,n=s.createSpan({cls:l?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.type});l&&a&&(n.style.backgroundColor=_(t.type,a.color,this.plugin.settings.colorTheme)),this.sep(s);let r=this.getTagDef(t.status,this.plugin.settings.statuses),o=s.createSpan({cls:l?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.status});l&&r&&(o.style.backgroundColor=_(t.status,r.color,this.plugin.settings.colorTheme)),this.sep(s);let c=this.dataManager.getProgress(t);if(s.createSpan({cls:"wl-widget-percent",text:`${c}%`}),this.sep(s),t.type==="Movie"){let u=t.watchedEpisodes.includes(1),g=s.createEl("input",{cls:"wl-widget-next-cb",attr:{type:"checkbox"}});g.checked=u,g.title="Mark movie as watched",g.addEventListener("change",()=>{this.dataManager.markEpisodeWatched(t.id,1,g.checked).then(()=>{let p=this.dataManager.getTitle(t.id);p&&this.renderWidgetMinimal(e,p)})})}else{let u=this.dataManager.getNextUnwatchedEpisode(t),g=s.createEl("input",{cls:"wl-widget-next-cb",attr:{type:"checkbox"}});u!==null?(g.checked=!1,g.title=`Mark Episode ${u} as watched`,g.addEventListener("change",()=>{this.dataManager.markEpisodeWatched(t.id,u,!0).then(()=>{let p=this.dataManager.getTitle(t.id);p&&this.renderWidgetMinimal(e,p)})})):(g.checked=!0,g.disabled=!0,g.title="All episodes watched")}}sep(e){e.createSpan({cls:"wl-widget-sep",text:"\xB7"})}getTagDef(e,t){return t.find(s=>s.name===e)}formatShortDate(e){try{let t=new Date(e);return`${String(t.getDate()).padStart(2,"0")}/${String(t.getMonth()+1).padStart(2,"0")}/${String(t.getFullYear()).slice(2)}`}catch(t){return e}}processWlTodo(e,t){var n;let s=e.trim().split(`
-`).map(r=>r.trim()).filter(Boolean),i=s.includes("mini"),a=(n=s.find(r=>r!=="mini"))!=null?n:"";if(!a){t.createDiv({cls:"wl-widget-error",text:"wl-todo: missing title name."});return}let l=this.dataManager.getTitles().find(r=>r.title.toLowerCase()===a.toLowerCase());if(!l){t.createDiv({cls:"wl-widget-error",text:`wl-todo: "${a}" not found.`});return}this.widgetRegistry.set(t,l.id),this.widgetMiniRegistry.set(t,i),i?this.renderWidgetMinimal(t,l):this.renderWidget(t,l)}processWlStat(e,t){let s=e.trim().toLowerCase();if(t.addClass("wl-wstat"),s==="watched"){let i=this.dataManager.getTotalTimeWatched();this.renderStatCard(t,"\u{1F550}",V(i),"watched")}else if(s==="completed"){let i=this.dataManager.getCompletedCount();this.renderStatCard(t,"\u2705",`${i} titles`,"completed")}else if(s==="remaining"){let i=this.dataManager.getTotalTimeRemaining();this.renderStatCard(t,"\u23F3",V(i),"remaining")}else if(s==="time"){let i=this.dataManager.getTotalTimeWatched(),a=this.dataManager.getTotalTimeRemaining();this.renderTimeCardMini(t,i,a)}else if(s==="time full"){let i=this.dataManager.getTotalTimeWatched(),a=this.dataManager.getTotalTimeRemaining();this.renderTimeCardFull(t,i,a)}else if(s==="completed full"){let i=this.dataManager.getCompletedCount();this.renderCompletedCardFull(t,i)}else if(s==="time completed full"){let i=this.dataManager.getTotalTimeWatched(),a=this.dataManager.getTotalTimeRemaining(),l=this.dataManager.getCompletedCount();this.renderTripleStatCard(t,i,a,l)}else t.createDiv({cls:"wl-widget-error",text:`wl-stat: unknown stat "${s}". Use watched, completed, remaining, time, time full, completed full, or time completed full.`})}renderStatCard(e,t,s,i){e.empty();let a=e.createDiv({cls:"wl-stat-card"});a.createSpan({cls:"wl-stat-icon",text:t}),a.createSpan({cls:"wl-stat-value",text:s}),a.createSpan({cls:"wl-stat-label",text:i})}renderTimeCardMini(e,t,s){e.empty();let i=e.createDiv({cls:"wl-stat-card wl-stat-card-time"});i.createSpan({cls:"wl-stat-icon",text:"\u{1F550}"}),i.createSpan({cls:"wl-stat-value",text:V(t)}),i.createSpan({cls:"wl-stat-label",text:"watched"}),i.createSpan({cls:"wl-stat-sep",text:"\xB7"}),i.createSpan({cls:"wl-stat-icon",text:"\u23F3"}),i.createSpan({cls:"wl-stat-value",text:V(s)}),i.createSpan({cls:"wl-stat-label",text:"left"})}renderTimeCardFull(e,t,s){e.empty();let i=e.createDiv({cls:"wl-full-card wl-full-card-time"});i.createDiv({cls:"wl-full-card-header",text:"Time"});let a=i.createDiv({cls:"wl-full-card-time-cols"}),l=(n,r)=>{let o=a.createDiv({cls:"wl-full-card-time-col"});o.createDiv({cls:"wl-full-card-value",text:V(n)}),o.createDiv({cls:"wl-full-card-days",text:this.formatTimeDays(n)}),o.createDiv({cls:"wl-full-card-sub",text:r})};l(t,"watched"),l(s,"remaining")}formatTimeDays(e){let t=Math.floor(e/1440),s=Math.floor(e%1440/60);return t===0?s>0?`${s}h`:"0h":`${t} days ${s}h`}renderCompletedCardFull(e,t){e.empty();let s=e.createDiv({cls:"wl-full-card"});s.createDiv({cls:"wl-full-card-header",text:"Completed"}),s.createDiv({cls:"wl-full-card-value",text:String(t)}),s.createDiv({cls:"wl-full-card-sub",text:"titles completed"})}renderTripleStatCard(e,t,s,i){e.empty(),e.addClass("wl-wstat-triple");let l=e.createDiv({cls:"wl-full-card wl-full-card-triple"}).createDiv({cls:"wl-triple-cols"}),n=(o,c)=>{let d=l.createDiv({cls:"wl-triple-col"});d.createDiv({cls:"wl-full-card-value",text:V(o)}),d.createDiv({cls:"wl-full-card-days",text:this.formatTimeDays(o)}),d.createDiv({cls:"wl-full-card-sub",text:c})};n(t,"watched"),l.createDiv({cls:"wl-vert-sep"}),n(s,"remaining"),l.createDiv({cls:"wl-vert-sep"});let r=l.createDiv({cls:"wl-triple-col"});r.createDiv({cls:"wl-full-card-value",text:String(i)}),r.createDiv({cls:"wl-full-card-sub",text:"completed"})}processWlUpcoming(e,t){let s=e.trim().toLowerCase(),i=s==="next full";if(s!=="next"&&s!=="next full"){t.createDiv({cls:"wl-widget-error",text:'wl-upcoming: only "next" or "next full" is supported.'});return}let a=this.dataManager.getAirtimeEntries(),l=this.dataManager.getTitles(),n=Date.now(),r=null,o=1/0;for(let v of a){let w=l.find(T=>T.id===v.titleId);if(!w||v.schedule.recurrence!=="once"||!v.schedule.releaseDate)continue;let y=new Date(v.schedule.releaseDate+"T12:00:00").getTime();if(y>=n&&y=30?` (${Math.round(p/30)} month${Math.round(p/30)!==1?"s":""})`:"",f=p===0?"today":p===1?"tomorrow":`in ${p} days${m}`;c.createSpan({cls:"wl-upcoming-countdown",text:f})}renderUpcomingFull(e,t){e.empty();let s=e.createDiv({cls:"wl-full-card"});if(s.createDiv({cls:"wl-full-card-header",text:"Up Next"}),!t){s.createDiv({cls:"wl-full-card-sub",text:"No upcoming titles."});return}s.createDiv({cls:"wl-full-card-title",text:t.titleName});let i=s.createDiv({cls:"wl-full-card-meta"}),a=this.getTagDef(t.type,this.plugin.settings.types),l=this.plugin.settings.coloredTypeBadges,n=i.createSpan({cls:l?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.type});l&&a&&(n.style.backgroundColor=_(t.type,a.color,this.plugin.settings.colorTheme));let r=t.daysUntil,o=r===0?"today":r===1?"tomorrow":`in ${r} day${r!==1?"s":""}`;i.createSpan({text:o}),i.createSpan({cls:"wl-full-card-sub",text:t.releaseDate})}processWlNowWatching(e,t){let s=e.trim().toLowerCase()==="full";t.addClass("wl-wnowwatching");let i=this.dataManager.getTitles().find(m=>m.pinned),a=this.dataManager.getPinnedGroupId(),l=a?this.dataManager.getGroups().find(m=>m.id===a):null;if(s){this.renderNowWatchingFull(t,i!=null?i:null,l!=null?l:null);return}if(!i&&!l){t.createDiv({cls:"wl-nowwatching-card wl-nowwatching-empty",text:"Pin a title to set it as now watching."});return}let n=t.createDiv({cls:"wl-nowwatching-card"});if(l){n.createSpan({cls:"wl-nowwatching-title",text:l.name});let m=this.dataManager.getTitles(),f=l.titleIds.map(D=>m.find(S=>S.id===D)).filter(D=>D!==void 0),v=f.reduce((D,S)=>D+S.watchedEpisodes.length,0),w=f.reduce((D,S)=>D+S.totalEpisodes,0),y=w>0?Math.round(v/w*100):0;n.createSpan({cls:"wl-nowwatching-ep",text:`${f.length} title${f.length!==1?"s":""}`});let T=n.createDiv({cls:"wl-nowwatching-bar-wrap"});T.createDiv({cls:"wl-nowwatching-bar"}).style.width=`${y}%`,n.createSpan({cls:"wl-nowwatching-pct",text:`${y}%`});return}let r=i;n.createSpan({cls:"wl-nowwatching-title",text:r.title});let o=this.getTagDef(r.type,this.plugin.settings.types),c=this.plugin.settings.coloredTypeBadges,d=n.createSpan({cls:c?"wl-badge wl-badge-sm":"wl-badge-plain",text:r.type});if(c&&o&&(d.style.backgroundColor=_(r.type,o.color,this.plugin.settings.colorTheme)),r.type!=="Movie"){let m=this.dataManager.getNextUnwatchedEpisode(r);n.createSpan({cls:"wl-nowwatching-ep",text:m!==null?`Ep ${m}`:"\u2713 Completed"})}let u=this.dataManager.getProgress(r),p=n.createDiv({cls:"wl-nowwatching-bar-wrap"}).createDiv({cls:"wl-nowwatching-bar"});p.style.width=`${u}%`,n.createSpan({cls:"wl-nowwatching-pct",text:`${u}%`})}renderNowWatchingFull(e,t,s){e.empty();let i=e.createDiv({cls:"wl-full-card wl-full-card-nw"});if(i.createDiv({cls:"wl-full-card-header",text:"NOW WATCHING"}),!t&&!s){i.createDiv({cls:"wl-full-card-sub",text:"Pin a title to set it as now watching."});return}if(s){i.createDiv({cls:"wl-full-card-title",text:s.name});let u=this.dataManager.getTitles(),g=s.titleIds.map(y=>u.find(T=>T.id===y)).filter(y=>y!==void 0),p=g.reduce((y,T)=>y+T.watchedEpisodes.length,0),m=g.reduce((y,T)=>y+T.totalEpisodes,0),f=m>0?Math.round(p/m*100):0,v=i.createDiv({cls:"wl-nw-bottom-row"});v.createSpan({cls:"wl-full-card-sub",text:`${g.length} title${g.length!==1?"s":""}`});let w=v.createDiv({cls:"wl-nw-bar-col"});w.createDiv({cls:"wl-nw-pct",text:`${f}%`}),w.createDiv({cls:"wl-full-card-bar-wrap"}).createDiv({cls:"wl-full-card-bar"}).style.width=`${f}%`;return}let a=t;i.createDiv({cls:"wl-full-card-title",text:a.title});let l=this.dataManager.getProgress(a),n=i.createDiv({cls:"wl-nw-bottom-row"}),r=this.getTagDef(a.type,this.plugin.settings.types),o=this.plugin.settings.coloredTypeBadges,c=n.createSpan({cls:o?"wl-badge wl-badge-sm":"wl-badge-plain",text:a.type});o&&r&&(c.style.backgroundColor=_(a.type,r.color,this.plugin.settings.colorTheme));let d=n.createDiv({cls:"wl-nw-bar-col"});d.createDiv({cls:"wl-nw-pct",text:`${l}%`}),d.createDiv({cls:"wl-full-card-bar-wrap"}).createDiv({cls:"wl-full-card-bar"}).style.width=`${l}%`}processWlNowNext(e,t){t.addClass("wl-wnownext");let s=this.dataManager.getTitles().find(d=>d.pinned),i=this.dataManager.getPinnedGroupId(),a=i?this.dataManager.getGroups().find(d=>d.id===i):null,l=this.dataManager.getAirtimeEntries(),n=this.dataManager.getTitles(),r=Date.now(),o=null,c=1/0;for(let d of l){let u=n.find(p=>p.id===d.titleId);if(!u||d.schedule.recurrence!=="once"||!d.schedule.releaseDate)continue;let g=new Date(d.schedule.releaseDate+"T12:00:00").getTime();if(g>=r&&go.find(m=>m.id===p)).filter(p=>p!==void 0),d=c.reduce((p,m)=>p+m.watchedEpisodes.length,0),u=c.reduce((p,m)=>p+m.totalEpisodes,0),g=u>0?Math.round(d/u*100):0;n.createDiv({cls:"wl-full-card-sub",text:`${c.length} title${c.length!==1?"s":""} \xB7 ${g}%`})}else{let o=t;n.createDiv({cls:"wl-full-card-title",text:o.title});let c=this.dataManager.getProgress(o);n.createDiv({cls:"wl-full-card-sub",text:`${c}%`})}l.createDiv({cls:"wl-vert-sep"});let r=l.createDiv({cls:"wl-double-col"});if(r.createDiv({cls:"wl-full-card-header",text:"UPCOMING NEXT"}),!i)r.createDiv({cls:"wl-full-card-sub",text:"No upcoming."});else{r.createDiv({cls:"wl-full-card-title",text:i.titleName});let o=i.daysUntil,c=o===0?"today":o===1?"tomorrow":`in ${o}d`;r.createDiv({cls:"wl-full-card-sub",text:`${i.releaseDate} \xB7 ${c}`})}}};var te=class extends rt.Plugin{constructor(){super(...arguments);this.settings=it;this.dataManager=new Et(this);this.apiService=new Dt("","");this.historyManager=new bt(this);this.importProgress=null;this.notifiedEntries=new Set}async onload(){await this.loadSettings(),this.dataManager=new Et(this),await this.dataManager.load(),this.dataManager.startWatchingExternalChanges(),this.apiService=new Dt(this.settings.omdbApiKey,this.settings.tmdbApiKey),this.historyManager=new bt(this),await this.historyManager.load(),this.dataManager.setHistoryManager(this.historyManager),await this.dataManager.ensureFolders(),this.startAirtimeScheduler(),this.registerView(At,t=>new yt(t,this,this.dataManager)),new Zt(this,this.dataManager),this.addRibbonIcon("tv","Watchlog",()=>{this.activateView()}),this.addCommand({id:"open-panel",name:"Open panel",callback:()=>void this.activateView()}),this.addCommand({id:"add-title",name:"Add title",callback:()=>{new tt(this.app,this,this.dataManager,()=>{this.activateView()}).open()}}),this.addCommand({id:"insert-widget",name:"Insert widget",editorCallback:(t,s)=>{this.openWidgetPalette(t)}}),this.addCommand({id:"search-title",name:"Search title",callback:()=>{let t=this.dataManager.getTitles();new dt(this.app,t,s=>{this.activateView().then(()=>{new rt.Notice(`"${s.title}" \u2014 ${s.type} \xB7 ${s.status}`)})}).open()}}),this.addSettingTab(new Jt(this.app,this))}onunload(){}startAirtimeScheduler(){this.checkAirtimeNotifications(),this.registerInterval(window.setInterval(()=>{this.checkAirtimeNotifications()},6e4))}markAirtimeHandled(t){this.notifiedEntries.add(t)}checkAirtimeNotifications(){var l,n;let t=new Date,s=`${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`,i=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,a=this.dataManager.getAirtimeEntries();for(let r of a){if(!r.schedule.releaseTime||r.schedule.releaseTime!==s||!this.isScheduledForToday(r.schedule))continue;let o=`${r.id}-${i}`;if(this.notifiedEntries.has(o))continue;this.notifiedEntries.add(o);let c=this.dataManager.getTitle(r.titleId);if(c&&(new rt.Notice(c.title,8e3),((l=c.totalEpisodes)!=null?l:0)>1&&r.currentEpisode!==void 0)){let d=(n=r.totalEpisodes)!=null?n:c.totalEpisodes,u=r.currentEpisode+1;u<=d&&(r.currentEpisode=u,this.dataManager.updateAirtimeEntry(r))}}}isScheduledForToday(t){var a;let s=new Date,i=new Date(s.getFullYear(),s.getMonth(),s.getDate());if(t.recurrence==="daily")return!0;if(t.recurrence==="weekly")return t.dayOfWeek===s.getDay();if(t.recurrence==="monthly")return s.getDate()===((a=t.dayOfMonth)!=null?a:-1);if(t.recurrence==="once"&&t.releaseDate){let l=new Date(t.releaseDate+"T00:00:00");return new Date(l.getFullYear(),l.getMonth(),l.getDate()).getTime()===i.getTime()}return!1}async loadSettings(){var i,a,l,n,r,o,c;let t=await this.loadData();this.settings=Object.assign({},it,(i=t==null?void 0:t.settings)!=null?i:{}),(a=t==null?void 0:t.settings)!=null&&a.listFilters&&(this.settings.listFilters=Object.assign({},it.listFilters,t.settings.listFilters)),this.settings.defaultView==="list"&&(this.settings.defaultView="watchlist"),(l=this.settings.types)!=null&&l.length||(this.settings.types=it.types),(n=this.settings.statuses)!=null&&n.length||(this.settings.statuses=it.statuses),(r=this.settings.reviews)!=null&&r.length||(this.settings.reviews=it.reviews),(o=this.settings.priorities)!=null&&o.length||(this.settings.priorities=it.priorities),(c=this.settings.seasonPalette)!=null&&c.length||(this.settings.seasonPalette=it.seasonPalette);let s=this.settings.statuses.find(d=>d.name==="Plan to watch");if(s&&s.color==="#888780"&&(s.color="#00A9A5"),!this.settings.statuses.find(d=>d.name==="To be released")){let d=this.settings.statuses.findIndex(g=>g.name==="Completed"),u=d>=0?d+1:this.settings.statuses.length;this.settings.statuses.splice(u,0,{name:"To be released",color:"#E8873A"})}}async saveSettings(){var s;let t=(s=await this.loadData())!=null?s:{};await this.saveData({...t,settings:this.settings})}openWidgetPalette(t){let s=[{name:"wl-todo \u2014 Track a title (full)",id:"wl-todo"},{name:"wl-todo mini \u2014 Track a title (compact)",id:"wl-todo:mini"},{name:"wl-stat: watched \u2014 Time watched (mini)",id:"wl-stat:watched"},{name:"wl-stat: completed \u2014 Completed titles (mini)",id:"wl-stat:completed"},{name:"wl-stat: remaining \u2014 Time remaining (mini)",id:"wl-stat:remaining"},{name:"wl-stat: time \u2014 Watched + remaining (mini)",id:"wl-stat:time"},{name:"wl-upcoming: next \u2014 Next upcoming (mini)",id:"wl-upcoming:next"},{name:"wl-stat: time completed full \u2014 Time \xB7 Remaining \xB7 Completed card",id:"wl-stat:time completed full"},{name:"wl-now-next \u2014 Now Watching + Up Next card",id:"wl-now-next"}];new ke(this.app,s,i=>{if(i.id==="wl-todo"||i.id==="wl-todo:mini"){let a=this.dataManager.getTitles();if(a.length===0){new rt.Notice("No titles in your watchlog library yet.");return}let l=i.id==="wl-todo:mini";new dt(this.app,a,n=>{l?t.replaceSelection(`\`\`\`wl-todo
-${n.title}
+Make sure the correct type is selected before searching. If Anime is selected and you search for a movie or TV show (or vice versa), it most likely won't be found, because each type queries its own API. APIs are a convenience, not a requirement \u2014 you can always add a title manually. The "Add from URL" button also relies on an API.`}]},{title:"\u{1F4DA} Reading",items:[{heading:"Books vs Manga",body:"Reading has two independent sub-tabs: Books and Manga. Each keeps its own entries, custom columns, filters, sort order, and saved filter. Set which one opens by default under Settings \u2192 Reading."},{heading:"Statuses",body:'Reading entries use five statuses: Reading, Completed, Plan to Read, To be released, and Dropped. "To be released" is set automatically when an entry has a release date in the future and cannot be chosen manually \u2014 once that date passes, the entry reverts to Plan to Read. Future-dated entries also appear in the shared Upcoming tab.'},{heading:"Progress tracking",body:'Books track pages read out of total pages. Manga track chapters read out of total chapters, plus volumes read out of total volumes. The card progress bar fills based on pages (books) or chapters (manga); entries with no total show "No progress" instead of a percentage.'},{heading:"Notes & favorite quotes",body:"Each entry generates a Markdown note file (under your Reading folder) with frontmatter plus Notes and Quotes sections. Favorite quotes you add from an entry's detail view are saved into that note as quote callouts, so they live alongside your own notes."},{heading:"Covers",body:`Covers are fetched automatically as cards scroll into view \u2014 Google Books for books, Jikan for manga. If a cover is missing or wrong, use "Refresh cover" from a card's \u22EE menu. (Refreshing a book cover needs the Google Books API key.)`},{heading:"Custom columns",body:"Use the \u2699 button in the Reading toolbar to add custom columns (text, number, or select). Columns are per sub-tab, and you can filter and sort by them just like the built-in fields."}]},{title:"\u{1F4C5} Upcoming",items:[{heading:"How Upcoming works",body:"Any title with a release date set in the future is automatically marked as To be released and appears in this tab. This status cannot be set manually; it is determined solely by the release date you enter. If a title has already started airing but only some episodes are out (e.g. an ongoing series), it will not appear here automatically; you'll need to manually add it through the Add button in the Upcoming tab."}]}];for(let i of e){let a=t.createDiv({cls:"wl-qi-section"}),n=a.createDiv({cls:"wl-qi-section-header"}),r=n.createSpan({cls:"wl-qi-chevron",text:"\u25B6"});n.createSpan({cls:"wl-qi-section-label",text:i.title});let o=a.createDiv({cls:"wl-qi-section-body wl-qi-section-body-hidden"});for(let l of i.items){let c=o.createDiv({cls:"wl-qi-banner"});c.createDiv({cls:"wl-qi-banner-heading",text:l.heading}),c.createDiv({cls:"wl-qi-banner-body",text:l.body})}n.addEventListener("click",()=>{let l=!o.hasClass("wl-qi-section-body-hidden");l?o.addClass("wl-qi-section-body-hidden"):o.removeClass("wl-qi-section-body-hidden"),r.textContent=l?"\u25B6":"\u25BC"})}}renderWidgets(t){t.createDiv({cls:"wl-settings-info",text:'Insert widgets into notes using the "WatchLog: Insert widget" command. Configure the keyboard shortcut in Obsidian Settings \u2192 Hotkeys.'});let e=[{name:"wl-todo",desc:'Track a specific title inline \u2014 shows type, status, progress, and a "next episode" checkbox.',syntax:"```wl-todo\nTitle Name\nmini\n```"},{name:"wl-todo (full)",desc:"Track a specific title inline (full card).",syntax:"```wl-todo\nTitle Name\n```"},{name:"wl-stat: completed",desc:"Completed titles count \u2014 compact inline stat card.",syntax:"```wl-stat\ncompleted\n```"},{name:"wl-stat: time",desc:"Time watched & remaining combined (mini).",syntax:"```wl-stat\ntime\n```"},{name:"wl-upcoming: next",desc:"Next upcoming title with name, type badge, release date, and countdown.",syntax:"```wl-upcoming\nnext\n```"},{name:"wl-nowwatching",desc:"Currently pinned title with name, type badge, and progress bar.",syntax:"```wl-nowwatching\n```"},{name:"wl-stat: time completed full",desc:"Full-width triple card: Time Watched \xB7 Time Remaining \xB7 Completed.",syntax:"```wl-stat\ntime completed full\n```"},{name:"wl-now-next",desc:"Full-width double card: Now Watching \xB7 Up Next.",syntax:"```wl-now-next\n```"}];for(let i of e){let a=t.createDiv({cls:"wl-widget-info-section"});a.createDiv({cls:"wl-widget-info-name",text:i.name}),a.createDiv({cls:"wl-widget-info-desc",text:i.desc});let n=a.createDiv({cls:"wl-widget-info-code-row"});n.createEl("code",{cls:"wl-widget-info-code",text:i.syntax});let r=n.createEl("button",{cls:"wl-btn wl-btn-sm",text:"Copy"});r.addEventListener("click",()=>{navigator.clipboard.writeText(i.syntax).then(()=>{r.textContent="Copied!",window.setTimeout(()=>{r.textContent="Copy"},1500)})})}}};Ke.LOCKED_TYPES=["Anime","Movie","TV Show"];var Ge=Ke;var Ai=require("obsidian"),ue=class extends Ai.FuzzySuggestModal{constructor(s,t,e){super(s),this.titles=t,this.onSelect=e,this.setPlaceholder("Search for a title to insert...")}getItems(){return this.titles}getItemText(s){return`${s.title} (${s.type})`}onChooseItem(s){this.onSelect(s)}};var ze=class{constructor(s,t){this.widgetRegistry=new Map;this.widgetMiniRegistry=new Map;this.widgetRerender=new Map;this.plugin=s,this.dataManager=t,this.plugin.registerMarkdownCodeBlockProcessor("watchlog",(e,i,a)=>this.process(e,i,a)),this.plugin.registerMarkdownCodeBlockProcessor("wl-todo",(e,i,a)=>this.processWlTodo(e,i)),this.plugin.registerMarkdownCodeBlockProcessor("wl-stat",(e,i,a)=>this.processWlStat(e,i)),this.plugin.registerMarkdownCodeBlockProcessor("wl-upcoming",(e,i,a)=>this.processWlUpcoming(e,i)),this.plugin.registerMarkdownCodeBlockProcessor("wl-nowwatching",(e,i,a)=>this.processWlNowWatching(e,i)),this.plugin.registerMarkdownCodeBlockProcessor("wl-now-next",(e,i,a)=>this.processWlNowNext(e,i)),this.dataChangedListener=()=>this.onDataChanged(),activeDocument.addEventListener("watchlog-data-changed",this.dataChangedListener),s.register(()=>{activeDocument.removeEventListener("watchlog-data-changed",this.dataChangedListener)})}onDataChanged(){var e;let s=[];for(let[i,a]of this.widgetRegistry){if(!i.isConnected){s.push(i);continue}let n=this.dataManager.getTitle(a);if(!n){s.push(i);continue}((e=this.widgetMiniRegistry.get(i))!=null?e:!1)?this.renderWidgetMinimal(i,n):this.renderWidget(i,n)}for(let i of s)this.widgetRegistry.delete(i),this.widgetMiniRegistry.delete(i);let t=[];for(let[i,a]of this.widgetRerender){if(!i.isConnected){t.push(i);continue}try{a()}catch(n){console.warn("[WL] widget rerender failed:",n)}}for(let i of t)this.widgetRerender.delete(i)}cleanup(){this.widgetRegistry.clear(),this.widgetMiniRegistry.clear(),this.widgetRerender.clear()}process(s,t,e){var r;let i=s.match(/id:\s*(.+)/),a=(r=i==null?void 0:i[1])==null?void 0:r.trim();if(!a){t.createDiv({cls:"wl-widget-error",text:"WatchLog: missing id field."});return}let n=this.dataManager.getTitle(a);if(!n){t.createDiv({cls:"wl-widget-error",text:`WatchLog: title "${a}" not found.`});return}this.widgetRegistry.set(t,a),this.renderWidget(t,n)}renderWidget(s,t){this.renderWidgetFull(s,t)}renderWidgetFull(s,t){var m;s.empty(),s.addClass("wl-widget");let e=s.createDiv({cls:"wl-widget-main-row"}),i=e.createEl("input",{cls:"wl-widget-cb",attr:{type:"checkbox"}});i.title="Personal task marker (does not affect watchlog data)",i.addEventListener("change",()=>{let v=e.querySelector(".wl-widget-title");v&&(v.style.textDecoration=i.checked?"line-through":"",v.style.opacity=i.checked?"0.5":"")}),e.createSpan({cls:"wl-widget-title",text:t.title}),this.sep(e);let a=this.getTagDef(t.type,this.plugin.settings.types),n=this.plugin.settings.coloredTypeBadges,r=e.createSpan({cls:n?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.type});n&&a&&(r.style.backgroundColor=N(t.type,a.color,this.plugin.settings.colorTheme)),this.sep(e);let o=this.getTagDef(t.status,this.plugin.settings.statuses),l=e.createSpan({cls:n?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.status});n&&o&&(l.style.backgroundColor=N(t.status,o.color,this.plugin.settings.colorTheme)),this.sep(e);let c=this.dataManager.getProgress(t),d=e.createDiv({cls:"wl-widget-bar-wrap"});d.createDiv({cls:"wl-progress-bar"}).style.width=`${c}%`,this.sep(e),e.createSpan({cls:"wl-widget-percent",text:`${c}%`}),this.sep(e);let u=(m=t.dateStarted)!=null?m:t.dateAdded,h=t.dateStarted?`since ${this.formatShortDate(u)}`:`added ${this.formatShortDate(u)}`;if(e.createSpan({cls:"wl-widget-date",text:h}),t.type==="Movie"){let v=s.createDiv({cls:"wl-widget-next-row"}),f=t.watchedEpisodes.includes(1),y=v.createEl("input",{cls:"wl-widget-next-cb",attr:{type:"checkbox"}});y.checked=f,y.title="Mark movie as watched",y.addEventListener("change",()=>{this.dataManager.markEpisodeWatched(t.id,1,y.checked).then(()=>{let w=this.dataManager.getTitle(t.id);w&&this.renderWidget(s,w)})}),v.createSpan({cls:"wl-widget-next-label",text:f?"Watched":"Mark as watched"})}else{let v=this.dataManager.getNextUnwatchedEpisode(t),f=s.createDiv({cls:"wl-widget-next-row"});if(v!==null){let y=f.createEl("input",{cls:"wl-widget-next-cb",attr:{type:"checkbox"}});y.checked=!1,y.title=`Mark Episode ${v} as watched`,y.addEventListener("change",()=>{this.dataManager.markEpisodeWatched(t.id,v,!0).then(()=>{let w=this.dataManager.getTitle(t.id);w&&this.renderWidget(s,w)})}),f.createSpan({cls:"wl-widget-next-label",text:`Next up: Ep ${v}`})}else f.createSpan({cls:"wl-widget-next-label",text:"\u2713 All episodes watched"})}}renderWidgetMinimal(s,t){s.empty(),s.addClass("wl-widget-minimal");let e=s.createDiv({cls:"wl-widget-minimal-row"}),i=e.createEl("input",{cls:"wl-widget-cb",attr:{type:"checkbox"}});i.title="Personal task marker (does not affect watchlog data)",i.addEventListener("change",()=>{let u=e.querySelector(".wl-widget-title");u&&(u.style.textDecoration=i.checked?"line-through":"",u.style.opacity=i.checked?"0.5":"")}),e.createSpan({cls:"wl-widget-title",text:t.title}),this.sep(e);let a=this.getTagDef(t.type,this.plugin.settings.types),n=this.plugin.settings.coloredTypeBadges,r=e.createSpan({cls:n?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.type});n&&a&&(r.style.backgroundColor=N(t.type,a.color,this.plugin.settings.colorTheme)),this.sep(e);let o=this.getTagDef(t.status,this.plugin.settings.statuses),l=e.createSpan({cls:n?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.status});n&&o&&(l.style.backgroundColor=N(t.status,o.color,this.plugin.settings.colorTheme)),this.sep(e);let c=this.dataManager.getProgress(t);if(e.createSpan({cls:"wl-widget-percent",text:`${c}%`}),this.sep(e),t.type==="Movie"){let u=t.watchedEpisodes.includes(1),h=e.createEl("input",{cls:"wl-widget-next-cb",attr:{type:"checkbox"}});h.checked=u,h.title="Mark movie as watched",h.addEventListener("change",()=>{this.dataManager.markEpisodeWatched(t.id,1,h.checked).then(()=>{let p=this.dataManager.getTitle(t.id);p&&this.renderWidgetMinimal(s,p)})})}else{let u=this.dataManager.getNextUnwatchedEpisode(t),h=e.createEl("input",{cls:"wl-widget-next-cb",attr:{type:"checkbox"}});u!==null?(h.checked=!1,h.title=`Mark Episode ${u} as watched`,h.addEventListener("change",()=>{this.dataManager.markEpisodeWatched(t.id,u,!0).then(()=>{let p=this.dataManager.getTitle(t.id);p&&this.renderWidgetMinimal(s,p)})})):(h.checked=!0,h.disabled=!0,h.title="All episodes watched")}}sep(s){s.createSpan({cls:"wl-widget-sep",text:"\xB7"})}getTagDef(s,t){return t.find(e=>e.name===s)}formatShortDate(s){try{let t=new Date(s);return`${String(t.getDate()).padStart(2,"0")}/${String(t.getMonth()+1).padStart(2,"0")}/${String(t.getFullYear()).slice(2)}`}catch(t){return s}}processWlTodo(s,t){var r;let e=s.trim().split(`
+`).map(o=>o.trim()).filter(Boolean),i=e.includes("mini"),a=(r=e.find(o=>o!=="mini"))!=null?r:"";if(!a){t.createDiv({cls:"wl-widget-error",text:"wl-todo: missing title name."});return}let n=this.dataManager.getTitles().find(o=>o.title.toLowerCase()===a.toLowerCase());if(!n){t.createDiv({cls:"wl-widget-error",text:`wl-todo: "${a}" not found.`});return}this.widgetRegistry.set(t,n.id),this.widgetMiniRegistry.set(t,i),i?this.renderWidgetMinimal(t,n):this.renderWidget(t,n)}processWlStat(s,t){this.widgetRerender.set(t,()=>this.renderWlStat(s,t)),this.renderWlStat(s,t)}renderWlStat(s,t){let e=s.trim().toLowerCase();if(t.empty(),t.addClass("wl-wstat"),e==="watched"){let i=this.dataManager.getTotalTimeWatched();this.renderStatCard(t,"\u{1F550}",z(i),"watched")}else if(e==="completed"){let i=this.dataManager.getCompletedCount();this.renderStatCard(t,"\u2705",`${i} titles`,"completed")}else if(e==="remaining"){let i=this.dataManager.getTotalTimeRemaining();this.renderStatCard(t,"\u23F3",z(i),"remaining")}else if(e==="time"){let i=this.dataManager.getTotalTimeWatched(),a=this.dataManager.getTotalTimeRemaining();this.renderTimeCardMini(t,i,a)}else if(e==="time full"){let i=this.dataManager.getTotalTimeWatched(),a=this.dataManager.getTotalTimeRemaining();this.renderTimeCardFull(t,i,a)}else if(e==="completed full"){let i=this.dataManager.getCompletedCount();this.renderCompletedCardFull(t,i)}else if(e==="time completed full"){let i=this.dataManager.getTotalTimeWatched(),a=this.dataManager.getTotalTimeRemaining(),n=this.dataManager.getCompletedCount();this.renderTripleStatCard(t,i,a,n)}else t.createDiv({cls:"wl-widget-error",text:`wl-stat: unknown stat "${e}". Use watched, completed, remaining, time, time full, completed full, or time completed full.`})}renderStatCard(s,t,e,i){s.empty();let a=s.createDiv({cls:"wl-stat-card"});a.createSpan({cls:"wl-stat-icon",text:t}),a.createSpan({cls:"wl-stat-value",text:e}),a.createSpan({cls:"wl-stat-label",text:i})}renderTimeCardMini(s,t,e){s.empty();let i=s.createDiv({cls:"wl-stat-card wl-stat-card-time"});i.createSpan({cls:"wl-stat-icon",text:"\u{1F550}"}),i.createSpan({cls:"wl-stat-value",text:z(t)}),i.createSpan({cls:"wl-stat-label",text:"watched"}),i.createSpan({cls:"wl-stat-sep",text:"\xB7"}),i.createSpan({cls:"wl-stat-icon",text:"\u23F3"}),i.createSpan({cls:"wl-stat-value",text:z(e)}),i.createSpan({cls:"wl-stat-label",text:"left"})}renderTimeCardFull(s,t,e){s.empty();let i=s.createDiv({cls:"wl-full-card wl-full-card-time"});i.createDiv({cls:"wl-full-card-header",text:"Time"});let a=i.createDiv({cls:"wl-full-card-time-cols"}),n=(r,o)=>{let l=a.createDiv({cls:"wl-full-card-time-col"});l.createDiv({cls:"wl-full-card-value",text:z(r)}),l.createDiv({cls:"wl-full-card-days",text:this.formatTimeDays(r)}),l.createDiv({cls:"wl-full-card-sub",text:o})};n(t,"watched"),n(e,"remaining")}formatTimeDays(s){let t=Math.floor(s/1440),e=Math.floor(s%1440/60);return t===0?e>0?`${e}h`:"0h":`${t} days ${e}h`}renderCompletedCardFull(s,t){s.empty();let e=s.createDiv({cls:"wl-full-card"});e.createDiv({cls:"wl-full-card-header",text:"Completed"}),e.createDiv({cls:"wl-full-card-value",text:String(t)}),e.createDiv({cls:"wl-full-card-sub",text:"titles completed"})}renderTripleStatCard(s,t,e,i){s.empty(),s.addClass("wl-wstat-triple");let n=s.createDiv({cls:"wl-full-card wl-full-card-triple"}).createDiv({cls:"wl-triple-cols"}),r=(l,c)=>{let d=n.createDiv({cls:"wl-triple-col"});d.createDiv({cls:"wl-full-card-value",text:z(l)}),d.createDiv({cls:"wl-full-card-days",text:this.formatTimeDays(l)}),d.createDiv({cls:"wl-full-card-sub",text:c})};r(t,"watched"),n.createDiv({cls:"wl-vert-sep"}),r(e,"remaining"),n.createDiv({cls:"wl-vert-sep"});let o=n.createDiv({cls:"wl-triple-col"});o.createDiv({cls:"wl-full-card-value",text:String(i)}),o.createDiv({cls:"wl-full-card-sub",text:"completed"})}processWlUpcoming(s,t){this.widgetRerender.set(t,()=>this.renderWlUpcoming(s,t)),this.renderWlUpcoming(s,t)}renderWlUpcoming(s,t){t.empty();let e=s.trim().toLowerCase(),i=e==="next full";if(e!=="next"&&e!=="next full"){t.createDiv({cls:"wl-widget-error",text:'wl-upcoming: only "next" or "next full" is supported.'});return}let a=this.dataManager.getAirtimeEntries(),n=this.dataManager.getTitles(),r=new Map(n.map(y=>[y.id,y])),o=Date.now(),l=null,c=1/0;for(let y of a){let w=r.get(y.titleId);if(!w||y.schedule.recurrence!=="once"||!y.schedule.releaseDate)continue;let b=new Date(y.schedule.releaseDate+"T12:00:00").getTime();if(b>=o&&b=30?` (${Math.round(m/30)} month${Math.round(m/30)!==1?"s":""})`:"",f=m===0?"today":m===1?"tomorrow":`in ${m} days${v}`;d.createSpan({cls:"wl-upcoming-countdown",text:f})}renderUpcomingFull(s,t){s.empty();let e=s.createDiv({cls:"wl-full-card"});if(e.createDiv({cls:"wl-full-card-header",text:"Up Next"}),!t){e.createDiv({cls:"wl-full-card-sub",text:"No upcoming titles."});return}e.createDiv({cls:"wl-full-card-title",text:t.titleName});let i=e.createDiv({cls:"wl-full-card-meta"}),a=this.getTagDef(t.type,this.plugin.settings.types),n=this.plugin.settings.coloredTypeBadges,r=i.createSpan({cls:n?"wl-badge wl-badge-sm":"wl-badge-plain",text:t.type});n&&a&&(r.style.backgroundColor=N(t.type,a.color,this.plugin.settings.colorTheme));let o=t.daysUntil,l=o===0?"today":o===1?"tomorrow":`in ${o} day${o!==1?"s":""}`;i.createSpan({text:l}),i.createSpan({cls:"wl-full-card-sub",text:t.releaseDate})}processWlNowWatching(s,t){this.widgetRerender.set(t,()=>this.renderWlNowWatching(s,t)),this.renderWlNowWatching(s,t)}renderWlNowWatching(s,t){t.empty();let e=s.trim().toLowerCase()==="full";t.addClass("wl-wnowwatching");let i=this.dataManager.getTitles().find(m=>m.pinned),a=this.dataManager.getPinnedGroupId(),n=a?this.dataManager.getGroups().find(m=>m.id===a):null;if(e){this.renderNowWatchingFull(t,i!=null?i:null,n!=null?n:null);return}if(!i&&!n){t.createDiv({cls:"wl-nowwatching-card wl-nowwatching-empty",text:"Pin a title to set it as now watching."});return}let r=t.createDiv({cls:"wl-nowwatching-card"});if(n){r.createSpan({cls:"wl-nowwatching-title",text:n.name});let m=this.dataManager.getTitles(),v=new Map(m.map(D=>[D.id,D])),f=n.titleIds.map(D=>v.get(D)).filter(D=>D!==void 0),y=f.reduce((D,E)=>D+E.watchedEpisodes.length,0),w=f.reduce((D,E)=>D+this.dataManager.getEffectiveTotal(E),0),b=w>0?Math.min(100,Math.round(y/w*100)):0;r.createSpan({cls:"wl-nowwatching-ep",text:`${f.length} title${f.length!==1?"s":""}`});let S=r.createDiv({cls:"wl-nowwatching-bar-wrap"});S.createDiv({cls:"wl-nowwatching-bar"}).style.width=`${b}%`,r.createSpan({cls:"wl-nowwatching-pct",text:`${b}%`});return}let o=i;r.createSpan({cls:"wl-nowwatching-title",text:o.title});let l=this.getTagDef(o.type,this.plugin.settings.types),c=this.plugin.settings.coloredTypeBadges,d=r.createSpan({cls:c?"wl-badge wl-badge-sm":"wl-badge-plain",text:o.type});if(c&&l&&(d.style.backgroundColor=N(o.type,l.color,this.plugin.settings.colorTheme)),o.type!=="Movie"){let m=this.dataManager.getNextUnwatchedEpisode(o);r.createSpan({cls:"wl-nowwatching-ep",text:m!==null?`Ep ${m}`:"\u2713 Completed"})}let u=this.dataManager.getProgress(o),p=r.createDiv({cls:"wl-nowwatching-bar-wrap"}).createDiv({cls:"wl-nowwatching-bar"});p.style.width=`${u}%`,r.createSpan({cls:"wl-nowwatching-pct",text:`${u}%`})}renderNowWatchingFull(s,t,e){s.empty();let i=s.createDiv({cls:"wl-full-card wl-full-card-nw"});if(i.createDiv({cls:"wl-full-card-header",text:"NOW WATCHING"}),!t&&!e){i.createDiv({cls:"wl-full-card-sub",text:"Pin a title to set it as now watching."});return}if(e){i.createDiv({cls:"wl-full-card-title",text:e.name});let u=this.dataManager.getTitles(),h=new Map(u.map(b=>[b.id,b])),p=e.titleIds.map(b=>h.get(b)).filter(b=>b!==void 0),m=p.reduce((b,S)=>b+S.watchedEpisodes.length,0),v=p.reduce((b,S)=>b+this.dataManager.getEffectiveTotal(S),0),f=v>0?Math.min(100,Math.round(m/v*100)):0,y=i.createDiv({cls:"wl-nw-bottom-row"});y.createSpan({cls:"wl-full-card-sub",text:`${p.length} title${p.length!==1?"s":""}`});let w=y.createDiv({cls:"wl-nw-bar-col"});w.createDiv({cls:"wl-nw-pct",text:`${f}%`}),w.createDiv({cls:"wl-full-card-bar-wrap"}).createDiv({cls:"wl-full-card-bar"}).style.width=`${f}%`;return}let a=t;i.createDiv({cls:"wl-full-card-title",text:a.title});let n=this.dataManager.getProgress(a),r=i.createDiv({cls:"wl-nw-bottom-row"}),o=this.getTagDef(a.type,this.plugin.settings.types),l=this.plugin.settings.coloredTypeBadges,c=r.createSpan({cls:l?"wl-badge wl-badge-sm":"wl-badge-plain",text:a.type});l&&o&&(c.style.backgroundColor=N(a.type,o.color,this.plugin.settings.colorTheme));let d=r.createDiv({cls:"wl-nw-bar-col"});d.createDiv({cls:"wl-nw-pct",text:`${n}%`}),d.createDiv({cls:"wl-full-card-bar-wrap"}).createDiv({cls:"wl-full-card-bar"}).style.width=`${n}%`}processWlNowNext(s,t){this.widgetRerender.set(t,()=>this.renderWlNowNext(s,t)),this.renderWlNowNext(s,t)}renderWlNowNext(s,t){t.empty(),t.addClass("wl-wnownext");let e=this.dataManager.getTitles().find(u=>u.pinned),i=this.dataManager.getPinnedGroupId(),a=i?this.dataManager.getGroups().find(u=>u.id===i):null,n=this.dataManager.getAirtimeEntries(),r=this.dataManager.getTitles(),o=new Map(r.map(u=>[u.id,u])),l=Date.now(),c=null,d=1/0;for(let u of n){let h=o.get(u.titleId);if(!h||u.schedule.recurrence!=="once"||!u.schedule.releaseDate)continue;let p=new Date(u.schedule.releaseDate+"T12:00:00").getTime();if(p>=l&&p[m.id,m])),d=e.titleIds.map(m=>c.get(m)).filter(m=>m!==void 0),u=d.reduce((m,v)=>m+v.watchedEpisodes.length,0),h=d.reduce((m,v)=>m+this.dataManager.getEffectiveTotal(v),0),p=h>0?Math.min(100,Math.round(u/h*100)):0;r.createDiv({cls:"wl-full-card-sub",text:`${d.length} title${d.length!==1?"s":""} \xB7 ${p}%`})}else{let l=t;r.createDiv({cls:"wl-full-card-title",text:l.title});let c=this.dataManager.getProgress(l);r.createDiv({cls:"wl-full-card-sub",text:`${c}%`})}n.createDiv({cls:"wl-vert-sep"});let o=n.createDiv({cls:"wl-double-col"});if(o.createDiv({cls:"wl-full-card-header",text:"UPCOMING NEXT"}),!i)o.createDiv({cls:"wl-full-card-sub",text:"No upcoming."});else{o.createDiv({cls:"wl-full-card-title",text:i.titleName});let l=i.daysUntil,c=l===0?"today":l===1?"tomorrow":`in ${l}d`;o.createDiv({cls:"wl-full-card-sub",text:`${i.releaseDate} \xB7 ${c}`})}}};var qe=class extends ot.Plugin{constructor(){super(...arguments);this.settings=dt;this.dataManager=new zt(this);this.readingDataManager=new qt(this);this.apiService=new Qt("","");this.historyManager=new Kt(this);this.importProgress=null;this.notifiedEntries=new Set;this.statusBarEl=null}async onload(){await this.loadSettings(),this.dataManager=new zt(this),await this.dataManager.load(),this.dataManager.startWatchingExternalChanges(),this.readingDataManager=new qt(this),await this.readingDataManager.load(),this.apiService=new Qt(this.settings.omdbApiKey,this.settings.tmdbApiKey,this.settings.googleBooksApiKey),this.historyManager=new Kt(this),await this.historyManager.load(),this.dataManager.setHistoryManager(this.historyManager),this.readingDataManager.setHistoryManager(this.historyManager),this.posterService=new ve(this.dataManager,()=>this.settings),await this.dataManager.ensureFolders(),this.startAirtimeScheduler(),this.registerView(oe,e=>new kt(e,this,this.dataManager)),this.widgetRenderer=new ze(this,this.dataManager),this.addRibbonIcon("tv","Watchlog",()=>{this.activateView()}),this.addCommand({id:"open-panel",name:"Open panel",callback:()=>void this.activateView()}),this.addCommand({id:"add-title",name:"Add title",callback:()=>{new ht(this.app,this,this.dataManager,()=>{this.activateView()}).open()}}),this.addCommand({id:"insert-widget",name:"Insert widget",editorCallback:(e,i)=>{this.openWidgetPalette(e)}}),this.addCommand({id:"search-title",name:"Search title",callback:()=>{let e=this.dataManager.getTitles();new ue(this.app,e,i=>{this.activateView().then(()=>{new ot.Notice(`"${i.title}" \u2014 ${i.type} \xB7 ${i.status}`)})}).open()}}),this.addSettingTab(new Ge(this.app,this)),this.setupStatusBar();let t=()=>this.updateStatusBar();activeDocument.addEventListener("watchlog-data-changed",t),this.register(()=>activeDocument.removeEventListener("watchlog-data-changed",t))}onunload(){var t,e,i;this.dataManager.flushPendingSaveSync(),this.dataManager.flushPosterSaveSync(),this.dataManager.flushQueuedSaveSync(),(t=this.readingDataManager)==null||t.flushCoverSave(),(e=this.posterService)==null||e.destroy(),(i=this.widgetRenderer)==null||i.cleanup()}startAirtimeScheduler(){this.checkAirtimeNotifications(),this.registerInterval(window.setInterval(()=>{this.checkAirtimeNotifications()},6e4))}markAirtimeHandled(t){this.notifiedEntries.add(t)}checkAirtimeNotifications(){var n,r,o,l;let t=new Date,e=`${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`,i=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,a=this.dataManager.getAirtimeEntries();for(let c of a){if(!c.schedule.releaseTime||c.schedule.releaseTime!==e||!this.isScheduledForToday(c.schedule))continue;let d=`${c.id}-${i}`;if(this.notifiedEntries.has(d))continue;this.notifiedEntries.add(d);let u,h;if(c.source==="reading"){let m=((n=c.readingKind)!=null?n:"book")==="book"?this.readingDataManager.getBook(c.titleId):this.readingDataManager.getManga(c.titleId);if(!m)continue;u=m.title,h=(o=(r=c.totalEpisodes)!=null?r:m.totalChapters)!=null?o:0}else{let p=this.dataManager.getTitle(c.titleId);if(!p)continue;u=p.title,h=(l=c.totalEpisodes)!=null?l:p.totalEpisodes}if(new ot.Notice(u,8e3),h>1&&c.currentEpisode!==void 0){let p=c.currentEpisode+1;p<=h&&(c.currentEpisode=p,this.dataManager.updateAirtimeEntry(c))}}this.updateStatusBar()}setupStatusBar(){ot.Platform.isMobile||(this.statusBarEl=this.addStatusBarItem(),this.statusBarEl.addClass("wl-statusbar-upcoming"),this.statusBarEl.style.cursor="pointer",this.statusBarEl.setAttribute("aria-label","WatchLog \u2014 Upcoming due"),this.statusBarEl.addEventListener("click",()=>void this.activateView("upcoming")),this.updateStatusBar())}updateStatusBar(){let t=this.statusBarEl;if(!t)return;if(!this.settings.showUpcomingStatusBar){t.hide();return}let e=vt.getAiredDueCount(this.dataManager,this.readingDataManager)+vt.getMaybeDueCount(this.dataManager);if(e<=0){t.hide();return}t.empty(),t.show(),t.style.color="var(--color-orange)";let i=t.createSpan({cls:"wl-statusbar-icon"});(0,ot.setIcon)(i,"tv"),t.createSpan({cls:"wl-statusbar-text",text:`${e} due`})}isScheduledForToday(t){var a;let e=new Date,i=new Date(e.getFullYear(),e.getMonth(),e.getDate());if(t.recurrence==="daily")return!0;if(t.recurrence==="weekly")return t.dayOfWeek===e.getDay();if(t.recurrence==="monthly")return e.getDate()===((a=t.dayOfMonth)!=null?a:-1);if(t.recurrence==="once"&&t.releaseDate){let n=new Date(t.releaseDate+"T00:00:00");return new Date(n.getFullYear(),n.getMonth(),n.getDate()).getTime()===i.getTime()}return!1}async loadSettings(){var i,a,n,r,o,l,c;let t=await this.loadData();this.settings=Object.assign({},dt,(i=t==null?void 0:t.settings)!=null?i:{}),(a=t==null?void 0:t.settings)!=null&&a.listFilters&&(this.settings.listFilters=Object.assign({},dt.listFilters,t.settings.listFilters)),this.settings.animeApiSource===void 0&&(this.settings.animeApiSource="jikan"),this.settings.typeApiMapping===void 0&&(this.settings.typeApiMapping={}),this.settings.readingTypeColors?(this.settings.readingTypeColors.manga||(this.settings.readingTypeColors.manga="#D4537E"),this.settings.readingTypeColors.book||(this.settings.readingTypeColors.book="#D85A30")):this.settings.readingTypeColors={manga:"#D4537E",book:"#D85A30"},this.settings.defaultWatchlistView!=="list"&&this.settings.defaultWatchlistView!=="cards"&&(this.settings.defaultWatchlistView="cards"),this.settings.showUpcomingStatusBar===void 0&&(this.settings.showUpcomingStatusBar=!0),(n=this.settings.types)!=null&&n.length||(this.settings.types=dt.types),(r=this.settings.statuses)!=null&&r.length||(this.settings.statuses=dt.statuses),(o=this.settings.reviews)!=null&&o.length||(this.settings.reviews=dt.reviews),(l=this.settings.priorities)!=null&&l.length||(this.settings.priorities=dt.priorities),(c=this.settings.seasonPalette)!=null&&c.length||(this.settings.seasonPalette=dt.seasonPalette);let e=this.settings.statuses.find(d=>d.name==="Plan to watch");if(e&&e.color==="#888780"&&(e.color="#00A9A5"),!this.settings.statuses.find(d=>d.name==="To be released")){let d=this.settings.statuses.findIndex(h=>h.name==="Completed"),u=d>=0?d+1:this.settings.statuses.length;this.settings.statuses.splice(u,0,{name:"To be released",color:"#E8873A"})}}async saveSettings(){await this.dataManager.saveSettings(this.settings)}openWidgetPalette(t){let e=[{name:"wl-todo \u2014 Track a title (full)",id:"wl-todo"},{name:"wl-todo mini \u2014 Track a title (compact)",id:"wl-todo:mini"},{name:"wl-stat: watched \u2014 Time watched (mini)",id:"wl-stat:watched"},{name:"wl-stat: completed \u2014 Completed titles (mini)",id:"wl-stat:completed"},{name:"wl-stat: remaining \u2014 Time remaining (mini)",id:"wl-stat:remaining"},{name:"wl-stat: time \u2014 Watched + remaining (mini)",id:"wl-stat:time"},{name:"wl-upcoming: next \u2014 Next upcoming (mini)",id:"wl-upcoming:next"},{name:"wl-stat: time completed full \u2014 Time \xB7 Remaining \xB7 Completed card",id:"wl-stat:time completed full"},{name:"wl-now-next \u2014 Now Watching + Up Next card",id:"wl-now-next"}];new Fs(this.app,e,i=>{if(i.id==="wl-todo"||i.id==="wl-todo:mini"){let a=this.dataManager.getTitles();if(a.length===0){new ot.Notice("No titles in your watchlog library yet.");return}let n=i.id==="wl-todo:mini";new ue(this.app,a,r=>{n?t.replaceSelection(`\`\`\`wl-todo
+${r.title}
mini
\`\`\``):t.replaceSelection(`\`\`\`wl-todo
-${n.title}
-\`\`\``)}).open()}else i.id==="wl-stat:watched"?t.replaceSelection("```wl-stat\nwatched\n```"):i.id==="wl-stat:completed"?t.replaceSelection("```wl-stat\ncompleted\n```"):i.id==="wl-stat:remaining"?t.replaceSelection("```wl-stat\nremaining\n```"):i.id==="wl-stat:time"?t.replaceSelection("```wl-stat\ntime\n```"):i.id==="wl-upcoming:next"?t.replaceSelection("```wl-upcoming\nnext\n```"):i.id==="wl-stat:time completed full"?t.replaceSelection("```wl-stat\ntime completed full\n```"):i.id==="wl-now-next"&&t.replaceSelection("```wl-now-next\n```")}).open()}async activateView(){let{workspace:t}=this.app,s=t.getLeavesOfType(At);if(s.length>0){t.revealLeaf(s[0]);return}let i=t.getLeaf("tab");await i.setViewState({type:At,active:!0}),t.revealLeaf(i)}},ke=class extends rt.FuzzySuggestModal{constructor(e,t,s){super(e),this.items=t,this.onSelect=s,this.setPlaceholder("Select a widget to insert...")}getItems(){return this.items}getItemText(e){return e.name}onChooseItem(e){this.onSelect(e)}};
+${r.title}
+\`\`\``)}).open()}else i.id==="wl-stat:watched"?t.replaceSelection("```wl-stat\nwatched\n```"):i.id==="wl-stat:completed"?t.replaceSelection("```wl-stat\ncompleted\n```"):i.id==="wl-stat:remaining"?t.replaceSelection("```wl-stat\nremaining\n```"):i.id==="wl-stat:time"?t.replaceSelection("```wl-stat\ntime\n```"):i.id==="wl-upcoming:next"?t.replaceSelection("```wl-upcoming\nnext\n```"):i.id==="wl-stat:time completed full"?t.replaceSelection("```wl-stat\ntime completed full\n```"):i.id==="wl-now-next"&&t.replaceSelection("```wl-now-next\n```")}).open()}async activateView(t){let{workspace:e}=this.app,i=e.getLeavesOfType(oe);if(i.length>0){let n=i[0];e.revealLeaf(n),t&&n.view instanceof kt&&n.view.setActiveTab(t);return}let a=e.getLeaf("tab");await a.setViewState({type:oe,active:!0}),e.revealLeaf(a),t&&a.view instanceof kt&&a.view.setActiveTab(t)}},Fs=class extends ot.FuzzySuggestModal{constructor(s,t,e){super(s),this.items=t,this.onSelect=e,this.setPlaceholder("Select a widget to insert...")}getItems(){return this.items}getItemText(s){return s.name}onChooseItem(s){this.onSelect(s)}};
diff --git a/manifest.json b/manifest.json
index 11fa745..b78d5eb 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,10 +1,10 @@
-{
- "id": "watchlog",
- "name": "WatchLog",
- "version": "1.1.0",
- "minAppVersion": "1.7.2",
- "description": "Track your anime, movies, and TV shows; with episode tracking, progress stats, upcoming release alerts, and embeddable widgets.",
- "author": "BogdanS",
- "authorUrl": "",
- "isDesktopOnly": false
-}
+{
+ "id": "watchlog",
+ "name": "WatchLog",
+ "version": "2.0.0",
+ "minAppVersion": "1.7.2",
+ "description": "Track your anime, movies, books, manga and TV shows; with episode tracking, progress stats, upcoming release alerts, and embeddable widgets.",
+ "author": "BogdanS",
+ "authorUrl": "",
+ "isDesktopOnly": false
+}
diff --git a/package-lock.json b/package-lock.json
index 38516c2..9c4b5a3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,5174 +1,5174 @@
-{
- "name": "watchlog-plugin",
- "version": "0.9.8",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "watchlog-plugin",
- "version": "0.9.8",
- "license": "0-BSD",
- "dependencies": {
- "fuse.js": "^7.3.0",
- "obsidian": "latest"
- },
- "devDependencies": {
- "@eslint/js": "9.30.1",
- "@types/node": "^16.11.6",
- "esbuild": "0.25.5",
- "eslint-plugin-obsidianmd": "0.1.9",
- "globals": "14.0.0",
- "jiti": "2.6.1",
- "tslib": "2.4.0",
- "typescript": "^5.8.3",
- "typescript-eslint": "8.35.1"
- }
- },
- "node_modules/@codemirror/state": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz",
- "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@marijn/find-cluster-break": "^1.0.0"
- }
- },
- "node_modules/@codemirror/view": {
- "version": "6.38.6",
- "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz",
- "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@codemirror/state": "^6.5.0",
- "crelt": "^1.0.6",
- "style-mod": "^4.1.0",
- "w3c-keyname": "^2.2.4"
- }
- },
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
- "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
- "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
- "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
- "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
- "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
- "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
- "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
- "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
- "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
- "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
- "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
- "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
- "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
- "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
- "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
- "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
- "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
- "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
- "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
- "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
- "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
- "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
- "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
- "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
- "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@eslint-community/eslint-utils": {
- "version": "4.9.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
- "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-visitor-keys": "^3.4.3"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- },
- "peerDependencies": {
- "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
- }
- },
- "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint-community/regexpp": {
- "version": "4.12.2",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
- "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
- }
- },
- "node_modules/@eslint/config-array": {
- "version": "0.21.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
- "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/object-schema": "^2.1.7",
- "debug": "^4.3.1",
- "minimatch": "^3.1.2"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/config-helpers": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
- "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^0.17.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/core": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
- "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@types/json-schema": "^7.0.15"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
- "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/js": {
- "version": "9.30.1",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz",
- "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- }
- },
- "node_modules/@eslint/json": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/@eslint/json/-/json-0.14.0.tgz",
- "integrity": "sha512-rvR/EZtvUG3p9uqrSmcDJPYSH7atmWr0RnFWN6m917MAPx82+zQgPUmDu0whPFG6XTyM0vB/hR6c1Q63OaYtCQ==",
- "dev": true,
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@eslint/core": "^0.17.0",
- "@eslint/plugin-kit": "^0.4.1",
- "@humanwhocodes/momoa": "^3.3.10",
- "natural-compare": "^1.4.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/object-schema": {
- "version": "2.1.7",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
- "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/plugin-kit": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
- "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^0.17.0",
- "levn": "^0.4.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@humanfs/core": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
- "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/node": {
- "version": "0.16.7",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
- "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@humanfs/core": "^0.19.1",
- "@humanwhocodes/retry": "^0.4.0"
- },
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanwhocodes/module-importer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=12.22"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@humanwhocodes/momoa": {
- "version": "3.3.10",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.10.tgz",
- "integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==",
- "dev": true,
- "license": "Apache-2.0",
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@humanwhocodes/retry": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
- "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@marijn/find-cluster-break": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
- "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/@microsoft/eslint-plugin-sdl": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@microsoft/eslint-plugin-sdl/-/eslint-plugin-sdl-1.1.0.tgz",
- "integrity": "sha512-dxdNHOemLnBhfY3eByrujX9KyLigcNtW8sU+axzWv5nLGcsSBeKW2YYyTpfPo1hV8YPOmIGnfA4fZHyKVtWqBQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-plugin-n": "17.10.3",
- "eslint-plugin-react": "7.37.3",
- "eslint-plugin-security": "1.4.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "eslint": "^9"
- }
- },
- "node_modules/@microsoft/eslint-plugin-sdl/node_modules/eslint-plugin-security": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz",
- "integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "safe-regex": "^1.1.0"
- }
- },
- "node_modules/@microsoft/eslint-plugin-sdl/node_modules/safe-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ret": "~0.1.10"
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@pkgr/core": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
- "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/unts"
- }
- },
- "node_modules/@rtsao/scc": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
- "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/codemirror": {
- "version": "5.60.8",
- "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz",
- "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
- "license": "MIT",
- "dependencies": {
- "@types/tern": "*"
- }
- },
- "node_modules/@types/eslint": {
- "version": "8.56.2",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz",
- "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
- "node_modules/@types/estree": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
- "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
- "license": "MIT"
- },
- "node_modules/@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/json5": {
- "version": "0.0.29",
- "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
- "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/node": {
- "version": "16.18.126",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz",
- "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/tern": {
- "version": "0.23.9",
- "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
- "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
- "license": "MIT",
- "dependencies": {
- "@types/estree": "*"
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
- "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.35.1",
- "@typescript-eslint/type-utils": "8.35.1",
- "@typescript-eslint/utils": "8.35.1",
- "@typescript-eslint/visitor-keys": "8.35.1",
- "graphemer": "^1.4.0",
- "ignore": "^7.0.0",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.35.1",
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
- "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/@typescript-eslint/parser": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
- "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/scope-manager": "8.35.1",
- "@typescript-eslint/types": "8.35.1",
- "@typescript-eslint/typescript-estree": "8.35.1",
- "@typescript-eslint/visitor-keys": "8.35.1",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
- "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.35.1",
- "@typescript-eslint/types": "^8.35.1",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
- "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.35.1",
- "@typescript-eslint/visitor-keys": "8.35.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
- "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
- "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/typescript-estree": "8.35.1",
- "@typescript-eslint/utils": "8.35.1",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
- "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
- "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.35.1",
- "@typescript-eslint/tsconfig-utils": "8.35.1",
- "@typescript-eslint/types": "8.35.1",
- "@typescript-eslint/visitor-keys": "8.35.1",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.7.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
- "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.35.1",
- "@typescript-eslint/types": "8.35.1",
- "@typescript-eslint/typescript-estree": "8.35.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
- "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.35.1",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
- }
- },
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true,
- "license": "Python-2.0"
- },
- "node_modules/array-buffer-byte-length": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
- "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "is-array-buffer": "^3.0.5"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array-includes": {
- "version": "3.1.9",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
- "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.24.0",
- "es-object-atoms": "^1.1.1",
- "get-intrinsic": "^1.3.0",
- "is-string": "^1.1.1",
- "math-intrinsics": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.findlast": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
- "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "es-shim-unscopables": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.findlastindex": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
- "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.9",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "es-shim-unscopables": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.flat": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
- "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-shim-unscopables": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.flatmap": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
- "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-shim-unscopables": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.tosorted": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
- "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.3",
- "es-errors": "^1.3.0",
- "es-shim-unscopables": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/arraybuffer.prototype.slice": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
- "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-buffer-byte-length": "^1.0.1",
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6",
- "is-array-buffer": "^3.0.4"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/async-function": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
- "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/available-typed-arrays": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
- "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "possible-typed-array-names": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/call-bind": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
- "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.0",
- "es-define-property": "^1.0.0",
- "get-intrinsic": "^1.2.4",
- "set-function-length": "^1.2.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/call-bind-apply-helpers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/call-bound": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
- "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "get-intrinsic": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/crelt": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
- "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/data-view-buffer": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
- "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/data-view-byte-length": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
- "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/inspect-js"
- }
- },
- "node_modules/data-view-byte-offset": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
- "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/deep-is": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/define-data-property": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/define-properties": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
- "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.0.1",
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/doctrine": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "esutils": "^2.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/dunder-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "es-errors": "^1.3.0",
- "gopd": "^1.2.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/empathic": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz",
- "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/enhanced-resolve": {
- "version": "5.18.3",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
- "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/es-abstract": {
- "version": "1.24.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
- "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-buffer-byte-length": "^1.0.2",
- "arraybuffer.prototype.slice": "^1.0.4",
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "data-view-buffer": "^1.0.2",
- "data-view-byte-length": "^1.0.2",
- "data-view-byte-offset": "^1.0.1",
- "es-define-property": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "es-set-tostringtag": "^2.1.0",
- "es-to-primitive": "^1.3.0",
- "function.prototype.name": "^1.1.8",
- "get-intrinsic": "^1.3.0",
- "get-proto": "^1.0.1",
- "get-symbol-description": "^1.1.0",
- "globalthis": "^1.0.4",
- "gopd": "^1.2.0",
- "has-property-descriptors": "^1.0.2",
- "has-proto": "^1.2.0",
- "has-symbols": "^1.1.0",
- "hasown": "^2.0.2",
- "internal-slot": "^1.1.0",
- "is-array-buffer": "^3.0.5",
- "is-callable": "^1.2.7",
- "is-data-view": "^1.0.2",
- "is-negative-zero": "^2.0.3",
- "is-regex": "^1.2.1",
- "is-set": "^2.0.3",
- "is-shared-array-buffer": "^1.0.4",
- "is-string": "^1.1.1",
- "is-typed-array": "^1.1.15",
- "is-weakref": "^1.1.1",
- "math-intrinsics": "^1.1.0",
- "object-inspect": "^1.13.4",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.7",
- "own-keys": "^1.0.1",
- "regexp.prototype.flags": "^1.5.4",
- "safe-array-concat": "^1.1.3",
- "safe-push-apply": "^1.0.0",
- "safe-regex-test": "^1.1.0",
- "set-proto": "^1.0.0",
- "stop-iteration-iterator": "^1.1.0",
- "string.prototype.trim": "^1.2.10",
- "string.prototype.trimend": "^1.0.9",
- "string.prototype.trimstart": "^1.0.8",
- "typed-array-buffer": "^1.0.3",
- "typed-array-byte-length": "^1.0.3",
- "typed-array-byte-offset": "^1.0.4",
- "typed-array-length": "^1.0.7",
- "unbox-primitive": "^1.1.0",
- "which-typed-array": "^1.1.19"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/es-define-property": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-iterator-helpers": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
- "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.6",
- "es-errors": "^1.3.0",
- "es-set-tostringtag": "^2.0.3",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.6",
- "globalthis": "^1.0.4",
- "gopd": "^1.2.0",
- "has-property-descriptors": "^1.0.2",
- "has-proto": "^1.2.0",
- "has-symbols": "^1.1.0",
- "internal-slot": "^1.1.0",
- "iterator.prototype": "^1.1.4",
- "safe-array-concat": "^1.1.3"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-object-atoms": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-set-tostringtag": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
- "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-shim-unscopables": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
- "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-to-primitive": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
- "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-callable": "^1.2.7",
- "is-date-object": "^1.0.5",
- "is-symbol": "^1.0.4"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/esbuild": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
- "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.5",
- "@esbuild/android-arm": "0.25.5",
- "@esbuild/android-arm64": "0.25.5",
- "@esbuild/android-x64": "0.25.5",
- "@esbuild/darwin-arm64": "0.25.5",
- "@esbuild/darwin-x64": "0.25.5",
- "@esbuild/freebsd-arm64": "0.25.5",
- "@esbuild/freebsd-x64": "0.25.5",
- "@esbuild/linux-arm": "0.25.5",
- "@esbuild/linux-arm64": "0.25.5",
- "@esbuild/linux-ia32": "0.25.5",
- "@esbuild/linux-loong64": "0.25.5",
- "@esbuild/linux-mips64el": "0.25.5",
- "@esbuild/linux-ppc64": "0.25.5",
- "@esbuild/linux-riscv64": "0.25.5",
- "@esbuild/linux-s390x": "0.25.5",
- "@esbuild/linux-x64": "0.25.5",
- "@esbuild/netbsd-arm64": "0.25.5",
- "@esbuild/netbsd-x64": "0.25.5",
- "@esbuild/openbsd-arm64": "0.25.5",
- "@esbuild/openbsd-x64": "0.25.5",
- "@esbuild/sunos-x64": "0.25.5",
- "@esbuild/win32-arm64": "0.25.5",
- "@esbuild/win32-ia32": "0.25.5",
- "@esbuild/win32-x64": "0.25.5"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint": {
- "version": "9.39.1",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
- "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.8.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.1",
- "@eslint/config-helpers": "^0.4.2",
- "@eslint/core": "^0.17.0",
- "@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.39.1",
- "@eslint/plugin-kit": "^0.4.1",
- "@humanfs/node": "^0.16.6",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.2",
- "@types/estree": "^1.0.6",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.6",
- "debug": "^4.3.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.4.0",
- "eslint-visitor-keys": "^4.2.1",
- "espree": "^10.4.0",
- "esquery": "^1.5.0",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^8.0.0",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- },
- "peerDependencies": {
- "jiti": "*"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-compat-utils": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
- "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=12"
- },
- "peerDependencies": {
- "eslint": ">=6.0.0"
- }
- },
- "node_modules/eslint-compat-utils/node_modules/semver": {
- "version": "7.7.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-import-resolver-node": {
- "version": "0.3.9",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
- "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^3.2.7",
- "is-core-module": "^2.13.0",
- "resolve": "^1.22.4"
- }
- },
- "node_modules/eslint-import-resolver-node/node_modules/debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.1"
- }
- },
- "node_modules/eslint-module-utils": {
- "version": "2.12.1",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
- "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^3.2.7"
- },
- "engines": {
- "node": ">=4"
- },
- "peerDependenciesMeta": {
- "eslint": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-module-utils/node_modules/debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.1"
- }
- },
- "node_modules/eslint-plugin-depend": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-depend/-/eslint-plugin-depend-1.3.1.tgz",
- "integrity": "sha512-1uo2rFAr9vzNrCYdp7IBZRB54LiyVxfaIso0R6/QV3t6Dax6DTbW/EV2Hktf0f4UtmGHK8UyzJWI382pwW04jw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "empathic": "^2.0.0",
- "module-replacements": "^2.8.0",
- "semver": "^7.6.3"
- }
- },
- "node_modules/eslint-plugin-depend/node_modules/semver": {
- "version": "7.7.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-plugin-es-x": {
- "version": "7.8.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
- "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
- "dev": true,
- "funding": [
- "https://github.com/sponsors/ota-meshi",
- "https://opencollective.com/eslint"
- ],
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.1.2",
- "@eslint-community/regexpp": "^4.11.0",
- "eslint-compat-utils": "^0.5.1"
- },
- "engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "peerDependencies": {
- "eslint": ">=8"
- }
- },
- "node_modules/eslint-plugin-import": {
- "version": "2.32.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
- "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@rtsao/scc": "^1.1.0",
- "array-includes": "^3.1.9",
- "array.prototype.findlastindex": "^1.2.6",
- "array.prototype.flat": "^1.3.3",
- "array.prototype.flatmap": "^1.3.3",
- "debug": "^3.2.7",
- "doctrine": "^2.1.0",
- "eslint-import-resolver-node": "^0.3.9",
- "eslint-module-utils": "^2.12.1",
- "hasown": "^2.0.2",
- "is-core-module": "^2.16.1",
- "is-glob": "^4.0.3",
- "minimatch": "^3.1.2",
- "object.fromentries": "^2.0.8",
- "object.groupby": "^1.0.3",
- "object.values": "^1.2.1",
- "semver": "^6.3.1",
- "string.prototype.trimend": "^1.0.9",
- "tsconfig-paths": "^3.15.0"
- },
- "engines": {
- "node": ">=4"
- },
- "peerDependencies": {
- "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
- }
- },
- "node_modules/eslint-plugin-import/node_modules/debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.1"
- }
- },
- "node_modules/eslint-plugin-json-schema-validator": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-json-schema-validator/-/eslint-plugin-json-schema-validator-5.1.0.tgz",
- "integrity": "sha512-ZmVyxRIjm58oqe2kTuy90PpmZPrrKvOjRPXKzq8WCgRgAkidCgm5X8domL2KSfadZ3QFAmifMgGTcVNhZ5ez2g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.3.0",
- "ajv": "^8.0.0",
- "debug": "^4.3.1",
- "eslint-compat-utils": "^0.5.0",
- "json-schema-migrate": "^2.0.0",
- "jsonc-eslint-parser": "^2.0.0",
- "minimatch": "^8.0.0",
- "synckit": "^0.9.0",
- "toml-eslint-parser": "^0.9.0",
- "tunnel-agent": "^0.6.0",
- "yaml-eslint-parser": "^1.0.0"
- },
- "engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ota-meshi"
- },
- "peerDependencies": {
- "eslint": ">=6.0.0"
- }
- },
- "node_modules/eslint-plugin-json-schema-validator/node_modules/ajv": {
- "version": "8.17.1",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
- "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.3",
- "fast-uri": "^3.0.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/eslint-plugin-json-schema-validator/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/eslint-plugin-json-schema-validator/node_modules/json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/eslint-plugin-json-schema-validator/node_modules/minimatch": {
- "version": "8.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz",
- "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/eslint-plugin-n": {
- "version": "17.10.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.10.3.tgz",
- "integrity": "sha512-ySZBfKe49nQZWR1yFaA0v/GsH6Fgp8ah6XV0WDz6CN8WO0ek4McMzb7A2xnf4DCYV43frjCygvb9f/wx7UUxRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "enhanced-resolve": "^5.17.0",
- "eslint-plugin-es-x": "^7.5.0",
- "get-tsconfig": "^4.7.0",
- "globals": "^15.8.0",
- "ignore": "^5.2.4",
- "minimatch": "^9.0.5",
- "semver": "^7.5.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- },
- "peerDependencies": {
- "eslint": ">=8.23.0"
- }
- },
- "node_modules/eslint-plugin-n/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/eslint-plugin-n/node_modules/globals": {
- "version": "15.15.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
- "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint-plugin-n/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/eslint-plugin-n/node_modules/semver": {
- "version": "7.7.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-plugin-obsidianmd": {
- "version": "0.1.9",
- "resolved": "https://registry.npmjs.org/eslint-plugin-obsidianmd/-/eslint-plugin-obsidianmd-0.1.9.tgz",
- "integrity": "sha512-/gyo5vky3Y7re4BtT/8MQbHU5Wes4o6VRqas3YmXE7aTCnMsdV0kfzV1GDXJN9Hrsc9UQPoeKUMiapKL0aGE4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@microsoft/eslint-plugin-sdl": "^1.1.0",
- "@types/eslint": "8.56.2",
- "@types/node": "20.12.12",
- "eslint": ">=9.0.0 <10.0.0",
- "eslint-plugin-depend": "1.3.1",
- "eslint-plugin-import": "^2.31.0",
- "eslint-plugin-json-schema-validator": "5.1.0",
- "eslint-plugin-security": "2.1.1",
- "globals": "14.0.0",
- "obsidian": "1.8.7",
- "typescript": "5.4.5"
- },
- "bin": {
- "eslint-plugin-obsidian": "dist/lib/index.js"
- },
- "engines": {
- "node": ">= 18"
- },
- "peerDependencies": {
- "@eslint/js": "^9.30.1",
- "@eslint/json": "0.14.0",
- "eslint": ">=9.0.0 <10.0.0",
- "obsidian": "1.8.7",
- "typescript-eslint": "^8.35.1"
- }
- },
- "node_modules/eslint-plugin-obsidianmd/node_modules/@types/node": {
- "version": "20.12.12",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
- "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "undici-types": "~5.26.4"
- }
- },
- "node_modules/eslint-plugin-obsidianmd/node_modules/obsidian": {
- "version": "1.8.7",
- "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.8.7.tgz",
- "integrity": "sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/codemirror": "5.60.8",
- "moment": "2.29.4"
- },
- "peerDependencies": {
- "@codemirror/state": "^6.0.0",
- "@codemirror/view": "^6.0.0"
- }
- },
- "node_modules/eslint-plugin-obsidianmd/node_modules/typescript": {
- "version": "5.4.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
- "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/eslint-plugin-react": {
- "version": "7.37.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz",
- "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-includes": "^3.1.8",
- "array.prototype.findlast": "^1.2.5",
- "array.prototype.flatmap": "^1.3.3",
- "array.prototype.tosorted": "^1.1.4",
- "doctrine": "^2.1.0",
- "es-iterator-helpers": "^1.2.1",
- "estraverse": "^5.3.0",
- "hasown": "^2.0.2",
- "jsx-ast-utils": "^2.4.1 || ^3.0.0",
- "minimatch": "^3.1.2",
- "object.entries": "^1.1.8",
- "object.fromentries": "^2.0.8",
- "object.values": "^1.2.1",
- "prop-types": "^15.8.1",
- "resolve": "^2.0.0-next.5",
- "semver": "^6.3.1",
- "string.prototype.matchall": "^4.0.12",
- "string.prototype.repeat": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- },
- "peerDependencies": {
- "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
- }
- },
- "node_modules/eslint-plugin-react/node_modules/resolve": {
- "version": "2.0.0-next.5",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
- "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/eslint-plugin-security": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-2.1.1.tgz",
- "integrity": "sha512-7cspIGj7WTfR3EhaILzAPcfCo5R9FbeWvbgsPYWivSurTBKW88VQxtP3c4aWMG9Hz/GfJlJVdXEJ3c8LqS+u2w==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "safe-regex": "^2.1.1"
- }
- },
- "node_modules/eslint-scope": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
- "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint/node_modules/@eslint/js": {
- "version": "9.39.1",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
- "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- }
- },
- "node_modules/espree": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
- "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.15.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "estraverse": "^5.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fast-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
- "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fastify"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/fastify"
- }
- ],
- "license": "BSD-3-Clause"
- },
- "node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
- "node_modules/file-entry-cache": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
- "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "flat-cache": "^4.0.0"
- },
- "engines": {
- "node": ">=16.0.0"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/flat-cache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
- "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.4"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/flatted": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
- "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/for-each": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
- "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-callable": "^1.2.7"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/function.prototype.name": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
- "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "functions-have-names": "^1.2.3",
- "hasown": "^2.0.2",
- "is-callable": "^1.2.7"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/functions-have-names": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
- "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/fuse.js": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.3.0.tgz",
- "integrity": "sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/krisk"
- }
- },
- "node_modules/generator-function": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
- "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/get-intrinsic": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
- "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "es-define-property": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "function-bind": "^1.1.2",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "has-symbols": "^1.1.0",
- "hasown": "^2.0.2",
- "math-intrinsics": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/get-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
- "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/get-symbol-description": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
- "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/get-tsconfig": {
- "version": "4.13.0",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
- "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "resolve-pkg-maps": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
- }
- },
- "node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/globalthis": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
- "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-properties": "^1.2.1",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/gopd": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/has-bigints": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
- "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/has-property-descriptors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
- "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-proto": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
- "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-symbols": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-tostringtag": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
- "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-symbols": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/ignore": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.19"
- }
- },
- "node_modules/internal-slot": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
- "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "hasown": "^2.0.2",
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/is-array-buffer": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
- "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-async-function": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
- "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "async-function": "^1.0.0",
- "call-bound": "^1.0.3",
- "get-proto": "^1.0.1",
- "has-tostringtag": "^1.0.2",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-bigint": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
- "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-bigints": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-boolean-object": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
- "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-callable": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
- "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-core-module": {
- "version": "2.16.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
- "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-data-view": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
- "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "get-intrinsic": "^1.2.6",
- "is-typed-array": "^1.1.13"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-date-object": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
- "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-finalizationregistry": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
- "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-generator-function": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
- "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.4",
- "generator-function": "^2.0.0",
- "get-proto": "^1.0.1",
- "has-tostringtag": "^1.0.2",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-map": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
- "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-negative-zero": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
- "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-number-object": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
- "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-regex": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
- "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "gopd": "^1.2.0",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-set": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
- "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-shared-array-buffer": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
- "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-string": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
- "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-symbol": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
- "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "has-symbols": "^1.1.0",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-typed-array": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
- "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "which-typed-array": "^1.1.16"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-weakmap": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
- "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-weakref": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
- "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-weakset": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
- "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/iterator.prototype": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
- "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.6",
- "get-proto": "^1.0.0",
- "has-symbols": "^1.1.0",
- "set-function-name": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/jiti": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
- "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "jiti": "lib/jiti-cli.mjs"
- }
- },
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/js-yaml": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
- "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/json-buffer": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
- "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/json-schema-migrate": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-2.0.0.tgz",
- "integrity": "sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ajv": "^8.0.0"
- }
- },
- "node_modules/json-schema-migrate/node_modules/ajv": {
- "version": "8.17.1",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
- "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.3",
- "fast-uri": "^3.0.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/json-schema-migrate/node_modules/json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/json5": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
- "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.0"
- },
- "bin": {
- "json5": "lib/cli.js"
- }
- },
- "node_modules/jsonc-eslint-parser": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.1.tgz",
- "integrity": "sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "acorn": "^8.5.0",
- "eslint-visitor-keys": "^3.0.0",
- "espree": "^9.0.0",
- "semver": "^7.3.5"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ota-meshi"
- }
- },
- "node_modules/jsonc-eslint-parser/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/jsonc-eslint-parser/node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.9.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/jsonc-eslint-parser/node_modules/semver": {
- "version": "7.7.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/jsx-ast-utils": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
- "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-includes": "^3.1.6",
- "array.prototype.flat": "^1.3.1",
- "object.assign": "^4.1.4",
- "object.values": "^1.1.6"
- },
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "json-buffer": "3.0.1"
- }
- },
- "node_modules/levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-locate": "^5.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- },
- "bin": {
- "loose-envify": "cli.js"
- }
- },
- "node_modules/math-intrinsics": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
- "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "braces": "^3.0.3",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/module-replacements": {
- "version": "2.10.1",
- "resolved": "https://registry.npmjs.org/module-replacements/-/module-replacements-2.10.1.tgz",
- "integrity": "sha512-qkKuLpMHDqRSM676OPL7HUpCiiP3NSxgf8NNR1ga2h/iJLNKTsOSjMEwrcT85DMSti2vmOqxknOVBGWj6H6etQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/moment": {
- "version": "2.29.4",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
- "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-inspect": {
- "version": "1.13.4",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
- "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object.assign": {
- "version": "4.1.7",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
- "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0",
- "has-symbols": "^1.1.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/object.entries": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
- "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object.fromentries": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
- "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/object.groupby": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
- "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object.values": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
- "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/obsidian": {
- "version": "1.10.3",
- "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.3.tgz",
- "integrity": "sha512-VP+ZSxNMG7y6Z+sU9WqLvJAskCfkFrTz2kFHWmmzis+C+4+ELjk/sazwcTHrHXNZlgCeo8YOlM6SOrAFCynNew==",
- "license": "MIT",
- "dependencies": {
- "@types/codemirror": "5.60.8",
- "moment": "2.29.4"
- },
- "peerDependencies": {
- "@codemirror/state": "6.5.0",
- "@codemirror/view": "6.38.6"
- }
- },
- "node_modules/optionator": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
- "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "deep-is": "^0.1.3",
- "fast-levenshtein": "^2.0.6",
- "levn": "^0.4.1",
- "prelude-ls": "^1.2.1",
- "type-check": "^0.4.0",
- "word-wrap": "^1.2.5"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/own-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
- "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-intrinsic": "^1.2.6",
- "object-keys": "^1.1.1",
- "safe-push-apply": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-limit": "^3.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/possible-typed-array-names": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
- "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/prelude-ls": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
- "node_modules/punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/reflect.getprototypeof": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
- "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.9",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.7",
- "get-proto": "^1.0.1",
- "which-builtin-type": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/regexp-tree": {
- "version": "0.1.27",
- "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
- "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "regexp-tree": "bin/regexp-tree"
- }
- },
- "node_modules/regexp.prototype.flags": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
- "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-errors": "^1.3.0",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "set-function-name": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/require-from-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/resolve": {
- "version": "1.22.11",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
- "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.16.1",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/resolve-pkg-maps": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
- "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
- }
- },
- "node_modules/ret": {
- "version": "0.1.15",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12"
- }
- },
- "node_modules/reusify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
- "node_modules/safe-array-concat": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
- "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "get-intrinsic": "^1.2.6",
- "has-symbols": "^1.1.0",
- "isarray": "^2.0.5"
- },
- "engines": {
- "node": ">=0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/safe-push-apply": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
- "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "isarray": "^2.0.5"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/safe-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
- "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "regexp-tree": "~0.1.1"
- }
- },
- "node_modules/safe-regex-test": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
- "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "is-regex": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/set-function-length": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
- "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/set-function-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
- "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "functions-have-names": "^1.2.3",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/set-proto": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
- "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
- "side-channel-map": "^1.0.1",
- "side-channel-weakmap": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-list": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-weakmap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3",
- "side-channel-map": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/stop-iteration-iterator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
- "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "internal-slot": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/string.prototype.matchall": {
- "version": "4.0.12",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
- "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.6",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.6",
- "gopd": "^1.2.0",
- "has-symbols": "^1.1.0",
- "internal-slot": "^1.1.0",
- "regexp.prototype.flags": "^1.5.3",
- "set-function-name": "^2.0.2",
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.repeat": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
- "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
- }
- },
- "node_modules/string.prototype.trim": {
- "version": "1.2.10",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
- "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "define-data-property": "^1.1.4",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-object-atoms": "^1.0.0",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.trimend": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
- "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.trimstart": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
- "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/strip-bom": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/style-mod": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
- "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/synckit": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.3.tgz",
- "integrity": "sha512-JJoOEKTfL1urb1mDoEblhD9NhEbWmq9jHEMEnxoC4ujUaZ4itA8vKgwkFAyNClgxplLi9tsUKX+EduK0p/l7sg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@pkgr/core": "^0.1.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/unts"
- }
- },
- "node_modules/synckit/node_modules/tslib": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "dev": true,
- "license": "0BSD"
- },
- "node_modules/tapable": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
- "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/toml-eslint-parser": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.9.3.tgz",
- "integrity": "sha512-moYoCvkNUAPCxSW9jmHmRElhm4tVJpHL8ItC/+uYD0EpPSFXbck7yREz9tNdJVTSpHVod8+HoipcpbQ0oE6gsw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-visitor-keys": "^3.0.0"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ota-meshi"
- }
- },
- "node_modules/toml-eslint-parser/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/ts-api-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
- "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18.12"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4"
- }
- },
- "node_modules/tsconfig-paths": {
- "version": "3.15.0",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
- "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/json5": "^0.0.29",
- "json5": "^1.0.2",
- "minimist": "^1.2.6",
- "strip-bom": "^3.0.0"
- }
- },
- "node_modules/tslib": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
- "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
- "dev": true,
- "license": "0BSD"
- },
- "node_modules/tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "safe-buffer": "^5.0.1"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/type-check": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "prelude-ls": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/typed-array-buffer": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
- "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-typed-array": "^1.1.14"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/typed-array-byte-length": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
- "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "for-each": "^0.3.3",
- "gopd": "^1.2.0",
- "has-proto": "^1.2.0",
- "is-typed-array": "^1.1.14"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/typed-array-byte-offset": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
- "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "for-each": "^0.3.3",
- "gopd": "^1.2.0",
- "has-proto": "^1.2.0",
- "is-typed-array": "^1.1.15",
- "reflect.getprototypeof": "^1.0.9"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/typed-array-length": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
- "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "is-typed-array": "^1.1.13",
- "possible-typed-array-names": "^1.0.0",
- "reflect.getprototypeof": "^1.0.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/typescript-eslint": {
- "version": "8.35.1",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz",
- "integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/eslint-plugin": "8.35.1",
- "@typescript-eslint/parser": "8.35.1",
- "@typescript-eslint/utils": "8.35.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/unbox-primitive": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
- "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-bigints": "^1.0.2",
- "has-symbols": "^1.1.0",
- "which-boxed-primitive": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/uri-js": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
- "node_modules/w3c-keyname": {
- "version": "2.2.8",
- "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
- "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/which-boxed-primitive": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
- "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-bigint": "^1.1.0",
- "is-boolean-object": "^1.2.1",
- "is-number-object": "^1.1.1",
- "is-string": "^1.1.1",
- "is-symbol": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/which-builtin-type": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
- "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "function.prototype.name": "^1.1.6",
- "has-tostringtag": "^1.0.2",
- "is-async-function": "^2.0.0",
- "is-date-object": "^1.1.0",
- "is-finalizationregistry": "^1.1.0",
- "is-generator-function": "^1.0.10",
- "is-regex": "^1.2.1",
- "is-weakref": "^1.0.2",
- "isarray": "^2.0.5",
- "which-boxed-primitive": "^1.1.0",
- "which-collection": "^1.0.2",
- "which-typed-array": "^1.1.16"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/which-collection": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
- "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-map": "^2.0.3",
- "is-set": "^2.0.3",
- "is-weakmap": "^2.0.2",
- "is-weakset": "^2.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/which-typed-array": {
- "version": "1.1.19",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
- "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "for-each": "^0.3.5",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/word-wrap": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
- "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/yaml": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
- "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- }
- },
- "node_modules/yaml-eslint-parser": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.0.tgz",
- "integrity": "sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-visitor-keys": "^3.0.0",
- "yaml": "^2.0.0"
- },
- "engines": {
- "node": "^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ota-meshi"
- }
- },
- "node_modules/yaml-eslint-parser/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- }
- }
-}
+{
+ "name": "watchlog-plugin",
+ "version": "2.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "watchlog-plugin",
+ "version": "2.0.0",
+ "license": "0-BSD",
+ "dependencies": {
+ "fuse.js": "^7.3.0",
+ "obsidian": "latest"
+ },
+ "devDependencies": {
+ "@eslint/js": "9.30.1",
+ "@types/node": "^16.11.6",
+ "esbuild": "0.25.5",
+ "eslint-plugin-obsidianmd": "0.1.9",
+ "globals": "14.0.0",
+ "jiti": "2.6.1",
+ "tslib": "2.4.0",
+ "typescript": "^5.8.3",
+ "typescript-eslint": "8.35.1"
+ }
+ },
+ "node_modules/@codemirror/state": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz",
+ "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@marijn/find-cluster-break": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/view": {
+ "version": "6.38.6",
+ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz",
+ "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@codemirror/state": "^6.5.0",
+ "crelt": "^1.0.6",
+ "style-mod": "^4.1.0",
+ "w3c-keyname": "^2.2.4"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
+ "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
+ "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
+ "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
+ "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
+ "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
+ "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
+ "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
+ "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
+ "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
+ "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
+ "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
+ "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
+ "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
+ "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
+ "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
+ "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
+ "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
+ "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
+ "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
+ "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
+ "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
+ "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.30.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz",
+ "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/json": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/@eslint/json/-/json-0.14.0.tgz",
+ "integrity": "sha512-rvR/EZtvUG3p9uqrSmcDJPYSH7atmWr0RnFWN6m917MAPx82+zQgPUmDu0whPFG6XTyM0vB/hR6c1Q63OaYtCQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanwhocodes/momoa": "^3.3.10",
+ "natural-compare": "^1.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/momoa": {
+ "version": "3.3.10",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.10.tgz",
+ "integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@marijn/find-cluster-break": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
+ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@microsoft/eslint-plugin-sdl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@microsoft/eslint-plugin-sdl/-/eslint-plugin-sdl-1.1.0.tgz",
+ "integrity": "sha512-dxdNHOemLnBhfY3eByrujX9KyLigcNtW8sU+axzWv5nLGcsSBeKW2YYyTpfPo1hV8YPOmIGnfA4fZHyKVtWqBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-plugin-n": "17.10.3",
+ "eslint-plugin-react": "7.37.3",
+ "eslint-plugin-security": "1.4.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^9"
+ }
+ },
+ "node_modules/@microsoft/eslint-plugin-sdl/node_modules/eslint-plugin-security": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz",
+ "integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "node_modules/@microsoft/eslint-plugin-sdl/node_modules/safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ret": "~0.1.10"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgr/core": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
+ "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/codemirror": {
+ "version": "5.60.8",
+ "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz",
+ "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/tern": "*"
+ }
+ },
+ "node_modules/@types/eslint": {
+ "version": "8.56.2",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz",
+ "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "16.18.126",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz",
+ "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/tern": {
+ "version": "0.23.9",
+ "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
+ "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
+ "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.35.1",
+ "@typescript-eslint/type-utils": "8.35.1",
+ "@typescript-eslint/utils": "8.35.1",
+ "@typescript-eslint/visitor-keys": "8.35.1",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.35.1",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
+ "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.35.1",
+ "@typescript-eslint/types": "8.35.1",
+ "@typescript-eslint/typescript-estree": "8.35.1",
+ "@typescript-eslint/visitor-keys": "8.35.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
+ "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.35.1",
+ "@typescript-eslint/types": "^8.35.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
+ "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.35.1",
+ "@typescript-eslint/visitor-keys": "8.35.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
+ "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
+ "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.35.1",
+ "@typescript-eslint/utils": "8.35.1",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
+ "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
+ "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.35.1",
+ "@typescript-eslint/tsconfig-utils": "8.35.1",
+ "@typescript-eslint/types": "8.35.1",
+ "@typescript-eslint/visitor-keys": "8.35.1",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
+ "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.35.1",
+ "@typescript-eslint/types": "8.35.1",
+ "@typescript-eslint/typescript-estree": "8.35.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
+ "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.35.1",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/crelt": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
+ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/empathic": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz",
+ "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.3",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
+ "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
+ "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
+ "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.6",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.4",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
+ "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.5",
+ "@esbuild/android-arm": "0.25.5",
+ "@esbuild/android-arm64": "0.25.5",
+ "@esbuild/android-x64": "0.25.5",
+ "@esbuild/darwin-arm64": "0.25.5",
+ "@esbuild/darwin-x64": "0.25.5",
+ "@esbuild/freebsd-arm64": "0.25.5",
+ "@esbuild/freebsd-x64": "0.25.5",
+ "@esbuild/linux-arm": "0.25.5",
+ "@esbuild/linux-arm64": "0.25.5",
+ "@esbuild/linux-ia32": "0.25.5",
+ "@esbuild/linux-loong64": "0.25.5",
+ "@esbuild/linux-mips64el": "0.25.5",
+ "@esbuild/linux-ppc64": "0.25.5",
+ "@esbuild/linux-riscv64": "0.25.5",
+ "@esbuild/linux-s390x": "0.25.5",
+ "@esbuild/linux-x64": "0.25.5",
+ "@esbuild/netbsd-arm64": "0.25.5",
+ "@esbuild/netbsd-x64": "0.25.5",
+ "@esbuild/openbsd-arm64": "0.25.5",
+ "@esbuild/openbsd-x64": "0.25.5",
+ "@esbuild/sunos-x64": "0.25.5",
+ "@esbuild/win32-arm64": "0.25.5",
+ "@esbuild/win32-ia32": "0.25.5",
+ "@esbuild/win32-x64": "0.25.5"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
+ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.1",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-compat-utils": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
+ "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "eslint": ">=6.0.0"
+ }
+ },
+ "node_modules/eslint-compat-utils/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-depend": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-depend/-/eslint-plugin-depend-1.3.1.tgz",
+ "integrity": "sha512-1uo2rFAr9vzNrCYdp7IBZRB54LiyVxfaIso0R6/QV3t6Dax6DTbW/EV2Hktf0f4UtmGHK8UyzJWI382pwW04jw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "empathic": "^2.0.0",
+ "module-replacements": "^2.8.0",
+ "semver": "^7.6.3"
+ }
+ },
+ "node_modules/eslint-plugin-depend/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-es-x": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
+ "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/ota-meshi",
+ "https://opencollective.com/eslint"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.1.2",
+ "@eslint-community/regexpp": "^4.11.0",
+ "eslint-compat-utils": "^0.5.1"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-json-schema-validator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-json-schema-validator/-/eslint-plugin-json-schema-validator-5.1.0.tgz",
+ "integrity": "sha512-ZmVyxRIjm58oqe2kTuy90PpmZPrrKvOjRPXKzq8WCgRgAkidCgm5X8domL2KSfadZ3QFAmifMgGTcVNhZ5ez2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.3.0",
+ "ajv": "^8.0.0",
+ "debug": "^4.3.1",
+ "eslint-compat-utils": "^0.5.0",
+ "json-schema-migrate": "^2.0.0",
+ "jsonc-eslint-parser": "^2.0.0",
+ "minimatch": "^8.0.0",
+ "synckit": "^0.9.0",
+ "toml-eslint-parser": "^0.9.0",
+ "tunnel-agent": "^0.6.0",
+ "yaml-eslint-parser": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ota-meshi"
+ },
+ "peerDependencies": {
+ "eslint": ">=6.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-json-schema-validator/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/eslint-plugin-json-schema-validator/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-json-schema-validator/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint-plugin-json-schema-validator/node_modules/minimatch": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz",
+ "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/eslint-plugin-n": {
+ "version": "17.10.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.10.3.tgz",
+ "integrity": "sha512-ySZBfKe49nQZWR1yFaA0v/GsH6Fgp8ah6XV0WDz6CN8WO0ek4McMzb7A2xnf4DCYV43frjCygvb9f/wx7UUxRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "enhanced-resolve": "^5.17.0",
+ "eslint-plugin-es-x": "^7.5.0",
+ "get-tsconfig": "^4.7.0",
+ "globals": "^15.8.0",
+ "ignore": "^5.2.4",
+ "minimatch": "^9.0.5",
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.23.0"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/globals": {
+ "version": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-obsidianmd": {
+ "version": "0.1.9",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-obsidianmd/-/eslint-plugin-obsidianmd-0.1.9.tgz",
+ "integrity": "sha512-/gyo5vky3Y7re4BtT/8MQbHU5Wes4o6VRqas3YmXE7aTCnMsdV0kfzV1GDXJN9Hrsc9UQPoeKUMiapKL0aGE4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@microsoft/eslint-plugin-sdl": "^1.1.0",
+ "@types/eslint": "8.56.2",
+ "@types/node": "20.12.12",
+ "eslint": ">=9.0.0 <10.0.0",
+ "eslint-plugin-depend": "1.3.1",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-json-schema-validator": "5.1.0",
+ "eslint-plugin-security": "2.1.1",
+ "globals": "14.0.0",
+ "obsidian": "1.8.7",
+ "typescript": "5.4.5"
+ },
+ "bin": {
+ "eslint-plugin-obsidian": "dist/lib/index.js"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@eslint/js": "^9.30.1",
+ "@eslint/json": "0.14.0",
+ "eslint": ">=9.0.0 <10.0.0",
+ "obsidian": "1.8.7",
+ "typescript-eslint": "^8.35.1"
+ }
+ },
+ "node_modules/eslint-plugin-obsidianmd/node_modules/@types/node": {
+ "version": "20.12.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
+ "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/eslint-plugin-obsidianmd/node_modules/obsidian": {
+ "version": "1.8.7",
+ "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.8.7.tgz",
+ "integrity": "sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/codemirror": "5.60.8",
+ "moment": "2.29.4"
+ },
+ "peerDependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-obsidianmd/node_modules/typescript": {
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz",
+ "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.8",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-plugin-security": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-2.1.1.tgz",
+ "integrity": "sha512-7cspIGj7WTfR3EhaILzAPcfCo5R9FbeWvbgsPYWivSurTBKW88VQxtP3c4aWMG9Hz/GfJlJVdXEJ3c8LqS+u2w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-regex": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/@eslint/js": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
+ "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/fuse.js": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.3.0.tgz",
+ "integrity": "sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/krisk"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-migrate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-2.0.0.tgz",
+ "integrity": "sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ }
+ },
+ "node_modules/json-schema-migrate/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/json-schema-migrate/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/jsonc-eslint-parser": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.1.tgz",
+ "integrity": "sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.5.0",
+ "eslint-visitor-keys": "^3.0.0",
+ "espree": "^9.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ota-meshi"
+ }
+ },
+ "node_modules/jsonc-eslint-parser/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/jsonc-eslint-parser/node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/jsonc-eslint-parser/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/module-replacements": {
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/module-replacements/-/module-replacements-2.10.1.tgz",
+ "integrity": "sha512-qkKuLpMHDqRSM676OPL7HUpCiiP3NSxgf8NNR1ga2h/iJLNKTsOSjMEwrcT85DMSti2vmOqxknOVBGWj6H6etQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/obsidian": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.3.tgz",
+ "integrity": "sha512-VP+ZSxNMG7y6Z+sU9WqLvJAskCfkFrTz2kFHWmmzis+C+4+ELjk/sazwcTHrHXNZlgCeo8YOlM6SOrAFCynNew==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/codemirror": "5.60.8",
+ "moment": "2.29.4"
+ },
+ "peerDependencies": {
+ "@codemirror/state": "6.5.0",
+ "@codemirror/view": "6.38.6"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexp-tree": {
+ "version": "0.1.27",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
+ "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "regexp-tree": "bin/regexp-tree"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
+ "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regexp-tree": "~0.1.1"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/style-mod": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
+ "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/synckit": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.3.tgz",
+ "integrity": "sha512-JJoOEKTfL1urb1mDoEblhD9NhEbWmq9jHEMEnxoC4ujUaZ4itA8vKgwkFAyNClgxplLi9tsUKX+EduK0p/l7sg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/synckit/node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toml-eslint-parser": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.9.3.tgz",
+ "integrity": "sha512-moYoCvkNUAPCxSW9jmHmRElhm4tVJpHL8ItC/+uYD0EpPSFXbck7yREz9tNdJVTSpHVod8+HoipcpbQ0oE6gsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.0.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ota-meshi"
+ }
+ },
+ "node_modules/toml-eslint-parser/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz",
+ "integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.35.1",
+ "@typescript-eslint/parser": "8.35.1",
+ "@typescript-eslint/utils": "8.35.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/w3c-keyname": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
+ "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ }
+ },
+ "node_modules/yaml-eslint-parser": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.0.tgz",
+ "integrity": "sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.0.0",
+ "yaml": "^2.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ota-meshi"
+ }
+ },
+ "node_modules/yaml-eslint-parser/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 6c6b4ed..9ffd237 100644
--- a/package.json
+++ b/package.json
@@ -1,30 +1,30 @@
-{
- "name": "watchlog-plugin",
- "version": "1.1.0",
- "description": "Track your anime, movies, and TV shows; with episode tracking, progress stats, upcoming release alerts, and embeddable widgets.",
- "main": "main.js",
- "type": "module",
- "scripts": {
- "dev": "node esbuild.config.mjs",
- "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
- "version": "node version-bump.mjs && git add manifest.json versions.json",
- "lint": "eslint ."
- },
- "keywords": [],
- "license": "0-BSD",
- "devDependencies": {
- "@eslint/js": "9.30.1",
- "@types/node": "^16.11.6",
- "esbuild": "0.25.5",
- "eslint-plugin-obsidianmd": "0.1.9",
- "globals": "14.0.0",
- "jiti": "2.6.1",
- "tslib": "2.4.0",
- "typescript": "^5.8.3",
- "typescript-eslint": "8.35.1"
- },
- "dependencies": {
- "fuse.js": "^7.3.0",
- "obsidian": "latest"
- }
-}
+{
+ "name": "watchlog-plugin",
+ "version": "2.0.0",
+ "description": "Track your anime, movies, books, manga and TV shows; with episode tracking, progress stats, upcoming release alerts, and embeddable widgets.",
+ "main": "main.js",
+ "type": "module",
+ "scripts": {
+ "dev": "node esbuild.config.mjs",
+ "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
+ "version": "node version-bump.mjs && git add manifest.json versions.json",
+ "lint": "eslint ."
+ },
+ "keywords": [],
+ "license": "0-BSD",
+ "devDependencies": {
+ "@eslint/js": "9.30.1",
+ "@types/node": "^16.11.6",
+ "esbuild": "0.25.5",
+ "eslint-plugin-obsidianmd": "0.1.9",
+ "globals": "14.0.0",
+ "jiti": "2.6.1",
+ "tslib": "2.4.0",
+ "typescript": "^5.8.3",
+ "typescript-eslint": "8.35.1"
+ },
+ "dependencies": {
+ "fuse.js": "^7.3.0",
+ "obsidian": "latest"
+ }
+}
diff --git a/src/AddFromUrlModal.ts b/src/AddFromUrlModal.ts
index 7d03ca4..63badb9 100644
--- a/src/AddFromUrlModal.ts
+++ b/src/AddFromUrlModal.ts
@@ -66,31 +66,34 @@ export class AddFromUrlModal extends Modal {
}
if (this.errorEl) this.errorEl.hide();
- const result = this.plugin.settings.activeApi === 'TMDB'
- ? await this.plugin.apiService.getTmdbByImdbId(imdbId)
- : await this.plugin.apiService.getOmdbByImdbId(imdbId);
+ try {
+ const result = this.plugin.settings.activeApi === 'TMDB'
+ ? await this.plugin.apiService.getTmdbByImdbId(imdbId)
+ : await this.plugin.apiService.getOmdbByImdbId(imdbId);
- if (!result) {
+ if (!result) {
+ this.showError('Title not found. Please check the URL and try again.');
+ return;
+ }
+
+ this.close();
+
+ const type = result.mediaType === 'movie' ? 'Movie' : 'TV Show';
+ new AddTitleModal(this.app, this.plugin, this.dataManager, this.onAdded, {
+ title: result.title,
+ type,
+ episodes: result.episodes,
+ duration: result.episodeDuration,
+ releaseDate: result.releaseDate,
+ link: result.url,
+ seasons: result.seasons,
+ }).open();
+ } finally {
if (this.addBtn) {
this.addBtn.disabled = false;
this.addBtn.textContent = 'Add';
}
- this.showError('Title not found. Please check the URL and try again.');
- return;
}
-
- this.close();
-
- const type = result.mediaType === 'movie' ? 'Movie' : 'TV Show';
- new AddTitleModal(this.app, this.plugin, this.dataManager, this.onAdded, {
- title: result.title,
- type,
- episodes: result.episodes,
- duration: result.episodeDuration,
- releaseDate: result.releaseDate,
- link: result.url,
- seasons: result.seasons,
- }).open();
}
private showError(msg: string): void {
diff --git a/src/AddReadingModal.ts b/src/AddReadingModal.ts
new file mode 100644
index 0000000..75137fb
--- /dev/null
+++ b/src/AddReadingModal.ts
@@ -0,0 +1,741 @@
+import { App, FuzzySuggestModal, Modal, Notice, TFile } from 'obsidian';
+import type WatchLogPlugin from './main';
+import type { ReadingDataManager } from './ReadingDataManager';
+import type { BookSearchResult, MangaSearchResult } from './ApiService';
+import { googleBooksErrorMessage } from './ApiService';
+import {
+ Book,
+ Manga,
+ ReadingStatus,
+ SELECTABLE_READING_STATUSES,
+ formatDateDisplay,
+ parseDateInput,
+} from './types';
+
+export type ReadingMode = 'book' | 'manga';
+
+interface ReadingFormState {
+ title: string;
+ author: string;
+ status: ReadingStatus;
+ rating: number;
+ pagesRead: number;
+ totalPages: number;
+ chaptersRead: number;
+ totalChapters: number;
+ volumesRead: number;
+ totalVolumes: number;
+ coverUrl: string;
+ googleBooksId: string;
+ malId: string;
+ vaultPage: string;
+ dateStarted: string | null;
+ dateFinished: string | null;
+ releaseDate: string | null;
+}
+
+function emptyFormState(): ReadingFormState {
+ return {
+ title: '',
+ author: '',
+ status: 'Plan to Read',
+ rating: 0,
+ pagesRead: 0,
+ totalPages: 0,
+ chaptersRead: 0,
+ totalChapters: 0,
+ volumesRead: 0,
+ totalVolumes: 0,
+ coverUrl: '',
+ googleBooksId: '',
+ malId: '',
+ vaultPage: '',
+ dateStarted: null,
+ dateFinished: null,
+ releaseDate: null,
+ };
+}
+
+export function bookToFormState(book: Book): ReadingFormState {
+ return {
+ title: book.title,
+ author: book.author,
+ status: book.status,
+ rating: book.rating,
+ pagesRead: book.pagesRead,
+ totalPages: book.totalPages,
+ chaptersRead: book.chaptersRead,
+ totalChapters: book.totalChapters,
+ volumesRead: 0,
+ totalVolumes: 0,
+ coverUrl: book.coverUrl,
+ googleBooksId: book.googleBooksId,
+ malId: '',
+ vaultPage: book.vaultPage,
+ dateStarted: book.dateStarted,
+ dateFinished: book.dateFinished,
+ releaseDate: book.releaseDate,
+ };
+}
+
+export function mangaToFormState(manga: Manga): ReadingFormState {
+ return {
+ title: manga.title,
+ author: manga.author,
+ status: manga.status,
+ rating: manga.rating,
+ pagesRead: 0,
+ totalPages: 0,
+ chaptersRead: manga.chaptersRead,
+ totalChapters: manga.totalChapters,
+ volumesRead: manga.volumesRead,
+ totalVolumes: manga.totalVolumes,
+ coverUrl: manga.coverUrl,
+ googleBooksId: '',
+ malId: manga.malId,
+ vaultPage: manga.vaultPage,
+ dateStarted: manga.dateStarted,
+ dateFinished: manga.dateFinished,
+ releaseDate: manga.releaseDate,
+ };
+}
+
+export class VaultFilePicker extends FuzzySuggestModal {
+ private onPick: (file: TFile) => void;
+ private files: TFile[];
+
+ constructor(app: App, files: TFile[], onPick: (file: TFile) => void) {
+ super(app);
+ this.files = files;
+ this.onPick = onPick;
+ this.setPlaceholder('Pick a vault note...');
+ }
+
+ getItems(): TFile[] {
+ return this.files;
+ }
+ getItemText(item: TFile): string {
+ return item.path;
+ }
+ onChooseItem(item: TFile): void {
+ this.onPick(item);
+ }
+}
+
+export class AddReadingModal extends Modal {
+ private plugin: WatchLogPlugin;
+ private readingData: ReadingDataManager;
+ private mode: ReadingMode;
+ private state: ReadingFormState;
+ private existingId: string | null;
+ private onSaved: () => void;
+ private starsWrap: HTMLElement | null = null;
+ private lookupResultsEl: HTMLElement | null = null;
+ private formEl: HTMLElement | null = null;
+ private openVaultBtn: HTMLButtonElement | null = null;
+ private linkVaultBtn: HTMLButtonElement | null = null;
+ private lookupSearchGen = 0;
+ private selectGen = 0;
+ private prefillSearch: string;
+
+ constructor(
+ app: App,
+ plugin: WatchLogPlugin,
+ readingData: ReadingDataManager,
+ mode: ReadingMode,
+ onSaved: () => void,
+ initial?: { state: ReadingFormState; id: string },
+ prefillSearch?: string,
+ ) {
+ super(app);
+ this.plugin = plugin;
+ this.readingData = readingData;
+ this.mode = mode;
+ this.state = initial ? { ...initial.state } : emptyFormState();
+ if (!initial) {
+ const defStatus = readingData.getSettings().defaultStatus;
+ if (defStatus) this.state.status = defStatus;
+ }
+ this.existingId = initial?.id ?? null;
+ this.onSaved = onSaved;
+ this.prefillSearch = prefillSearch ?? '';
+ }
+
+ onOpen(): void {
+ const colorTheme = this.plugin.settings.colorTheme ?? 'default';
+ this.modalEl.setAttribute('data-theme', colorTheme);
+ this.contentEl.setAttribute('data-theme', colorTheme);
+ this.contentEl.addClass('wl-view');
+ this.contentEl.addClass('wl-reading-modal');
+ this.contentEl.addClass(this.mode === 'book' ? 'wl-reading-modal-book' : 'wl-reading-modal-manga');
+ this.titleEl.setText(this.headerTitle());
+ this.buildUI();
+ }
+
+ onClose(): void {
+ this.contentEl.empty();
+ }
+
+ private headerTitle(): string {
+ if (this.existingId) {
+ return this.mode === 'book' ? 'Edit book' : 'Edit manga';
+ }
+ return this.mode === 'book' ? 'Add book' : 'Add manga';
+ }
+
+ private buildUI(): void {
+ const c = this.contentEl;
+ c.empty();
+
+ this.renderHeaderRow(c);
+ this.renderLookupBar(c);
+ c.createDiv({ cls: 'wl-reading-modal-divider' });
+ this.formEl = c.createDiv({ cls: 'wl-reading-form-wrap' });
+ this.renderForm(this.formEl);
+ this.renderFooter(c);
+ }
+
+ private renderHeaderRow(parent: HTMLElement): void {
+ const row = parent.createDiv({ cls: 'wl-reading-modal-header' });
+ const left = row.createDiv({ cls: 'wl-reading-modal-header-left' });
+ left.createSpan({
+ cls: 'wl-reading-modal-header-icon',
+ text: this.mode === 'book' ? '📖' : '📓',
+ });
+ left.createSpan({
+ cls: 'wl-reading-modal-header-title',
+ text: this.headerTitle(),
+ });
+
+ const actions = row.createDiv({ cls: 'wl-reading-modal-header-actions' });
+ // "Link" button intentionally omitted from the Add modal — vault-page linking
+ // lives in the detail/edit modal (ReadingDetailModal).
+ this.openVaultBtn = actions.createEl('button', {
+ cls: 'wl-btn wl-btn-sm',
+ text: 'Open',
+ });
+ this.openVaultBtn.addEventListener('click', () => this.openLinkedVaultPage());
+ this.refreshVaultButtons();
+ }
+
+ private refreshVaultButtons(): void {
+ if (this.linkVaultBtn) {
+ this.linkVaultBtn.textContent = this.state.vaultPage ? 'Change link' : 'Link';
+ this.linkVaultBtn.title = this.state.vaultPage
+ ? `Linked: ${this.state.vaultPage}`
+ : 'Link a vault note to this entry';
+ }
+ if (this.openVaultBtn) {
+ const hasLink = !!this.state.vaultPage;
+ this.openVaultBtn.disabled = !hasLink;
+ this.openVaultBtn.toggleClass('is-hidden', !hasLink);
+ }
+ }
+
+ private openLinkedVaultPage(): void {
+ const path = this.state.vaultPage;
+ if (!path) return;
+ const file = this.plugin.app.vault.getAbstractFileByPath(path);
+ if (file instanceof TFile) {
+ void this.plugin.app.workspace.getLeaf('tab').openFile(file);
+ this.close();
+ } else {
+ new Notice('Linked vault page no longer exists.');
+ }
+ }
+
+ private renderLookupBar(parent: HTMLElement): void {
+ const lookup = parent.createDiv({ cls: 'wl-reading-lookup' });
+ const input = lookup.createEl('input', {
+ cls: 'wl-modal-input wl-reading-lookup-input',
+ attr: {
+ type: 'text',
+ placeholder: this.mode === 'book'
+ ? 'Search by title or ISBN...'
+ : 'Search by title or MAL ID...',
+ },
+ });
+ const fetchBtn = lookup.createEl('button', {
+ cls: 'wl-btn wl-reading-lookup-btn',
+ text: 'Fetch',
+ });
+ this.lookupResultsEl = parent.createDiv({ cls: 'wl-reading-lookup-results' });
+
+ // Prefill the search box (e.g. from a Drafts entry) without auto-fetching,
+ // mirroring the Watchlist Add modal's prefill behavior.
+ if (this.prefillSearch) input.value = this.prefillSearch;
+
+ const runFetch = (): void => {
+ const q = input.value.trim();
+ if (!q) {
+ new Notice('Enter a search term first.');
+ return;
+ }
+ void this.performLookup(q);
+ };
+
+ fetchBtn.addEventListener('click', runFetch);
+ input.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ runFetch();
+ }
+ });
+ }
+
+ private async performLookup(query: string): Promise {
+ const gen = ++this.lookupSearchGen;
+ if (!this.lookupResultsEl) return;
+ this.lookupResultsEl.empty();
+ this.lookupResultsEl.createDiv({ cls: 'wl-reading-lookup-loading', text: 'Searching...' });
+
+ try {
+ if (this.mode === 'book') {
+ if (!this.plugin.apiService.hasGoogleBooksKey()) {
+ if (gen !== this.lookupSearchGen) return;
+ this.lookupResultsEl.empty();
+ this.lookupResultsEl.createDiv({
+ cls: 'wl-reading-lookup-empty',
+ text: 'Google Books API key required — add one in Settings → API → Books.',
+ });
+ return;
+ }
+ const results = await this.plugin.apiService.searchGoogleBooks(query);
+ if (gen !== this.lookupSearchGen) return;
+ this.renderBookLookupResults(results);
+ } else {
+ const isNumeric = /^\d+$/.test(query);
+ let results: MangaSearchResult[];
+ if (isNumeric) {
+ const single = await this.plugin.apiService.getMangaByMalId(parseInt(query, 10));
+ if (gen !== this.lookupSearchGen) return;
+ results = single ? [single] : [];
+ } else {
+ results = await this.plugin.apiService.searchManga(query);
+ if (gen !== this.lookupSearchGen) return;
+ }
+ this.renderMangaLookupResults(results);
+ }
+ } catch (err) {
+ if (gen !== this.lookupSearchGen) return;
+ this.lookupResultsEl.empty();
+ const msg = this.mode === 'book' ? googleBooksErrorMessage(err) : 'Lookup failed. Check your connection.';
+ this.lookupResultsEl.createDiv({ cls: 'wl-reading-lookup-empty', text: msg });
+ }
+ }
+
+ private renderBookLookupResults(results: BookSearchResult[]): void {
+ if (!this.lookupResultsEl) return;
+ this.lookupResultsEl.empty();
+ if (results.length === 0) {
+ this.lookupResultsEl.createDiv({ cls: 'wl-reading-lookup-empty', text: 'No matches.' });
+ return;
+ }
+ for (const r of results) {
+ const item = this.lookupResultsEl.createDiv({ cls: 'wl-reading-lookup-item' });
+ this.renderLookupItemTitle(item, r.title, r.url);
+ const meta = [r.author || 'Unknown author', r.year ? String(r.year) : ''].filter(Boolean).join(' · ');
+ item.createDiv({ cls: 'wl-reading-lookup-item-meta', text: meta });
+ item.addEventListener('click', () => this.applyBookResult(r));
+ }
+ }
+
+ /** Title row with a globe web-link to the right (same pattern as the Watchlist). */
+ private renderLookupItemTitle(item: HTMLElement, title: string, url: string): void {
+ const titleRow = item.createDiv({ cls: 'wl-reading-lookup-item-title-row' });
+ titleRow.createDiv({ cls: 'wl-reading-lookup-item-title', text: title || '(untitled)' });
+ if (url) {
+ const linkIcon = titleRow.createEl('a', { cls: 'wl-acc-link-icon', text: '🌐' });
+ linkIcon.href = url;
+ linkIcon.title = 'Open external link';
+ linkIcon.target = '_blank';
+ linkIcon.rel = 'noopener noreferrer';
+ linkIcon.addEventListener('click', (e) => e.stopPropagation());
+ }
+ }
+
+ private renderMangaLookupResults(results: MangaSearchResult[]): void {
+ if (!this.lookupResultsEl) return;
+ this.lookupResultsEl.empty();
+ if (results.length === 0) {
+ this.lookupResultsEl.createDiv({ cls: 'wl-reading-lookup-empty', text: 'No matches.' });
+ return;
+ }
+ for (const r of results) {
+ const item = this.lookupResultsEl.createDiv({ cls: 'wl-reading-lookup-item' });
+ this.renderLookupItemTitle(item, r.title, r.url);
+ const meta = [r.author || 'Unknown author', r.year ? String(r.year) : ''].filter(Boolean).join(' · ');
+ item.createDiv({ cls: 'wl-reading-lookup-item-meta', text: meta });
+ item.addEventListener('click', () => this.applyMangaResult(r));
+ }
+ }
+
+ private applyBookResult(r: BookSearchResult): void {
+ // Always overwrite the auto-populated fields so re-selecting a different
+ // result fully replaces the previous selection's data.
+ const gen = ++this.selectGen;
+ this.state.title = r.title;
+ this.state.author = r.author;
+ this.state.totalPages = r.totalPages;
+ this.state.coverUrl = r.coverUrl;
+ this.state.googleBooksId = r.googleBooksId;
+ this.state.releaseDate = r.releaseDate ?? '';
+ this.refreshForm();
+ // Keep the results list open so another result can be picked.
+ // Search results sometimes omit page counts and only return thumbnail covers —
+ // fetch the full volume for the selected book to upgrade both.
+ if (r.googleBooksId) {
+ void (async () => {
+ try {
+ const detail = await this.plugin.apiService.getGoogleBookById(r.googleBooksId);
+ if (gen !== this.selectGen || !detail) return; // a newer selection superseded this one
+ if (detail.totalPages > 0) this.state.totalPages = detail.totalPages;
+ if (detail.coverUrl) this.state.coverUrl = detail.coverUrl;
+ this.refreshForm();
+ } catch {
+ // Best-effort enrichment; the book is already applied from search results.
+ }
+ })();
+ }
+ }
+
+ private applyMangaResult(r: MangaSearchResult): void {
+ // Always overwrite the auto-populated fields so re-selecting a different
+ // result fully replaces the previous selection's data.
+ const gen = ++this.selectGen;
+ this.state.title = r.title;
+ this.state.author = r.author;
+ this.state.totalChapters = r.totalChapters;
+ this.state.totalVolumes = r.totalVolumes;
+ this.state.coverUrl = r.coverUrl;
+ this.state.malId = r.malId > 0 ? String(r.malId) : '';
+ this.state.releaseDate = r.releaseDate ?? '';
+ this.refreshForm();
+ // Keep the results list open so another result can be picked.
+ // Search returns null chapters/volumes for ongoing titles — fetch detail only
+ // for the selected manga to populate the totals.
+ if (r.malId > 0) {
+ void (async () => {
+ const detail = await this.plugin.apiService.getMangaByMalId(r.malId);
+ if (gen !== this.selectGen || !detail) return; // superseded or no data
+ this.state.totalChapters = detail.totalChapters > 0 ? detail.totalChapters : this.state.totalChapters;
+ this.state.totalVolumes = detail.totalVolumes > 0 ? detail.totalVolumes : this.state.totalVolumes;
+ this.refreshForm();
+ })();
+ }
+ }
+
+ private refreshForm(): void {
+ if (!this.formEl) return;
+ this.formEl.empty();
+ this.renderForm(this.formEl);
+ }
+
+ private renderForm(parent: HTMLElement): void {
+ const form = parent.createDiv({ cls: 'wl-reading-form' });
+
+ // Title (required)
+ this.renderTextField(form, 'Title', 'Book title', this.state.title, (v) => {
+ this.state.title = v;
+ });
+
+ // Author
+ this.renderTextField(form, 'Author', 'Author name', this.state.author, (v) => {
+ this.state.author = v;
+ });
+
+ // Status + Rating row (compact, inline)
+ this.renderStatusRatingRow(form);
+
+ // PROGRESS + OPTIONAL side-by-side grid
+ this.renderProgressOptionalGrid(form);
+ }
+
+ private renderProgressOptionalGrid(parent: HTMLElement): void {
+ const grid = parent.createDiv({ cls: 'wl-reading-form-grid' });
+
+ const progressCol = grid.createDiv({ cls: 'wl-reading-form-grid-col' });
+ progressCol.createDiv({ cls: 'wl-reading-section-label', text: 'Progress' });
+ if (this.mode === 'book') {
+ this.renderTwoNumberRow(
+ progressCol,
+ { label: 'Pages read', value: this.state.pagesRead, onChange: (n) => { this.state.pagesRead = n; } },
+ { label: 'Chapters read', value: this.state.chaptersRead, onChange: (n) => { this.state.chaptersRead = n; } },
+ );
+ this.renderTwoNumberRow(
+ progressCol,
+ { label: 'Total pages', value: this.state.totalPages, onChange: (n) => { this.state.totalPages = n; } },
+ { label: 'Total chapters', value: this.state.totalChapters, onChange: (n) => { this.state.totalChapters = n; } },
+ );
+ } else {
+ this.renderTwoNumberRow(
+ progressCol,
+ { label: 'Chapters read', value: this.state.chaptersRead, onChange: (n) => { this.state.chaptersRead = n; } },
+ { label: 'Volumes read', value: this.state.volumesRead, onChange: (n) => { this.state.volumesRead = n; } },
+ );
+ this.renderTwoNumberRow(
+ progressCol,
+ { label: 'Total chapters', value: this.state.totalChapters, onChange: (n) => { this.state.totalChapters = n; } },
+ { label: 'Total volumes', value: this.state.totalVolumes, onChange: (n) => { this.state.totalVolumes = n; } },
+ );
+ }
+
+ const optionalCol = grid.createDiv({ cls: 'wl-reading-form-grid-col' });
+ optionalCol.createDiv({ cls: 'wl-reading-section-label', text: 'Optional' });
+ this.renderTextField(optionalCol, 'Cover URL', 'https://...', this.state.coverUrl, (v) => {
+ this.state.coverUrl = v;
+ });
+ this.renderDateField(optionalCol, 'Start date', this.state.dateStarted, (parsed) => {
+ this.state.dateStarted = parsed;
+ });
+ this.renderDateField(optionalCol, 'Release date', this.state.releaseDate, (parsed) => {
+ this.state.releaseDate = parsed;
+ });
+ }
+
+ private renderTextField(
+ parent: HTMLElement,
+ label: string,
+ placeholder: string,
+ value: string,
+ onChange: (v: string) => void,
+ ): void {
+ const row = parent.createDiv({ cls: 'wl-reading-row' });
+ row.createSpan({ cls: 'wl-reading-label', text: label });
+ const input = row.createEl('input', {
+ cls: 'wl-modal-input',
+ attr: { type: 'text', placeholder },
+ });
+ input.value = value;
+ input.addEventListener('input', () => onChange(input.value));
+ }
+
+ private renderStatusRatingRow(parent: HTMLElement): void {
+ const row = parent.createDiv({ cls: 'wl-reading-status-rating-row' });
+
+ // Status (compact — only as wide as needed)
+ const statusGroup = row.createDiv({ cls: 'wl-reading-inline-group' });
+ statusGroup.createSpan({ cls: 'wl-reading-label', text: 'Status' });
+ const select = statusGroup.createEl('select', { cls: 'wl-select wl-reading-status-select' });
+ for (const s of SELECTABLE_READING_STATUSES) {
+ const opt = select.createEl('option', { text: s, value: s });
+ if (s === this.state.status) opt.selected = true;
+ }
+ select.addEventListener('change', () => {
+ this.state.status = select.value as ReadingStatus;
+ });
+
+ // Rating (inline next to status)
+ const ratingGroup = row.createDiv({ cls: 'wl-reading-inline-group' });
+ ratingGroup.createSpan({ cls: 'wl-reading-label', text: 'Rating' });
+ this.starsWrap = ratingGroup.createDiv({ cls: 'wl-stars wl-reading-stars' });
+ this.renderStars();
+ }
+
+ private renderStars(): void {
+ if (!this.starsWrap) return;
+ this.starsWrap.empty();
+ for (let i = 1; i <= 5; i++) {
+ const star = this.starsWrap.createSpan({
+ cls: `wl-star${this.state.rating >= i ? ' is-active' : ''}`,
+ text: '★',
+ });
+ star.addEventListener('click', () => {
+ this.state.rating = this.state.rating === i ? 0 : i;
+ this.renderStars();
+ });
+ }
+ }
+
+ private renderTwoNumberRow(
+ parent: HTMLElement,
+ left: { label: string; value: number; onChange: (n: number) => void },
+ right: { label: string; value: number; onChange: (n: number) => void },
+ ): void {
+ const row = parent.createDiv({ cls: 'wl-reading-row wl-reading-row-split' });
+ this.renderNumberCol(row, left);
+ this.renderNumberCol(row, right);
+ }
+
+ private renderNumberCol(
+ parent: HTMLElement,
+ field: { label: string; value: number; onChange: (n: number) => void },
+ ): void {
+ const col = parent.createDiv({ cls: 'wl-reading-col' });
+ col.createSpan({ cls: 'wl-reading-label', text: field.label });
+ const input = col.createEl('input', {
+ cls: 'wl-modal-input wl-modal-input-sm',
+ attr: { type: 'number', min: '0' },
+ });
+ input.value = String(field.value);
+ input.addEventListener('input', () => {
+ const n = parseInt(input.value, 10);
+ field.onChange(isNaN(n) || n < 0 ? 0 : n);
+ });
+ }
+
+ private renderDateField(
+ parent: HTMLElement,
+ label: string,
+ value: string | null,
+ onParsed: (parsed: string | null) => void,
+ ): void {
+ const row = parent.createDiv({ cls: 'wl-reading-row' });
+ row.createSpan({ cls: 'wl-reading-label', text: label });
+ const input = row.createEl('input', {
+ cls: 'wl-modal-input',
+ attr: { type: 'text', placeholder: 'DD/MM/YYYY', maxlength: '10' },
+ });
+ input.value = value ? formatDateDisplay(value) : '';
+ input.addEventListener('change', () => {
+ const raw = input.value.trim();
+ if (!raw) {
+ input.removeClass('wl-input-error');
+ onParsed(null);
+ return;
+ }
+ const parsed = parseDateInput(raw);
+ if (parsed) {
+ input.removeClass('wl-input-error');
+ onParsed(parsed);
+ } else {
+ input.addClass('wl-input-error');
+ }
+ });
+ }
+
+ private renderFooter(parent: HTMLElement): void {
+ const footer = parent.createDiv({ cls: 'wl-reading-modal-footer' });
+ const cancelBtn = footer.createEl('button', { cls: 'wl-btn', text: 'Cancel' });
+ cancelBtn.addEventListener('click', () => this.close());
+
+ const saveBtn = footer.createEl('button', {
+ cls: 'wl-reading-add-btn wl-btn-success wl-reading-modal-save',
+ text: this.saveButtonLabel(),
+ });
+ saveBtn.addEventListener('click', () => void this.save());
+ }
+
+ private saveButtonLabel(): string {
+ if (this.existingId) return 'Save changes';
+ return this.mode === 'book' ? 'Add book' : 'Add manga';
+ }
+
+ private async save(): Promise {
+ const title = this.state.title.trim();
+ if (!title) {
+ new Notice('Please enter a title.');
+ return;
+ }
+
+ if (this.mode === 'book') {
+ await this.saveBook(title);
+ } else {
+ await this.saveManga(title);
+ }
+ this.onSaved();
+ this.close();
+ }
+
+ private async saveBook(title: string): Promise {
+ if (this.existingId) {
+ const existing = this.readingData.getBook(this.existingId);
+ if (!existing) {
+ new Notice('Original book no longer exists.');
+ return;
+ }
+ const updated: Book = {
+ ...existing,
+ title,
+ author: this.state.author.trim(),
+ status: this.state.status,
+ rating: this.state.rating,
+ pagesRead: this.state.pagesRead,
+ totalPages: this.state.totalPages,
+ chaptersRead: this.state.chaptersRead,
+ totalChapters: this.state.totalChapters,
+ coverUrl: this.state.coverUrl.trim(),
+ googleBooksId: this.state.googleBooksId.trim(),
+ vaultPage: this.state.vaultPage,
+ dateStarted: this.state.dateStarted,
+ dateFinished: this.state.dateFinished,
+ releaseDate: this.state.releaseDate,
+ };
+ await this.readingData.updateBook(updated);
+ } else {
+ const now = new Date().toISOString();
+ const book: Book = {
+ id: this.readingData.generateBookId(title),
+ title,
+ author: this.state.author.trim(),
+ status: this.state.status,
+ rating: this.state.rating,
+ pagesRead: this.state.pagesRead,
+ totalPages: this.state.totalPages,
+ chaptersRead: this.state.chaptersRead,
+ totalChapters: this.state.totalChapters,
+ coverUrl: this.state.coverUrl.trim(),
+ googleBooksId: this.state.googleBooksId.trim(),
+ vaultPage: this.state.vaultPage,
+ dateStarted: this.state.dateStarted,
+ dateFinished: this.state.dateFinished,
+ releaseDate: this.state.releaseDate,
+ dateAdded: now,
+ dateModified: now,
+ customFields: {},
+ };
+ await this.readingData.addBook(book);
+ }
+ }
+
+ private async saveManga(title: string): Promise {
+ if (this.existingId) {
+ const existing = this.readingData.getManga(this.existingId);
+ if (!existing) {
+ new Notice('Original manga no longer exists.');
+ return;
+ }
+ const updated: Manga = {
+ ...existing,
+ title,
+ author: this.state.author.trim(),
+ status: this.state.status,
+ rating: this.state.rating,
+ chaptersRead: this.state.chaptersRead,
+ totalChapters: this.state.totalChapters,
+ volumesRead: this.state.volumesRead,
+ totalVolumes: this.state.totalVolumes,
+ coverUrl: this.state.coverUrl.trim(),
+ malId: this.state.malId.trim(),
+ vaultPage: this.state.vaultPage,
+ dateStarted: this.state.dateStarted,
+ dateFinished: this.state.dateFinished,
+ releaseDate: this.state.releaseDate,
+ };
+ await this.readingData.updateManga(updated);
+ } else {
+ const now = new Date().toISOString();
+ const manga: Manga = {
+ id: this.readingData.generateMangaId(title),
+ title,
+ author: this.state.author.trim(),
+ status: this.state.status,
+ rating: this.state.rating,
+ chaptersRead: this.state.chaptersRead,
+ totalChapters: this.state.totalChapters,
+ volumesRead: this.state.volumesRead,
+ totalVolumes: this.state.totalVolumes,
+ coverUrl: this.state.coverUrl.trim(),
+ malId: this.state.malId.trim(),
+ vaultPage: this.state.vaultPage,
+ dateStarted: this.state.dateStarted,
+ dateFinished: this.state.dateFinished,
+ releaseDate: this.state.releaseDate,
+ dateAdded: now,
+ dateModified: now,
+ customFields: {},
+ };
+ await this.readingData.addManga(manga);
+ }
+ }
+}
diff --git a/src/AddTitleModal.ts b/src/AddTitleModal.ts
index 64725a8..556fdb3 100644
--- a/src/AddTitleModal.ts
+++ b/src/AddTitleModal.ts
@@ -8,7 +8,7 @@ import type {
MediaSearchResult,
Season,
} from './types';
-import { formatDateDisplay, parseDateInput, parseReleaseDateInput } from './types';
+import { formatDateDisplay, parseDateInput, parseReleaseDateInput, getApiGroupForType } from './types';
type SearchResult = AnimeSearchResult | MediaSearchResult;
@@ -26,7 +26,9 @@ export class AddTitleModal extends Modal {
private searchQuery = '';
private searchResults: SearchResult[] = [];
private isSearching = false;
+ private searchGeneration = 0;
private autoSearch = false;
+ private noApiMessage = '';
// Editable fields
private fieldTitle = '';
@@ -39,6 +41,7 @@ export class AddTitleModal extends Modal {
private fieldPriority = 'Medium';
private fieldDateStarted = '';
private fieldMalId: number | null = null;
+ private fieldAnilistId: number | null = null;
private skipDuplicateCheck = false;
private duplicateWarningEl: HTMLElement | null = null;
@@ -117,6 +120,7 @@ export class AddTitleModal extends Modal {
typeSelect.addEventListener('change', () => {
this.selectedType = typeSelect.value;
this.searchResults = [];
+ this.noApiMessage = '';
this.renderResults();
this.renderForm();
});
@@ -148,28 +152,42 @@ export class AddTitleModal extends Modal {
private async performSearch(): Promise {
if (!this.searchQuery.trim()) return;
+ const gen = ++this.searchGeneration;
this.isSearching = true;
this.renderResults();
try {
const api = this.plugin.apiService;
const activeApi = this.plugin.settings.activeApi ?? 'OMDb';
- const isAnime = this.selectedType === 'Anime';
+ const apiGroup = getApiGroupForType(this.selectedType, this.plugin.settings.typeApiMapping);
const isMovie = this.selectedType === 'Movie';
- if (isAnime) {
- this.searchResults = await api.searchAnime(this.searchQuery);
+ let results: SearchResult[] = [];
+ let noApi = '';
+ if (apiGroup === '') {
+ noApi = `No API configured for type "${this.selectedType}". Go to Settings → API to set one up.`;
+ } else if (apiGroup === 'anime') {
+ const animeSource = this.plugin.settings.animeApiSource ?? 'jikan';
+ results = animeSource === 'anilist'
+ ? await api.searchAniList(this.searchQuery)
+ : await api.searchAnime(this.searchQuery);
} else if (activeApi === 'TMDB') {
- this.searchResults = await api.searchTmdb(this.searchQuery, isMovie ? 'movie' : 'series');
+ results = await api.searchTmdb(this.searchQuery, isMovie ? 'movie' : 'series');
} else {
- this.searchResults = await api.searchOmdb(this.searchQuery, isMovie ? 'movie' : 'series');
+ results = await api.searchOmdb(this.searchQuery, isMovie ? 'movie' : 'series');
}
+ if (gen !== this.searchGeneration) return; // stale
+ this.noApiMessage = noApi;
+ this.searchResults = results;
} catch {
+ if (gen !== this.searchGeneration) return;
new Notice('Search failed. Check your connection and API settings.');
this.searchResults = [];
} finally {
- this.isSearching = false;
- this.renderResults();
+ if (gen === this.searchGeneration) {
+ this.isSearching = false;
+ this.renderResults();
+ }
}
}
@@ -182,6 +200,13 @@ export class AddTitleModal extends Modal {
return;
}
+ if (this.noApiMessage) {
+ const msg = this.resultsEl.createDiv({ cls: 'wl-modal-no-api' });
+ msg.createSpan({ cls: 'wl-modal-no-api-icon', text: 'ℹ' });
+ msg.createSpan({ cls: 'wl-modal-no-api-text', text: this.noApiMessage });
+ return;
+ }
+
if (this.searchResults.length === 0) return;
this.searchResults.forEach((result, idx) => {
@@ -204,6 +229,7 @@ export class AddTitleModal extends Modal {
}
itemEl.addClass('is-selected');
+ const gen = ++this.searchGeneration;
const api = this.plugin.apiService;
const activeApi = this.plugin.settings.activeApi ?? 'OMDb';
try {
@@ -211,6 +237,7 @@ export class AddTitleModal extends Modal {
const full = activeApi === 'TMDB'
? await api.getTmdbTvDetails(result.imdbId)
: await api.getOmdbTvDetails(result.imdbId);
+ if (gen !== this.searchGeneration) return;
if (full) {
this.fieldTitle = full.title;
this.fieldEpisodes = full.episodes;
@@ -223,6 +250,7 @@ export class AddTitleModal extends Modal {
const full = activeApi === 'TMDB'
? await api.getTmdbMovieDetails(result.imdbId)
: await api.getOmdbMovieDetails(result.imdbId);
+ if (gen !== this.searchGeneration) return;
if (full) {
this.fieldTitle = full.title;
this.fieldEpisodes = full.episodes;
@@ -239,7 +267,13 @@ export class AddTitleModal extends Modal {
this.fieldReleaseDate = anime.releaseDate;
this.fieldLink = anime.url;
this.fieldSeasons = anime.seasons;
- this.fieldMalId = anime.malId;
+ if (anime.anilistId && anime.anilistId > 0) {
+ this.fieldAnilistId = anime.anilistId;
+ this.fieldMalId = null;
+ } else {
+ this.fieldMalId = anime.malId;
+ this.fieldAnilistId = null;
+ }
}
} catch {
this.fieldTitle = result.title;
@@ -478,9 +512,10 @@ export class AddTitleModal extends Modal {
seasons: this.fieldSeasons,
watchedEpisodes: [],
...(this.fieldMalId !== null ? { malId: this.fieldMalId } : {}),
+ ...(this.fieldAnilistId !== null ? { anilistId: this.fieldAnilistId } : {}),
};
- // Bug 3: auto-complete episodes when adding with Completed status
+ // Auto-complete episodes when adding with Completed status
if (entry.status === 'Completed' && entry.totalEpisodes > 0) {
entry.watchedEpisodes = Array.from({ length: entry.totalEpisodes }, (_, i) => i + 1);
if (this.plugin.settings.setFinishDateAutomatically) {
@@ -490,6 +525,17 @@ export class AddTitleModal extends Modal {
await this.dataManager.addTitle(entry);
+ // Fire-and-forget community rating fetch (no await — non-blocking)
+ void (async () => {
+ const animeSource = this.plugin.settings.animeApiSource ?? 'jikan';
+ const group = getApiGroupForType(entry.type, this.plugin.settings.typeApiMapping);
+ if (group === '') return;
+ const result = await this.plugin.apiService.fetchCommunityRating(entry, animeSource, group);
+ if (result) {
+ this.dataManager.updateCommunityRating(entry.id, result.rating, result.votes, result.source);
+ }
+ })();
+
if (this.selectedGroupId) {
await this.dataManager.addTitleToGroup(this.selectedGroupId, entry.id);
} else if (this.newGroupName.trim()) {
diff --git a/src/AirtimeTab.ts b/src/AirtimeTab.ts
index aae3efb..2542042 100644
--- a/src/AirtimeTab.ts
+++ b/src/AirtimeTab.ts
@@ -1,11 +1,42 @@
import { App, Modal, Notice } from 'obsidian';
import type WatchLogPlugin from './main';
import type { DataManager } from './DataManager';
-import type { WatchLogTitle, AirtimeEntry, AirtimeSchedule, AirtimeRecurrence, MaybeTitle } from './types';
-import { getAirtimeScheduleString, getThemedColor } from './types';
-import { InsertWidgetModal } from './InsertWidgetModal';
+import type { ReadingDataManager } from './ReadingDataManager';
+import type { WatchLogTitle, AirtimeEntry, AirtimeSchedule, AirtimeRecurrence, MaybeTitle, Book, Manga } from './types';
+import { getAirtimeScheduleString, getThemedColor, getReadingTypeColor, parseDateInput, isReleaseDateFuture } from './types';
import { ConfirmModal } from './ConfirmModal';
import { MaybeAddModal } from './MaybeAddModal';
+import { ReadingScheduleModal } from './ReadingScheduleModal';
+import { UpcomingFinderModal, type UpcomingFinderItem } from './UpcomingFinderModal';
+
+/**
+ * A normalized Upcoming card subject — either a watchlist title or a reading item.
+ * Lets the tracker render and act on both through one shared code path.
+ */
+type ResolvedUpcoming = {
+ source: 'watchlist' | 'reading';
+ id: string;
+ title: string;
+ /** Badge text: a watch type ("Anime", "Movie", …) or "Book" / "Manga". */
+ typeName: string;
+ /** Colored-badge color, or null to render a plain badge (reading). */
+ typeColor: string | null;
+ externalLink: string;
+ /** True when the item is a single release (movie/book): 0 or 1 total units. */
+ isSingle: boolean;
+ /** Total episodes (watch) or total chapters (reading). */
+ totalUnits: number;
+ /** Lowercase noun for the incrementing unit: "episode" | "chapter". */
+ unitNoun: string;
+ /** Capitalized incrementing unit: "Episode" | "Chapter". */
+ unitNounCap: string;
+ /** Capitalized grouping unit: "Season" | "Volume". */
+ groupNounCap: string;
+ /** Badge tail: "Airing next" | "Reading next". */
+ nextLabel: string;
+ /** Status to revert to on single-release tick: "Plan to watch" | "Plan to Read". */
+ planStatus: string;
+};
// ── Helpers ───────────────────────────────────────────────────────────────────
@@ -73,7 +104,7 @@ function getDetailedCountdown(
if (schedule.recurrence === 'daily') {
const passed = isAirtimePassedNow(schedule);
- // If user already ticked today, advance to tomorrow
+ // Already ticked today → advance to tomorrow
if (passed && acknowledgedToday) return { kind: 'future', label: 'Tomorrow', daysUntil: 1 };
if (passed) return { kind: 'aired', label: 'Aired', daysUntil: 0 };
return { kind: 'today-before', label: 'Today', daysUntil: 0 };
@@ -138,9 +169,10 @@ export class AirtimeTab {
private container: HTMLElement;
private plugin: WatchLogPlugin;
private dataManager: DataManager;
+ private readingDataManager: ReadingDataManager;
private onCountChange?: (count: number) => void;
- // Sub-tab state (Feature 2)
+ // Sub-tab state
currentSubTab: 'tracker' | 'history' | 'maybe' = 'tracker';
// Selection mode
@@ -159,9 +191,63 @@ export class AirtimeTab {
this.container = container;
this.plugin = plugin;
this.dataManager = dataManager;
+ this.readingDataManager = plugin.readingDataManager;
this.onCountChange = onCountChange;
}
+ /**
+ * Normalizes an airtime entry into a renderable subject, resolving the
+ * underlying watchlist title or reading item. Returns null for orphan entries.
+ */
+ private resolveEntry(entry: AirtimeEntry): ResolvedUpcoming | null {
+ if (entry.source === 'reading') {
+ const kind = entry.readingKind ?? 'book';
+ const item: Book | Manga | undefined =
+ kind === 'book' ? this.readingDataManager.getBook(entry.titleId) : this.readingDataManager.getManga(entry.titleId);
+ if (!item) return null;
+ const totalUnits = entry.totalEpisodes ?? item.totalChapters ?? 0;
+ return {
+ source: 'reading',
+ id: item.id,
+ title: item.title,
+ typeName: kind === 'book' ? 'Book' : 'Manga',
+ typeColor: this.plugin.settings.coloredTypeBadges
+ ? getReadingTypeColor(kind, this.plugin.settings)
+ : null,
+ externalLink: item.externalLink ?? '',
+ isSingle: totalUnits <= 1,
+ totalUnits,
+ unitNoun: 'chapter',
+ unitNounCap: 'Chapter',
+ groupNounCap: 'Volume',
+ nextLabel: 'Reading next',
+ planStatus: 'Plan to Read',
+ };
+ }
+
+ const title = this.dataManager.getTitle(entry.titleId);
+ if (!title) return null;
+ const typeDef = this.plugin.settings.types.find((t) => t.name === title.type);
+ const colored = this.plugin.settings.coloredTypeBadges;
+ return {
+ source: 'watchlist',
+ id: title.id,
+ title: title.title,
+ typeName: title.type,
+ typeColor: colored && typeDef
+ ? getThemedColor(title.type, typeDef.color, this.plugin.settings.colorTheme)
+ : null,
+ externalLink: title.externalLink,
+ isSingle: title.totalEpisodes <= 1,
+ totalUnits: entry.totalEpisodes ?? title.totalEpisodes,
+ unitNoun: 'episode',
+ unitNounCap: 'Episode',
+ groupNounCap: 'Season',
+ nextLabel: 'Airing next',
+ planStatus: 'Plan to watch',
+ };
+ }
+
/** Returns the count of Due entries in the Maybe list — usable without rendering the tab. */
static getMaybeDueCount(dataManager: DataManager): number {
const today = new Date();
@@ -173,15 +259,29 @@ export class AirtimeTab {
}
/** Returns the count of Aired/Due entries — usable without rendering the tab. */
- static getAiredDueCount(dataManager: DataManager): number {
+ static getAiredDueCount(dataManager: DataManager, readingDataManager?: ReadingDataManager): number {
const allEntries = dataManager.getAirtimeEntries();
const titles = dataManager.getTitles();
+ const titleMap = new Map(titles.map((t) => [t.id, t]));
let count = 0;
for (const entry of allEntries) {
- const title = titles.find((t) => t.id === entry.titleId);
- if (!title) continue;
- const isMovie = title.totalEpisodes <= 1;
- const cd = getDetailedCountdown(entry.schedule, isMovie, entry.lastAcknowledgedDate);
+ let isSingle: boolean;
+ if (entry.source === 'reading') {
+ // Verify the reading item still exists (skip orphans) when we have the manager.
+ if (readingDataManager) {
+ const kind = entry.readingKind ?? 'book';
+ const item = kind === 'book'
+ ? readingDataManager.getBook(entry.titleId)
+ : readingDataManager.getManga(entry.titleId);
+ if (!item) continue;
+ }
+ isSingle = (entry.totalEpisodes ?? 0) <= 1;
+ } else {
+ const title = titleMap.get(entry.titleId);
+ if (!title) continue;
+ isSingle = title.totalEpisodes <= 1;
+ }
+ const cd = getDetailedCountdown(entry.schedule, isSingle, entry.lastAcknowledgedDate);
if (cd.kind === 'aired' || cd.kind === 'due') count++;
}
return count;
@@ -191,7 +291,7 @@ export class AirtimeTab {
this.container.empty();
this.container.addClass('wl-airtime');
if (this.onCountChange) {
- this.onCountChange(AirtimeTab.getAiredDueCount(this.dataManager) + AirtimeTab.getMaybeDueCount(this.dataManager));
+ this.onCountChange(AirtimeTab.getAiredDueCount(this.dataManager, this.readingDataManager) + AirtimeTab.getMaybeDueCount(this.dataManager));
}
this.renderInnerTabBar();
if (this.currentSubTab === 'tracker') {
@@ -205,11 +305,11 @@ export class AirtimeTab {
private renderInnerTabBar(): void {
const bar = this.container.createDiv({ cls: 'wl-inner-tab-bar' });
- const trackerCount = AirtimeTab.getAiredDueCount(this.dataManager);
+ const trackerCount = AirtimeTab.getAiredDueCount(this.dataManager, this.readingDataManager);
const maybeCount = AirtimeTab.getMaybeDueCount(this.dataManager);
const tabs: Array<{ key: 'tracker' | 'history' | 'maybe'; label: string; badge: number }> = [
{ key: 'tracker', label: 'Tracker', badge: trackerCount },
- { key: 'history', label: 'History', badge: 0 },
+ { key: 'history', label: 'Log', badge: 0 },
{ key: 'maybe', label: 'Maybe', badge: maybeCount },
];
for (const { key, label, badge } of tabs) {
@@ -230,11 +330,12 @@ export class AirtimeTab {
}
private renderTracker(): void {
- // Notice banner
- this.container.createDiv({
- cls: 'wl-cl-draft-banner',
- text: '⚠ All titles with a release date in the future will be automatically marked as "To be released" in Watchlist and added here.',
- });
+ if (this.plugin.settings.showHintBanners) {
+ this.container.createDiv({
+ cls: 'wl-cl-draft-banner',
+ text: '⚠ All titles with a release date in the future will be automatically marked as "To be released" in Watchlist and added here.',
+ });
+ }
this.renderHeader();
this.renderSearch();
this.renderCards();
@@ -279,21 +380,20 @@ export class AirtimeTab {
metaRow.createSpan({ cls: 'wl-airtime-schedule', text: title.releaseDate });
}
- const right = card.createDiv({ cls: 'wl-airtime-card-right' });
+ const right = card.createDiv({ cls: 'wl-airtime-card-right wl-airtime-history-right' });
if (title.releaseDate) {
right.createDiv({ cls: 'wl-airtime-pill wl-airtime-pill-aired', text: formatDaysAgo(title.releaseDate) });
}
- const actions = right.createDiv({ cls: 'wl-airtime-actions' });
- const globeBtn = actions.createEl('button', { cls: 'wl-airtime-action-btn', text: '🌐' });
+ const globeBtn = right.createEl('button', { cls: 'wl-airtime-action-btn', text: '🌐' });
globeBtn.title = 'Open page';
globeBtn.addEventListener('click', (e) => {
e.stopPropagation();
- if (title.externalLink) window.open(title.externalLink, '_blank');
+ if (title.externalLink) activeWindow.open(title.externalLink, '_blank');
else new Notice('No external link set for this title.');
});
- const deleteBtn = actions.createEl('button', { cls: 'wl-airtime-action-btn wl-airtime-action-btn-delete', text: '✕' });
+ const deleteBtn = right.createEl('button', { cls: 'wl-airtime-action-btn wl-airtime-action-btn-delete wl-btn-danger', text: '✕' });
deleteBtn.title = 'Remove from watchlist';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
@@ -311,8 +411,10 @@ export class AirtimeTab {
headerEl.createSpan({ cls: 'wl-list-count', text: String(maybeTitles.length) });
}
const controls = headerEl.createDiv({ cls: 'wl-header-controls' });
- const addBtnWrap = controls.createDiv({ cls: 'wl-add-btn-wrap' });
- const addBtn = addBtnWrap.createEl('button', { cls: 'wl-add-btn', text: '+ add' });
+ // Add pinned to the far right of the toolbar row
+ const rightGroup = controls.createDiv({ cls: 'wl-header-controls-right' });
+ const addBtnWrap = rightGroup.createDiv({ cls: 'wl-add-btn-wrap' });
+ const addBtn = addBtnWrap.createEl('button', { cls: 'wl-add-btn wl-btn-success', text: '+ add' });
addBtn.addEventListener('click', (e) => {
e.stopPropagation();
new MaybeAddModal(this.plugin.app, this.plugin, this.dataManager, () => this.render()).open();
@@ -376,10 +478,10 @@ export class AirtimeTab {
globeBtn.title = 'Open page';
globeBtn.addEventListener('click', (e) => {
e.stopPropagation();
- if (mt.externalLink) window.open(mt.externalLink, '_blank');
+ if (mt.externalLink) activeWindow.open(mt.externalLink, '_blank');
else new Notice('No external link set.');
});
- const deleteBtn = right.createEl('button', { cls: 'wl-airtime-action-btn wl-airtime-action-btn-delete', text: '✕' });
+ const deleteBtn = right.createEl('button', { cls: 'wl-airtime-action-btn wl-airtime-action-btn-delete wl-btn-danger', text: '✕' });
deleteBtn.title = 'Remove from maybe';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
@@ -396,9 +498,14 @@ export class AirtimeTab {
attr: { type: 'text', placeholder: 'Search upcoming...' },
});
input.value = this.searchQuery;
+ let debounceTimer: number | null = null;
input.addEventListener('input', () => {
this.searchQuery = input.value;
- this.rerenderCards();
+ if (debounceTimer !== null) window.clearTimeout(debounceTimer);
+ debounceTimer = window.setTimeout(() => {
+ debounceTimer = null;
+ this.rerenderCards();
+ }, 250);
});
}
@@ -415,10 +522,9 @@ export class AirtimeTab {
private renderHeader(): void {
const header = this.container.createDiv({ cls: 'wl-list-header' });
- // Entry count (only entries with a valid title match)
- const allTitles = this.dataManager.getTitles();
+ // Entry count (only entries that still resolve to a watchlist or reading item)
const count = this.dataManager.getAirtimeEntries().filter(
- (e) => allTitles.some((t) => t.id === e.titleId),
+ (e) => this.resolveEntry(e) !== null,
).length;
if (count > 0) {
header.createSpan({ cls: 'wl-list-count', text: String(count) });
@@ -448,8 +554,11 @@ export class AirtimeTab {
});
}
+ // Select + add pinned to the far right of the toolbar row
+ const rightGroup = controls.createDiv({ cls: 'wl-header-controls-right' });
+
// Selection mode toggle button
- const selBtn = controls.createEl('button', {
+ const selBtn = rightGroup.createEl('button', {
cls: `wl-btn wl-btn-sm${this.selectionMode ? ' is-active' : ''}`,
text: 'Select',
});
@@ -459,8 +568,8 @@ export class AirtimeTab {
this.render();
});
- const addBtnWrap = controls.createDiv({ cls: 'wl-add-btn-wrap' });
- const addBtn = addBtnWrap.createEl('button', { cls: 'wl-add-btn', text: '+ add' });
+ const addBtnWrap = rightGroup.createDiv({ cls: 'wl-add-btn-wrap' });
+ const addBtn = addBtnWrap.createEl('button', { cls: 'wl-add-btn wl-btn-success', text: '+ add' });
addBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.openAddFlow();
@@ -472,7 +581,7 @@ export class AirtimeTab {
// Delete — removes selected entries from Upcoming
const deleteBtn = bar.createEl('button', {
- cls: 'wl-group-action-btn wl-group-action-btn-delete',
+ cls: 'wl-group-action-btn wl-group-action-btn-delete wl-btn-danger',
text: '✕',
});
deleteBtn.title = 'Remove from upcoming';
@@ -484,9 +593,7 @@ export class AirtimeTab {
`Remove ${count} selected item${count !== 1 ? 's' : ''} from Upcoming? This cannot be undone.`,
() => {
void (async () => {
- for (const id of Array.from(this.selectedItems)) {
- await this.dataManager.removeAirtimeEntry(id);
- }
+ await this.dataManager.removeAirtimeEntriesBatch(Array.from(this.selectedItems));
this.selectedItems.clear();
this.render();
})();
@@ -498,26 +605,142 @@ export class AirtimeTab {
// ── Add flow ──────────────────────────────────────────────────────────────────
private openAddFlow(): void {
- const titles = this.dataManager.getTitles();
- if (titles.length === 0) {
- new Notice('No titles in your watchlog library yet.');
- return;
- }
-
const entries = this.dataManager.getAirtimeEntries();
- const alreadyAdded = new Set(entries.map((e) => e.titleId));
- const available = titles.filter((t) => !alreadyAdded.has(t.id));
+ // Already-added keys, kept separate per source so a watchlist id and a
+ // reading id that happen to match don't shadow each other.
+ const addedWatch = new Set(entries.filter((e) => e.source !== 'reading').map((e) => e.titleId));
+ const addedReading = new Set(entries.filter((e) => e.source === 'reading').map((e) => e.titleId));
- if (available.length === 0) {
- new Notice('All titles are already in upcoming.');
+ const items: UpcomingFinderItem[] = [];
+ for (const t of this.dataManager.getTitles()) {
+ if (addedWatch.has(t.id)) continue;
+ items.push({ source: 'watchlist', id: t.id, title: t.title, typeLabel: t.type });
+ }
+ for (const b of this.readingDataManager.getBooks()) {
+ if (addedReading.has(b.id)) continue;
+ items.push({ source: 'reading', kind: 'book', id: b.id, title: b.title, typeLabel: 'Book' });
+ }
+ for (const m of this.readingDataManager.getMangaList()) {
+ if (addedReading.has(m.id)) continue;
+ items.push({ source: 'reading', kind: 'manga', id: m.id, title: m.title, typeLabel: 'Manga' });
+ }
+
+ if (this.dataManager.getTitles().length === 0 &&
+ this.readingDataManager.getBooks().length === 0 &&
+ this.readingDataManager.getMangaList().length === 0) {
+ new Notice('No titles in your watchlog or reading library yet.');
+ return;
+ }
+ if (items.length === 0) {
+ new Notice('Everything is already in upcoming.');
return;
}
- new InsertWidgetModal(this.plugin.app, available, (title) => {
- void this.startAddWithTitle(title);
+ new UpcomingFinderModal(this.plugin.app, items, (item) => {
+ if (item.source === 'reading') {
+ const kind = item.kind ?? 'book';
+ const readItem = kind === 'book'
+ ? this.readingDataManager.getBook(item.id)
+ : this.readingDataManager.getManga(item.id);
+ if (readItem) this.startAddWithReading(readItem, kind);
+ } else {
+ const title = this.dataManager.getTitle(item.id);
+ if (title) void this.startAddWithTitle(title);
+ }
}).open();
}
+ private startAddWithReading(item: Book | Manga, kind: 'book' | 'manga'): void {
+ const prefilled: AirtimeSchedule | null =
+ item.releaseDate && /^\d{4}-\d{2}-\d{2}$/.test(item.releaseDate)
+ ? { recurrence: 'once', releaseDate: item.releaseDate }
+ : null;
+
+ new ReadingScheduleModal(
+ this.plugin.app,
+ item,
+ kind,
+ prefilled,
+ null,
+ null,
+ null,
+ null,
+ async (schedule, volume, chapter, totalVolumes, totalChapters) => {
+ const entry: AirtimeEntry = {
+ id: this.dataManager.generateReadingAirtimeId(item.id),
+ titleId: item.id,
+ source: 'reading',
+ readingKind: kind,
+ schedule,
+ currentSeason: volume ?? undefined,
+ currentEpisode: chapter ?? undefined,
+ totalSeasons: totalVolumes ?? undefined,
+ totalEpisodes: totalChapters ?? undefined,
+ dateAdded: new Date().toISOString(),
+ };
+ await this.dataManager.addAirtimeEntry(entry);
+ await this.syncReadingItemFromSchedule(item.id, kind, schedule, totalVolumes, totalChapters);
+ this.render();
+ },
+ ).open();
+ }
+
+ /** Pushes schedule-derived totals / release date back onto the reading item. */
+ private async syncReadingItemFromSchedule(
+ itemId: string,
+ kind: 'book' | 'manga',
+ schedule: AirtimeSchedule,
+ totalVolumes: number | null,
+ totalChapters: number | null,
+ ): Promise {
+ if (kind === 'book') {
+ const b = this.readingDataManager.getBook(itemId);
+ if (!b) return;
+ let changed = false;
+ if (totalChapters !== null && totalChapters !== b.totalChapters) { b.totalChapters = totalChapters; changed = true; }
+ // Only write a future 'once' date back (drives auto "To be released"); a
+ // past/due date is left alone so the just-added entry isn't auto-removed.
+ if (schedule.recurrence === 'once' && isReleaseDateFuture(schedule.releaseDate)) {
+ const newDate = schedule.releaseDate ?? null;
+ if (b.releaseDate !== newDate) { b.releaseDate = newDate; changed = true; }
+ }
+ if (changed) await this.readingDataManager.updateBook(b);
+ } else {
+ const m = this.readingDataManager.getManga(itemId);
+ if (!m) return;
+ let changed = false;
+ if (totalChapters !== null && totalChapters !== m.totalChapters) { m.totalChapters = totalChapters; changed = true; }
+ if (totalVolumes !== null && totalVolumes !== m.totalVolumes) { m.totalVolumes = totalVolumes; changed = true; }
+ // Only write a future 'once' date back (drives auto "To be released"); a
+ // past/due date is left alone so the just-added entry isn't auto-removed.
+ if (schedule.recurrence === 'once' && isReleaseDateFuture(schedule.releaseDate)) {
+ const newDate = schedule.releaseDate ?? null;
+ if (m.releaseDate !== newDate) { m.releaseDate = newDate; changed = true; }
+ }
+ if (changed) await this.readingDataManager.updateManga(m);
+ }
+ }
+
+ /**
+ * On a single-release tick, revert the underlying item's auto "To be released"
+ * status back to its plan status (watchlist title or reading item).
+ */
+ private async revertToBeReleasedStatus(entry: AirtimeEntry, r: ResolvedUpcoming): Promise {
+ if (r.source === 'reading') {
+ const kind = entry.readingKind ?? 'book';
+ if (kind === 'book') {
+ const b = this.readingDataManager.getBook(entry.titleId);
+ if (b && b.status === 'To be released') { b.status = 'Plan to Read'; await this.readingDataManager.updateBook(b); }
+ } else {
+ const m = this.readingDataManager.getManga(entry.titleId);
+ if (m && m.status === 'To be released') { m.status = 'Plan to Read'; await this.readingDataManager.updateManga(m); }
+ }
+ } else {
+ const t = this.dataManager.getTitle(entry.titleId);
+ if (t && t.status === 'To be released') { t.status = 'Plan to watch'; await this.dataManager.updateTitle(t); }
+ }
+ }
+
private async startAddWithTitle(title: WatchLogTitle): Promise {
let prefilled: AirtimeSchedule | null = null;
@@ -530,7 +753,7 @@ export class AirtimeTab {
new Notice('Schedule auto-filled from myanimelist.');
}
} catch {
- // ignore — user fills manually
+ // ignore — fields stay empty for manual entry
}
}
@@ -568,7 +791,7 @@ export class AirtimeTab {
t.totalEpisodes = totalEpisodes;
changed = true;
}
- // Bug 2: sync releaseDate when schedule is 'once'
+ // Sync releaseDate when schedule is 'once'
if (schedule.recurrence === 'once') {
const newDate = schedule.releaseDate ?? null;
if (t.releaseDate !== newDate) {
@@ -597,20 +820,18 @@ export class AirtimeTab {
return;
}
- const titles = this.dataManager.getTitles();
const q = this.searchQuery.trim().toLowerCase();
const cardData = allEntries
.map((entry) => {
- const title = titles.find((t) => t.id === entry.titleId);
- if (!title) return null;
- if (q && !title.title.toLowerCase().includes(q)) return null;
- const isMovie = title.totalEpisodes <= 1;
- const countdown = getDetailedCountdown(entry.schedule, isMovie, entry.lastAcknowledgedDate);
- return { entry, title, countdown };
+ const r = this.resolveEntry(entry);
+ if (!r) return null;
+ if (q && !r.title.toLowerCase().includes(q)) return null;
+ const countdown = getDetailedCountdown(entry.schedule, r.isSingle, entry.lastAcknowledgedDate);
+ return { entry, r, countdown };
})
.filter(
- (d): d is { entry: AirtimeEntry; title: WatchLogTitle; countdown: DetailedCountdown } =>
+ (d): d is { entry: AirtimeEntry; r: ResolvedUpcoming; countdown: DetailedCountdown } =>
d !== null,
)
.sort((a, b) => {
@@ -629,8 +850,8 @@ export class AirtimeTab {
}
const cardsEl = this.container.createDiv({ cls: 'wl-airtime-cards' });
- for (const { entry, title, countdown } of cardData) {
- this.renderCard(cardsEl, entry, title, countdown);
+ for (const { entry, r, countdown } of cardData) {
+ this.renderCard(cardsEl, entry, r, countdown);
}
}
@@ -639,11 +860,11 @@ export class AirtimeTab {
private renderCard(
parent: HTMLElement,
entry: AirtimeEntry,
- title: WatchLogTitle,
+ r: ResolvedUpcoming,
countdown: DetailedCountdown,
): void {
- const isMovie = title.totalEpisodes <= 1;
- const totalEps = entry.totalEpisodes ?? title.totalEpisodes;
+ const isMovie = r.isSingle;
+ const totalEps = r.totalUnits;
const isFinalEpisode =
!isMovie &&
entry.currentEpisode !== undefined &&
@@ -686,25 +907,24 @@ export class AirtimeTab {
// ── Left ──────────────────────────────────────────────────────────────────
const left = card.createDiv({ cls: 'wl-airtime-card-left' });
- left.createDiv({ cls: 'wl-airtime-card-title', text: title.title });
+ left.createDiv({ cls: 'wl-airtime-card-title', text: r.title });
- // Type badge + schedule string (above episode badge per spec)
+ // Type badge + schedule string, above the episode badge. Reading entries
+ // render a plain (theme-driven) badge; watch entries may be colored.
const metaRow = left.createDiv({ cls: 'wl-airtime-card-meta' });
- const typeDef = this.plugin.settings.types.find((t) => t.name === title.type);
- const colored = this.plugin.settings.coloredTypeBadges;
const typeBadge = metaRow.createSpan({
- cls: colored ? 'wl-badge wl-badge-sm' : 'wl-badge-plain',
- text: title.type,
+ cls: r.typeColor ? 'wl-badge wl-badge-sm' : 'wl-badge-plain',
+ text: r.typeName,
});
- if (colored && typeDef) typeBadge.style.backgroundColor = getThemedColor(title.type, typeDef.color, this.plugin.settings.colorTheme);
+ if (r.typeColor) typeBadge.style.backgroundColor = r.typeColor;
metaRow.createSpan({ cls: 'wl-airtime-schedule', text: getAirtimeScheduleString(entry.schedule) });
- // Episode badge — only for series/anime, positioned below meta row
+ // Progress badge — only for multi-part titles (series / manga), below meta row
if (!isMovie && (entry.currentSeason !== undefined || entry.currentEpisode !== undefined)) {
const badgeParts: string[] = [];
- if (entry.currentSeason !== undefined) badgeParts.push(`Season ${entry.currentSeason}`);
- if (entry.currentEpisode !== undefined) badgeParts.push(`Episode ${entry.currentEpisode}`);
- badgeParts.push('Airing next');
+ if (entry.currentSeason !== undefined) badgeParts.push(`${r.groupNounCap} ${entry.currentSeason}`);
+ if (entry.currentEpisode !== undefined) badgeParts.push(`${r.unitNounCap} ${entry.currentEpisode}`);
+ badgeParts.push(r.nextLabel);
const badgeCls = isFinalEpisode
? 'wl-ep-badge wl-ep-badge-final'
: 'wl-ep-badge';
@@ -737,14 +957,14 @@ export class AirtimeTab {
cls: 'wl-airtime-action-btn wl-airtime-action-btn-tick',
text: '✓',
});
- tickBtn.title = isMovie ? 'Mark as released' : (isFinalEpisode ? 'Final episode — mark done' : 'Mark episode as aired');
+ tickBtn.title = isMovie ? 'Mark as released' : (isFinalEpisode ? `Final ${r.unitNoun} — mark done` : `Mark ${r.unitNoun} as aired`);
tickBtn.addEventListener('click', (e) => {
e.stopPropagation();
const confirmMsg = isMovie
- ? `"${title.title}" has been released.\nRemove from Upcoming and set status to Plan to watch?`
+ ? `"${r.title}" has been released.\nRemove from Upcoming and set status to ${r.planStatus}?`
: isFinalEpisode
- ? `Final episode of "${title.title}" aired.\nRemove from Upcoming? (Watchlist status unchanged)`
- : `Episode ${entry.currentEpisode ?? ''} of "${title.title}" aired.\nMark and track next episode?`;
+ ? `Final ${r.unitNoun} of "${r.title}".\nRemove from Upcoming? (status unchanged)`
+ : `${r.unitNounCap} ${entry.currentEpisode ?? ''} of "${r.title}".\nMark and track next ${r.unitNoun}?`;
new ConfirmModal(this.plugin.app, confirmMsg, () => {
void (async () => {
@@ -755,17 +975,13 @@ export class AirtimeTab {
if (isMovie) {
await this.dataManager.removeAirtimeEntry(entry.id);
- const t = this.dataManager.getTitle(title.id);
- if (t && t.status === 'To be released') {
- t.status = 'Plan to watch';
- await this.dataManager.updateTitle(t);
- }
+ await this.revertToBeReleasedStatus(entry, r);
} else if (isFinalEpisode) {
- // Final episode: remove from Upcoming, do NOT change Watchlist status
+ // Final unit: remove from Upcoming, do NOT change status
await this.dataManager.removeAirtimeEntry(entry.id);
} else {
- // Non-final episode: increment episode and record acknowledgement date
- // so the countdown resets to the next occurrence immediately.
+ // Non-final: increment the unit (episode/chapter) and record
+ // the acknowledgement date so the countdown resets immediately.
entry.currentEpisode = (entry.currentEpisode ?? 1) + 1;
entry.lastAcknowledgedDate = todayStr;
await this.dataManager.updateAirtimeEntry(entry);
@@ -781,71 +997,108 @@ export class AirtimeTab {
globeBtn.title = 'Open page';
globeBtn.addEventListener('click', (e) => {
e.stopPropagation();
- if (title.externalLink) {
- window.open(title.externalLink, '_blank');
+ if (r.externalLink) {
+ activeWindow.open(r.externalLink, '_blank');
} else {
new Notice('No external link set for this title.');
}
});
- // Edit — open schedule modal
+ // Edit — open the schedule modal matching the entry's source
const editBtn = actions.createEl('button', { cls: 'wl-airtime-action-btn', text: '✏' });
editBtn.title = 'Edit schedule';
editBtn.addEventListener('click', (e) => {
e.stopPropagation();
- new AirtimeScheduleModal(
- this.plugin.app,
- title,
- { ...entry.schedule },
- entry.currentSeason ?? null,
- entry.currentEpisode ?? null,
- entry.totalSeasons ?? null,
- entry.totalEpisodes ?? null,
- async (schedule, season, episode, totalSeasons, totalEpisodes) => {
- entry.schedule = schedule;
- entry.currentSeason = season ?? undefined;
- entry.currentEpisode = episode ?? undefined;
- entry.totalSeasons = totalSeasons ?? undefined;
- entry.totalEpisodes = totalEpisodes ?? undefined;
- await this.dataManager.updateAirtimeEntry(entry);
-
- // Sync back to Watchlist title
- const currentTitle = this.dataManager.getTitle(entry.titleId);
- if (currentTitle) {
- let changed = false;
- if (totalEpisodes !== null && totalEpisodes !== currentTitle.totalEpisodes) {
- currentTitle.totalEpisodes = totalEpisodes;
- changed = true;
- }
- // Bug 2: sync releaseDate when schedule is 'once'
- if (schedule.recurrence === 'once') {
- const newDate = schedule.releaseDate ?? null;
- if (currentTitle.releaseDate !== newDate) {
- currentTitle.releaseDate = newDate;
- changed = true;
- }
- }
- if (changed) await this.dataManager.updateTitle(currentTitle);
- }
-
- this.render();
- },
- ).open();
+ if (r.source === 'reading') {
+ this.openEditReadingSchedule(entry);
+ } else {
+ this.openEditWatchSchedule(entry);
+ }
});
- // Delete — with confirmation (Feature 1)
+ // Delete — with confirmation
const deleteBtn = actions.createEl('button', {
- cls: 'wl-airtime-action-btn wl-airtime-action-btn-delete',
+ cls: 'wl-airtime-action-btn wl-airtime-action-btn-delete wl-btn-danger',
text: '✕',
});
deleteBtn.title = 'Remove from upcoming';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
- new ConfirmModal(this.plugin.app, `Remove "${title.title}" from Upcoming?`, () => {
+ new ConfirmModal(this.plugin.app, `Remove "${r.title}" from Upcoming?`, () => {
void this.dataManager.removeAirtimeEntry(entry.id).then(() => this.render());
}).open();
});
}
+
+ private openEditWatchSchedule(entry: AirtimeEntry): void {
+ const title = this.dataManager.getTitle(entry.titleId);
+ if (!title) return;
+ new AirtimeScheduleModal(
+ this.plugin.app,
+ title,
+ { ...entry.schedule },
+ entry.currentSeason ?? null,
+ entry.currentEpisode ?? null,
+ entry.totalSeasons ?? null,
+ entry.totalEpisodes ?? null,
+ async (schedule, season, episode, totalSeasons, totalEpisodes) => {
+ entry.schedule = schedule;
+ entry.currentSeason = season ?? undefined;
+ entry.currentEpisode = episode ?? undefined;
+ entry.totalSeasons = totalSeasons ?? undefined;
+ entry.totalEpisodes = totalEpisodes ?? undefined;
+ await this.dataManager.updateAirtimeEntry(entry);
+
+ // Sync back to Watchlist title
+ const currentTitle = this.dataManager.getTitle(entry.titleId);
+ if (currentTitle) {
+ let changed = false;
+ if (totalEpisodes !== null && totalEpisodes !== currentTitle.totalEpisodes) {
+ currentTitle.totalEpisodes = totalEpisodes;
+ changed = true;
+ }
+ // Sync releaseDate when schedule is 'once'
+ if (schedule.recurrence === 'once') {
+ const newDate = schedule.releaseDate ?? null;
+ if (currentTitle.releaseDate !== newDate) {
+ currentTitle.releaseDate = newDate;
+ changed = true;
+ }
+ }
+ if (changed) await this.dataManager.updateTitle(currentTitle);
+ }
+
+ this.render();
+ },
+ ).open();
+ }
+
+ private openEditReadingSchedule(entry: AirtimeEntry): void {
+ const kind = entry.readingKind ?? 'book';
+ const item: Book | Manga | undefined =
+ kind === 'book' ? this.readingDataManager.getBook(entry.titleId) : this.readingDataManager.getManga(entry.titleId);
+ if (!item) return;
+ new ReadingScheduleModal(
+ this.plugin.app,
+ item,
+ kind,
+ { ...entry.schedule },
+ entry.currentSeason ?? null, // volume
+ entry.currentEpisode ?? null, // chapter
+ entry.totalSeasons ?? null, // total volumes
+ entry.totalEpisodes ?? null, // total chapters
+ async (schedule, volume, chapter, totalVolumes, totalChapters) => {
+ entry.schedule = schedule;
+ entry.currentSeason = volume ?? undefined;
+ entry.currentEpisode = chapter ?? undefined;
+ entry.totalSeasons = totalVolumes ?? undefined;
+ entry.totalEpisodes = totalChapters ?? undefined;
+ await this.dataManager.updateAirtimeEntry(entry);
+ await this.syncReadingItemFromSchedule(entry.titleId, kind, schedule, totalVolumes, totalChapters);
+ this.render();
+ },
+ ).open();
+ }
}
// ── AirtimeScheduleModal ──────────────────────────────────────────────────────
@@ -887,7 +1140,7 @@ class AirtimeScheduleModal extends Modal {
this.schedule = existingSchedule
? { ...existingSchedule }
: { recurrence: isMovie ? 'once' : 'weekly' };
- // Fix 9: pre-fill releaseDate from title if the schedule doesn't have one yet
+ // Pre-fill releaseDate from title if the schedule doesn't have one yet
if (!this.schedule.releaseDate && title.releaseDate && /^\d{4}-\d{2}-\d{2}$/.test(title.releaseDate)) {
this.schedule.releaseDate = title.releaseDate;
}
@@ -979,11 +1232,11 @@ class AirtimeScheduleModal extends Modal {
? this.schedule.releaseDate.split('-').reverse().join('/')
: '';
dateInput.addEventListener('change', () => {
- const parts = dateInput.value.split('/');
- if (parts.length === 3) {
- this.schedule.recurrence = 'once';
- this.schedule.releaseDate = `${parts[2]}-${parts[1]}-${parts[0]}`;
- }
+ const parsed = parseDateInput(dateInput.value);
+ if (parsed) {
+ this.schedule.recurrence = 'once';
+ this.schedule.releaseDate = parsed;
+ }
});
makeTimeRow('Time (optional)', content);
@@ -1019,10 +1272,10 @@ class AirtimeScheduleModal extends Modal {
? this.schedule.releaseDate.split('-').reverse().join('/')
: '';
inp.addEventListener('change', () => {
- const parts = inp.value.split('/');
- if (parts.length === 3) {
- this.schedule.releaseDate = `${parts[2]}-${parts[1]}-${parts[0]}`;
- }
+ const parsed = parseDateInput(inp.value);
+ if (parsed) {
+ this.schedule.releaseDate = parsed;
+ }
});
}
@@ -1078,7 +1331,7 @@ class AirtimeScheduleModal extends Modal {
if (this.currentEpisode !== null) epInput.value = String(this.currentEpisode);
epInput.addEventListener('input', () => { this.currentEpisode = parseInt(epInput.value) || null; });
- // ── Feature 4: Total seasons + total episodes ────────────────────────────
+ // ── Total seasons + total episodes ───────────────────────────────────────
const totSeasRow = makeRow('Total seasons');
const totSeasInput = totSeasRow.createEl('input', {
cls: 'wl-modal-input wl-modal-input-sm',
@@ -1095,7 +1348,7 @@ class AirtimeScheduleModal extends Modal {
if (this.totalEpisodes !== null) totEpInput.value = String(this.totalEpisodes);
totEpInput.addEventListener('input', () => { this.totalEpisodes = parseInt(totEpInput.value) || null; });
- // Static hint note (always visible per spec)
+ // Static hint note (always visible)
content.createDiv({
cls: 'wl-modal-info wl-schedule-hint',
text: 'Titles with 0 or 1 total episodes will be treated as a single release date, like a movie.',
diff --git a/src/ApiService.ts b/src/ApiService.ts
index a407fbf..6924142 100644
--- a/src/ApiService.ts
+++ b/src/ApiService.ts
@@ -2,6 +2,7 @@ import { requestUrl } from 'obsidian';
import type {
AnimeSearchResult,
MediaSearchResult,
+ WatchLogTitle,
JikanAnime,
OmdbSearchResponse,
OmdbDetailResponse,
@@ -12,20 +13,143 @@ import type {
TmdbExternalIds,
TmdbFindResult,
Season,
+ AniListMedia,
+ AniListSearchResponse,
+ AniListMediaResponse,
} from './types';
const JIKAN_BASE = 'https://api.jikan.moe/v4';
const OMDB_BASE = 'https://www.omdbapi.com';
const TMDB_BASE = 'https://api.themoviedb.org/3';
+const ANILIST_ENDPOINT = 'https://graphql.anilist.co';
+const ANILIST_RATE_LIMIT_MS = 700;
+const GOOGLE_BOOKS_BASE = 'https://www.googleapis.com/books/v1';
+const JIKAN_RATE_LIMIT_MS = 400;
const API_TIMEOUT_MS = 8000;
+export interface BookSearchResult {
+ title: string;
+ author: string;
+ year: number;
+ totalPages: number;
+ coverUrl: string;
+ googleBooksId: string;
+ releaseDate: string;
+ url: string;
+}
+
+export interface MangaSearchResult {
+ malId: number;
+ title: string;
+ author: string;
+ year: number;
+ totalChapters: number;
+ totalVolumes: number;
+ coverUrl: string;
+ releaseDate: string;
+ url: string;
+}
+
+interface GoogleVolumeInfo {
+ title?: string;
+ subtitle?: string;
+ authors?: string[];
+ publishedDate?: string;
+ pageCount?: number;
+ imageLinks?: {
+ smallThumbnail?: string;
+ thumbnail?: string;
+ small?: string;
+ medium?: string;
+ large?: string;
+ extraLarge?: string;
+ };
+ infoLink?: string;
+ canonicalVolumeLink?: string;
+}
+
+interface GoogleVolume {
+ id?: string;
+ volumeInfo?: GoogleVolumeInfo;
+}
+
+interface GoogleBooksResponse {
+ items?: GoogleVolume[];
+}
+
+interface JikanMangaItem {
+ mal_id: number;
+ title: string;
+ title_english?: string | null;
+ authors?: Array<{ name: string }>;
+ chapters?: number | null;
+ volumes?: number | null;
+ published?: { from?: string | null } | null;
+ images?: { jpg?: { image_url?: string | null } | null } | null;
+ url?: string | null;
+}
+
+/** Why a Google Books request failed — used to surface accurate, non-misleading UI messages. */
+export type GoogleBooksErrorReason = 'no-key' | 'rate-limited' | 'http' | 'parse' | 'network';
+
+export class GoogleBooksError extends Error {
+ reason: GoogleBooksErrorReason;
+ status?: number;
+ constructor(reason: GoogleBooksErrorReason, message: string, status?: number) {
+ super(message);
+ this.name = 'GoogleBooksError';
+ this.reason = reason;
+ this.status = status;
+ }
+}
+
+/** Maps any error from a Google Books call to a short, user-facing explanation. */
+export function googleBooksErrorMessage(err: unknown): string {
+ if (err instanceof GoogleBooksError) {
+ switch (err.reason) {
+ case 'no-key':
+ return 'Google Books API key required — add one in Settings → API → Books.';
+ case 'rate-limited':
+ return 'Google Books rate limit reached — your API key is missing or over quota.';
+ case 'http':
+ return `Google Books request failed (HTTP ${err.status ?? '?'}).`;
+ case 'parse':
+ return 'Google Books returned an unreadable response.';
+ default:
+ return 'Google Books request failed — check your connection.';
+ }
+ }
+ return 'Google Books request failed — check your connection.';
+}
+
export class ApiService {
private omdbApiKey: string;
private tmdbApiKey: string;
+ private googleBooksApiKey: string;
+ private anilistLastRequest = 0;
+ private anilistQueue: Promise = Promise.resolve();
+ private jikanLastRequest = 0;
+ private jikanQueue: Promise = Promise.resolve();
- constructor(omdbApiKey: string, tmdbApiKey = '') {
+ private throttleJikan(task: () => Promise): Promise {
+ const run = async (): Promise => {
+ const now = Date.now();
+ const gap = now - this.jikanLastRequest;
+ if (gap < JIKAN_RATE_LIMIT_MS) {
+ await new Promise((r) => window.setTimeout(r, JIKAN_RATE_LIMIT_MS - gap));
+ }
+ this.jikanLastRequest = Date.now();
+ return task();
+ };
+ const next = this.jikanQueue.then(run, run);
+ this.jikanQueue = next.then(() => undefined, () => undefined);
+ return next;
+ }
+
+ constructor(omdbApiKey: string, tmdbApiKey = '', googleBooksApiKey = '') {
this.omdbApiKey = omdbApiKey;
this.tmdbApiKey = tmdbApiKey;
+ this.googleBooksApiKey = googleBooksApiKey;
}
setOmdbKey(key: string): void {
@@ -36,16 +160,178 @@ export class ApiService {
this.tmdbApiKey = key;
}
+ setGoogleBooksKey(key: string): void {
+ this.googleBooksApiKey = key;
+ }
+
+ hasGoogleBooksKey(): boolean {
+ return this.googleBooksApiKey.trim().length > 0;
+ }
+
private tmdbHeaders(): Record {
return { 'Authorization': `Bearer ${this.tmdbApiKey}` };
}
private async fetchWithTimeout(url: string, headers?: Record): Promise {
- const timeoutPromise = new Promise((_, reject) =>
- window.setTimeout(() => reject(new Error('Request timed out')), API_TIMEOUT_MS),
- );
- const fetchPromise = requestUrl({ url, headers }).then((r) => r.json as unknown);
- return Promise.race([fetchPromise, timeoutPromise]);
+ let timer: number | null = null;
+ const timeoutPromise = new Promise((_, reject) => {
+ timer = window.setTimeout(() => reject(new Error('Request timed out')), API_TIMEOUT_MS);
+ });
+ try {
+ const fetchPromise = requestUrl({ url, headers }).then((r) => r.json as unknown);
+ return await Promise.race([fetchPromise, timeoutPromise]);
+ } finally {
+ if (timer !== null) window.clearTimeout(timer);
+ }
+ }
+
+ private async postJsonWithTimeout(
+ url: string,
+ body: unknown,
+ headers?: Record,
+ ): Promise {
+ let timer: number | null = null;
+ const timeoutPromise = new Promise((_, reject) => {
+ timer = window.setTimeout(() => reject(new Error('Request timed out')), API_TIMEOUT_MS);
+ });
+ try {
+ const fetchPromise = requestUrl({
+ url,
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json', ...(headers ?? {}) },
+ body: JSON.stringify(body),
+ }).then((r) => r.json as unknown);
+ return await Promise.race([fetchPromise, timeoutPromise]);
+ } finally {
+ if (timer !== null) window.clearTimeout(timer);
+ }
+ }
+
+ // ─── AniList (GraphQL) ───────────────────────────────────────────────────────
+
+ /** Serializes AniList requests with a minimum gap of ANILIST_RATE_LIMIT_MS between them. */
+ private throttleAniList(task: () => Promise): Promise {
+ const run = async (): Promise => {
+ const now = Date.now();
+ const gap = now - this.anilistLastRequest;
+ if (gap < ANILIST_RATE_LIMIT_MS) {
+ await new Promise((r) => window.setTimeout(r, ANILIST_RATE_LIMIT_MS - gap));
+ }
+ this.anilistLastRequest = Date.now();
+ return task();
+ };
+ const next = this.anilistQueue.then(run, run);
+ this.anilistQueue = next.then(() => undefined, () => undefined);
+ return next;
+ }
+
+ private stripHtml(s: string): string {
+ return s
+ .replace(/
/gi, '\n')
+ .replace(/<[^>]+>/g, '')
+ .replace(/ /g, ' ')
+ .replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(/'/g, "'")
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .trim();
+ }
+
+ private formatAniListDate(d: { year?: number | null; month?: number | null; day?: number | null } | null | undefined): string {
+ if (!d?.year) return '';
+ const yyyy = String(d.year);
+ const mm = String(d.month ?? 1).padStart(2, '0');
+ const dd = String(d.day ?? 1).padStart(2, '0');
+ return `${yyyy}-${mm}-${dd}`;
+ }
+
+ private mapAniListMedia(media: AniListMedia): AnimeSearchResult {
+ const name = media.title?.english || media.title?.romaji || media.title?.native || '';
+ const episodes = media.episodes ?? 0;
+ const duration = media.duration ?? 24;
+ const seasons: Season[] =
+ episodes > 0 ? [{ name: 'Season 1', episodes, offset: 0 }] : [];
+ const description = media.description ? this.stripHtml(media.description) : '';
+ return {
+ malId: 0,
+ anilistId: media.id,
+ title: name,
+ episodes,
+ duration,
+ releaseDate: this.formatAniListDate(media.startDate),
+ url: `https://anilist.co/anime/${media.id}`,
+ seasons,
+ description,
+ averageScore: media.averageScore ?? undefined,
+ genres: media.genres ?? undefined,
+ posterUrl: media.coverImage?.large ?? media.coverImage?.medium ?? undefined,
+ };
+ }
+
+ async searchAniList(query: string): Promise {
+ const gql = `
+ query ($search: String) {
+ Page(page: 1, perPage: 10) {
+ media(search: $search, type: ANIME) {
+ id
+ title { romaji english native }
+ episodes
+ duration
+ status
+ season
+ seasonYear
+ startDate { year month day }
+ averageScore
+ popularity
+ coverImage { large medium }
+ description
+ genres
+ }
+ }
+ }
+ `;
+ try {
+ const res = await this.throttleAniList(() =>
+ this.postJsonWithTimeout(ANILIST_ENDPOINT, { query: gql, variables: { search: query } }),
+ );
+ const data = res as AniListSearchResponse;
+ const media = data.data?.Page?.media ?? [];
+ return media.map((m) => this.mapAniListMedia(m));
+ } catch {
+ return [];
+ }
+ }
+
+ async getAniListById(anilistId: number): Promise {
+ const gql = `
+ query ($id: Int) {
+ Media(id: $id, type: ANIME) {
+ id
+ title { romaji english native }
+ episodes
+ duration
+ status
+ averageScore
+ popularity
+ coverImage { large }
+ nextAiringEpisode {
+ airingAt
+ episode
+ timeUntilAiring
+ }
+ }
+ }
+ `;
+ try {
+ const res = await this.throttleAniList(() =>
+ this.postJsonWithTimeout(ANILIST_ENDPOINT, { query: gql, variables: { id: anilistId } }),
+ );
+ const data = res as AniListMediaResponse;
+ return data.data?.Media ?? null;
+ } catch {
+ return null;
+ }
}
// ─── Jikan / MAL ─────────────────────────────────────────────────────────────
@@ -324,6 +610,313 @@ export class ApiService {
}
}
+ // ─── Community ratings ───────────────────────────────────────────────────────
+
+ private parseOmdbVotes(s: string | undefined): number {
+ if (!s || s === 'N/A') return 0;
+ const n = parseInt(s.replace(/,/g, ''), 10);
+ return isNaN(n) ? 0 : n;
+ }
+
+ private parseOmdbRating(s: string | undefined): number {
+ if (!s || s === 'N/A') return 0;
+ const n = parseFloat(s);
+ return isNaN(n) ? 0 : n;
+ }
+
+ private extractImdbId(externalLink: string): string {
+ const m = externalLink.match(/tt\d+/);
+ return m ? m[0] : '';
+ }
+
+ private async fetchMalRating(malId: number): Promise<{ rating: number; votes: number } | null> {
+ try {
+ const url = `${JIKAN_BASE}/anime/${malId}`;
+ const data = (await this.fetchWithTimeout(url)) as {
+ data?: { score?: number | null; scored_by?: number | null };
+ };
+ const rating = data.data?.score ?? 0;
+ const votes = data.data?.scored_by ?? 0;
+ if (!rating && !votes) return null;
+ return { rating, votes };
+ } catch {
+ return null;
+ }
+ }
+
+ private async fetchAniListRating(
+ anilistId: number,
+ ): Promise<{ rating: number; votes: number } | null> {
+ const media = await this.getAniListById(anilistId);
+ if (!media) return null;
+ const rating = media.averageScore ?? 0;
+ const votes = media.popularity ?? 0;
+ if (!rating && !votes) return null;
+ return { rating, votes };
+ }
+
+ private async fetchOmdbRating(imdbId: string): Promise<{ rating: number; votes: number } | null> {
+ if (!this.omdbApiKey) return null;
+ try {
+ const url = `${OMDB_BASE}/?i=${encodeURIComponent(imdbId)}&apikey=${encodeURIComponent(this.omdbApiKey)}`;
+ const data = (await this.fetchWithTimeout(url)) as {
+ Response?: string;
+ imdbRating?: string;
+ imdbVotes?: string;
+ };
+ if (data.Response === 'False') return null;
+ const rating = this.parseOmdbRating(data.imdbRating);
+ const votes = this.parseOmdbVotes(data.imdbVotes);
+ if (!rating && !votes) return null;
+ return { rating, votes };
+ } catch {
+ return null;
+ }
+ }
+
+ private async fetchTmdbRatingByImdb(
+ imdbId: string,
+ ): Promise<{ rating: number; votes: number } | null> {
+ if (!this.tmdbApiKey) return null;
+ try {
+ const url = `${TMDB_BASE}/find/${encodeURIComponent(imdbId)}?external_source=imdb_id`;
+ const data = (await this.fetchWithTimeout(url, this.tmdbHeaders())) as {
+ movie_results?: Array<{ vote_average?: number; vote_count?: number }>;
+ tv_results?: Array<{ vote_average?: number; vote_count?: number }>;
+ };
+ const hit =
+ (data.movie_results && data.movie_results[0]) ||
+ (data.tv_results && data.tv_results[0]) ||
+ null;
+ if (!hit) return null;
+ const rating = hit.vote_average ?? 0;
+ const votes = hit.vote_count ?? 0;
+ if (!rating && !votes) return null;
+ return { rating, votes };
+ } catch {
+ return null;
+ }
+ }
+
+ /**
+ * Picks the right API based on the title's stored IDs and returns the
+ * community rating + vote count + source label. Returns null on failure
+ * or if no suitable API is available.
+ */
+ async fetchCommunityRating(
+ title: WatchLogTitle,
+ animeApiSource: 'jikan' | 'anilist' = 'jikan',
+ apiGroup: 'anime' | 'movie' | '' = '',
+ ): Promise<{ rating: number; votes: number; source: 'mal' | 'anilist' | 'imdb' | 'tmdb' } | null> {
+ const hasMal = (title.malId ?? 0) > 0;
+ const hasAniList = (title.anilistId ?? 0) > 0;
+ const resolvedGroup: 'anime' | 'movie' | '' =
+ apiGroup || (title.type === 'Anime' ? 'anime' : title.type === 'Movie' || title.type === 'TV Show' || title.type === 'TvShow' ? 'movie' : '');
+
+ if (resolvedGroup === '') return null;
+
+ // Anime API group is bound to anime APIs.
+ if (resolvedGroup === 'anime') {
+ if (animeApiSource === 'anilist') {
+ if (hasAniList) {
+ const r = await this.fetchAniListRating(title.anilistId!);
+ if (r) return { ...r, source: 'anilist' };
+ }
+ if (hasMal) {
+ const r = await this.fetchMalRating(title.malId!);
+ if (r) return { ...r, source: 'mal' };
+ }
+ } else {
+ if (hasMal) {
+ const r = await this.fetchMalRating(title.malId!);
+ if (r) return { ...r, source: 'mal' };
+ }
+ if (hasAniList) {
+ const r = await this.fetchAniListRating(title.anilistId!);
+ if (r) return { ...r, source: 'anilist' };
+ }
+ }
+ // Last-resort: anime without any anime-api ID falls through to IMDb/TMDB.
+ }
+
+ const imdbId = this.extractImdbId(title.externalLink ?? '');
+ if (imdbId) {
+ if (this.omdbApiKey) {
+ const r = await this.fetchOmdbRating(imdbId);
+ if (r) return { ...r, source: 'imdb' };
+ }
+ if (this.tmdbApiKey) {
+ const r = await this.fetchTmdbRatingByImdb(imdbId);
+ if (r) return { ...r, source: 'tmdb' };
+ }
+ }
+
+ return null;
+ }
+
+ // ─── Google Books (Books) ─────────────────────────────────────────────────────
+
+ /**
+ * Picks the largest available cover from a volume's imageLinks, falling down the
+ * chain extraLarge → large → medium → small → thumbnail → smallThumbnail. Returns
+ * a direct (200) HTTPS image URL — upgraded from http and stripped of the page-curl
+ * effect — so no local caching is needed. Empty string when no image is present.
+ */
+ private googleCoverUrl(info: GoogleVolumeInfo): string {
+ const links = info.imageLinks;
+ if (!links) return '';
+ const raw =
+ links.extraLarge ??
+ links.large ??
+ links.medium ??
+ links.small ??
+ links.thumbnail ??
+ links.smallThumbnail ??
+ '';
+ if (!raw) return '';
+ return raw.replace(/^http:/, 'https:').replace(/&edge=curl/g, '');
+ }
+
+ /** Normalizes Google's `publishedDate` (YYYY | YYYY-MM | YYYY-MM-DD) to a YYYY-MM-DD storage value. */
+ private googleReleaseDate(published?: string): string {
+ if (!published) return '';
+ if (/^\d{4}-\d{2}-\d{2}$/.test(published)) return published;
+ if (/^\d{4}-\d{2}$/.test(published)) return `${published}-01`;
+ if (/^\d{4}$/.test(published)) return `${published}-01-01`;
+ return published;
+ }
+
+ private mapGoogleVolume(item: GoogleVolume): BookSearchResult {
+ const info = item.volumeInfo ?? {};
+ const published = info.publishedDate ?? '';
+ const year = published ? parseInt(published.slice(0, 4), 10) || 0 : 0;
+ const fullTitle = info.subtitle ? `${info.title ?? ''}: ${info.subtitle}` : (info.title ?? '');
+ return {
+ title: fullTitle,
+ author: (info.authors ?? []).join(', '),
+ year,
+ totalPages: info.pageCount ?? 0,
+ coverUrl: this.googleCoverUrl(info),
+ googleBooksId: item.id ?? '',
+ releaseDate: this.googleReleaseDate(published),
+ url: info.infoLink ?? info.canonicalVolumeLink ?? '',
+ };
+ }
+
+ /**
+ * Google Books requests must always carry the user's API key — the anonymous
+ * endpoint shares a global quota and returns 429. Throws a typed GoogleBooksError
+ * (no-key / rate-limited / http / parse / network) so callers can show an accurate
+ * message instead of a generic "check your connection". `path` is everything after
+ * the base, e.g. `/volumes?q=...`; the `key` param is appended here.
+ */
+ private async googleBooksFetch(path: string): Promise {
+ const key = this.googleBooksApiKey.trim();
+ if (!key) {
+ throw new GoogleBooksError('no-key', 'Google Books API key not set');
+ }
+ const sep = path.includes('?') ? '&' : '?';
+ const url = `${GOOGLE_BOOKS_BASE}${path}${sep}key=${encodeURIComponent(key)}`;
+ let timer: number | null = null;
+ const timeoutPromise = new Promise((_, reject) => {
+ timer = window.setTimeout(() => reject(new GoogleBooksError('network', 'Request timed out')), API_TIMEOUT_MS);
+ });
+ try {
+ const reqPromise = requestUrl({ url, throw: false }).then((r) => {
+ // 429 (rate limit) and 403 (quota/keyInvalid) both point at the key, not the network.
+ if (r.status === 429 || r.status === 403) {
+ throw new GoogleBooksError('rate-limited', `Google Books returned ${r.status}`, r.status);
+ }
+ if (r.status < 200 || r.status >= 300) {
+ throw new GoogleBooksError('http', `Google Books returned ${r.status}`, r.status);
+ }
+ try {
+ return r.json as unknown;
+ } catch {
+ throw new GoogleBooksError('parse', 'Failed to parse Google Books response');
+ }
+ });
+ return await Promise.race([reqPromise, timeoutPromise]);
+ } catch (err) {
+ if (err instanceof GoogleBooksError) throw err;
+ throw new GoogleBooksError('network', (err as Error)?.message ?? 'Network error');
+ } finally {
+ if (timer !== null) window.clearTimeout(timer);
+ }
+ }
+
+ /** Throws GoogleBooksError on failure (no key, rate limit, etc.) — see googleBooksErrorMessage. */
+ async searchGoogleBooks(query: string): Promise {
+ const data = (await this.googleBooksFetch(
+ `/volumes?q=${encodeURIComponent(query)}&maxResults=10`,
+ )) as GoogleBooksResponse;
+ return (data.items ?? []).slice(0, 10).map((item) => this.mapGoogleVolume(item));
+ }
+
+ /** Resolves on success, throws GoogleBooksError on failure (so the test can report the real cause). */
+ async checkGoogleBooksConnection(): Promise {
+ const data = (await this.googleBooksFetch('/volumes?q=tolkien&maxResults=1')) as GoogleBooksResponse;
+ return Array.isArray(data.items);
+ }
+
+ // ─── Jikan (Manga) ──────────────────────────────────────────────────────────
+
+ private mapJikanManga(m: JikanMangaItem): MangaSearchResult {
+ const yearStr = m.published?.from ?? '';
+ const year = yearStr ? parseInt(yearStr.slice(0, 4), 10) || 0 : 0;
+ return {
+ malId: m.mal_id,
+ title: m.title_english ?? m.title,
+ author: (m.authors ?? []).map((a) => a.name).join(', '),
+ year,
+ totalChapters: m.chapters ?? 0,
+ totalVolumes: m.volumes ?? 0,
+ coverUrl: m.images?.jpg?.image_url ?? '',
+ releaseDate: m.published?.from ? (m.published.from.split('T')[0] ?? '') : '',
+ url: m.url ?? `https://myanimelist.net/manga/${m.mal_id}`,
+ };
+ }
+
+ async searchManga(query: string): Promise {
+ const url = `${JIKAN_BASE}/manga?q=${encodeURIComponent(query)}&limit=10`;
+ try {
+ const data = (await this.throttleJikan(() => this.fetchWithTimeout(url))) as { data?: JikanMangaItem[] };
+ return (data.data ?? []).map((m) => this.mapJikanManga(m));
+ } catch {
+ return [];
+ }
+ }
+
+ /**
+ * Detail lookup for a single manga (Jikan /manga/{mal_id}). Search-list results
+ * return null chapters/volumes for ongoing titles, so totals are fetched here on
+ * selection. Shares the existing Jikan throttle. (Jikan also exposes a richer
+ * `/manga/{id}/full` endpoint not used here.)
+ */
+ async getMangaByMalId(malId: number): Promise {
+ const url = `${JIKAN_BASE}/manga/${malId}`;
+ try {
+ const data = (await this.throttleJikan(() => this.fetchWithTimeout(url))) as { data?: JikanMangaItem };
+ if (!data.data) return null;
+ return this.mapJikanManga(data.data);
+ } catch {
+ return null;
+ }
+ }
+
+ /**
+ * Detail lookup for a single Google Books volume. The search endpoint sometimes
+ * omits pageCount and only returns thumbnail-sized images, so on selection this
+ * resolves the full volume (better page count + larger cover). Returns null when
+ * the volume can't be fetched.
+ */
+ async getGoogleBookById(volumeId: string): Promise {
+ if (!volumeId) return null;
+ const item = (await this.googleBooksFetch(`/volumes/${encodeURIComponent(volumeId)}`)) as GoogleVolume;
+ if (!item || !item.id) return null;
+ return this.mapGoogleVolume(item);
+ }
+
async checkTmdbConnection(): Promise {
if (!this.tmdbApiKey) return false;
try {
diff --git a/src/CommunityRating.ts b/src/CommunityRating.ts
new file mode 100644
index 0000000..94b2725
--- /dev/null
+++ b/src/CommunityRating.ts
@@ -0,0 +1,251 @@
+import { Notice } from 'obsidian';
+import type WatchLogPlugin from './main';
+import type { WatchLogTitle, AnimeSearchResult } from './types';
+import { formatVoteCount, getApiGroupForType } from './types';
+
+const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
+
+const SOURCE_LABELS: Record = {
+ imdb: 'IMDb',
+ mal: 'MAL',
+ anilist: 'AniList',
+ tmdb: 'TMDB',
+};
+
+function formatScore(rating: number, source: string): string {
+ if (source === 'anilist') return `${Math.round(rating)}%`;
+ return rating.toFixed(rating >= 10 ? 0 : 1);
+}
+
+export function renderCommunityRating(
+ parent: HTMLElement,
+ plugin: WatchLogPlugin,
+ titleId: string,
+ onUpdated?: () => void,
+): void {
+ const title = plugin.dataManager.getTitle(titleId);
+ if (!title) return;
+
+ parent.createSpan({ cls: 'wl-rating-divider' });
+
+ const badge = parent.createDiv({ cls: 'wl-community-badge' });
+ const hasRating = !!title.communitySource && (title.communityRating ?? 0) > 0;
+
+ if (hasRating) {
+ const source = title.communitySource ?? '';
+ const label = SOURCE_LABELS[source] ?? source.toUpperCase();
+ badge.createSpan({
+ cls: `wl-community-source wl-community-source--${source}`,
+ text: label,
+ });
+ badge.createSpan({
+ cls: 'wl-community-score',
+ text: formatScore(title.communityRating ?? 0, source),
+ });
+ if ((title.communityVotes ?? 0) > 0) {
+ badge.createSpan({
+ cls: 'wl-community-votes',
+ text: `(${formatVoteCount(title.communityVotes ?? 0)})`,
+ });
+ }
+ } else {
+ badge.createSpan({ cls: 'wl-community-empty', text: 'No community rating' });
+ }
+
+ const refreshBtn = badge.createEl('button', {
+ cls: 'wl-community-refresh',
+ text: '⟳',
+ attr: { title: 'Refresh community rating', type: 'button' },
+ });
+ refreshBtn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ void refreshCommunityRating(plugin, titleId, refreshBtn, true).then(() => {
+ if (onUpdated) onUpdated();
+ });
+ });
+}
+
+/**
+ * Auto-refresh: triggers a background fetch when data is stale (≥30 days)
+ * or has never been fetched. For anime titles, only uses the API that
+ * matches the current animeApiSource setting AND that has its ID already
+ * present on the title — no search-by-name in the background. For non-anime
+ * titles, uses the standard fetchCommunityRating routing.
+ */
+const pendingRatingRefreshes: Set = new Set();
+
+export function maybeAutoRefreshCommunityRating(
+ plugin: WatchLogPlugin,
+ titleId: string,
+ onUpdated?: () => void,
+): void {
+ const title = plugin.dataManager.getTitle(titleId);
+ if (!title) return;
+ const last = title.communityRatingLastFetched ?? '';
+ if (last) {
+ const ts = Date.parse(last);
+ if (!isNaN(ts) && Date.now() - ts < THIRTY_DAYS_MS) return;
+ }
+ if (!hasFetchableSource(title, plugin.settings.animeApiSource ?? 'jikan', plugin.settings.typeApiMapping)) return;
+ if (pendingRatingRefreshes.has(titleId)) return;
+ pendingRatingRefreshes.add(titleId);
+ void backgroundRefresh(plugin, titleId)
+ .then((ok) => {
+ if (ok && onUpdated) onUpdated();
+ })
+ .finally(() => {
+ pendingRatingRefreshes.delete(titleId);
+ });
+}
+
+function hasFetchableSource(
+ title: WatchLogTitle,
+ animeApiSource: 'jikan' | 'anilist',
+ mapping: Record | undefined,
+): boolean {
+ const group = getApiGroupForType(title.type, mapping);
+ if (group === '') return false;
+ if (group === 'anime') {
+ if (animeApiSource === 'anilist') return (title.anilistId ?? 0) > 0;
+ return (title.malId ?? 0) > 0;
+ }
+ return !!(title.externalLink ?? '').match(/tt\d+/);
+}
+
+async function backgroundRefresh(plugin: WatchLogPlugin, titleId: string): Promise {
+ const title = plugin.dataManager.getTitle(titleId);
+ if (!title) return false;
+ const animeSource = plugin.settings.animeApiSource ?? 'jikan';
+ const group = getApiGroupForType(title.type, plugin.settings.typeApiMapping);
+ const result = await plugin.apiService.fetchCommunityRating(title, animeSource, group);
+ if (!result) return false;
+ plugin.dataManager.updateCommunityRating(titleId, result.rating, result.votes, result.source);
+ return true;
+}
+
+/**
+ * Manual refresh:
+ * - For anime: respects current animeApiSource. If the title is missing
+ * the ID for that source, searches by name on that API; on a reasonable
+ * match, stores the new ID + updates externalLink to the new source.
+ * - For non-anime: standard fetchCommunityRating.
+ * Saves changes via updateTitle (full save) when IDs/externalLink change;
+ * otherwise the lighter updateCommunityRating path.
+ */
+export async function refreshCommunityRating(
+ plugin: WatchLogPlugin,
+ titleId: string,
+ btnEl: HTMLElement | null,
+ showNotice: boolean,
+): Promise {
+ let title = plugin.dataManager.getTitle(titleId);
+ if (!title) return false;
+ if (btnEl) btnEl.addClass('is-loading');
+ try {
+ const animeSource = plugin.settings.animeApiSource ?? 'jikan';
+ const group = getApiGroupForType(title.type, plugin.settings.typeApiMapping);
+
+ if (group === '') {
+ if (showNotice) {
+ new Notice(`No API configured for type "${title.type}". Configure it in Settings → API.`);
+ }
+ return false;
+ }
+
+ if (group === 'anime') {
+ const wantsAniList = animeSource === 'anilist';
+ const hasPreferredId = wantsAniList
+ ? (title.anilistId ?? 0) > 0
+ : (title.malId ?? 0) > 0;
+
+ if (!hasPreferredId) {
+ // Search by name on the preferred API.
+ const matched = await searchForMatch(plugin, title, wantsAniList);
+ if (!matched) {
+ if (showNotice) {
+ new Notice(`Could not find matching title on ${wantsAniList ? 'AniList' : 'MAL'}.`);
+ }
+ return false;
+ }
+ // Persist the new ID + externalLink before fetching the rating.
+ const fresh = plugin.dataManager.getTitle(titleId);
+ if (!fresh) return false;
+ if (wantsAniList) {
+ fresh.anilistId = matched.anilistId ?? 0;
+ fresh.externalLink = `https://anilist.co/anime/${fresh.anilistId}`;
+ } else {
+ fresh.malId = matched.malId;
+ fresh.externalLink = `https://myanimelist.net/anime/${fresh.malId}`;
+ }
+ await plugin.dataManager.updateTitle(fresh);
+ title = fresh;
+ } else {
+ // We already have the right ID — still make sure externalLink
+ // matches the preferred source so the UI link is consistent.
+ const expectedLink = wantsAniList
+ ? `https://anilist.co/anime/${title.anilistId}`
+ : `https://myanimelist.net/anime/${title.malId}`;
+ if (title.externalLink !== expectedLink) {
+ title.externalLink = expectedLink;
+ await plugin.dataManager.updateTitle(title);
+ }
+ }
+ }
+
+ const result = await plugin.apiService.fetchCommunityRating(title, animeSource, group);
+ if (!result) {
+ if (showNotice) new Notice('Could not fetch community rating.');
+ return false;
+ }
+ plugin.dataManager.updateCommunityRating(
+ titleId,
+ result.rating,
+ result.votes,
+ result.source,
+ );
+ if (showNotice) {
+ const label = SOURCE_LABELS[result.source] ?? result.source;
+ new Notice(`Rating updated from ${label}.`);
+ }
+ return true;
+ } finally {
+ if (btnEl) btnEl.removeClass('is-loading');
+ }
+}
+
+function normalizeName(s: string): string {
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
+}
+
+function isReasonableMatch(needle: string, hay: string): boolean {
+ const a = normalizeName(needle);
+ const b = normalizeName(hay);
+ if (!a || !b) return false;
+ if (a === b) return true;
+ if (a.includes(b) || b.includes(a)) return true;
+ const aWords = new Set(a.split(' ').filter((w) => w.length > 2));
+ const bWords = new Set(b.split(' ').filter((w) => w.length > 2));
+ if (aWords.size === 0 || bWords.size === 0) return false;
+ let shared = 0;
+ for (const w of aWords) if (bWords.has(w)) shared++;
+ const minSize = Math.min(aWords.size, bWords.size);
+ return shared / minSize >= 0.5;
+}
+
+async function searchForMatch(
+ plugin: WatchLogPlugin,
+ title: WatchLogTitle,
+ wantsAniList: boolean,
+): Promise {
+ const results = wantsAniList
+ ? await plugin.apiService.searchAniList(title.title)
+ : await plugin.apiService.searchAnime(title.title);
+ if (!results.length) return null;
+ const top = results[0]!;
+ if (isReasonableMatch(title.title, top.title)) return top;
+ // Try a few more results in case the first isn't the best.
+ for (let i = 1; i < Math.min(results.length, 5); i++) {
+ if (isReasonableMatch(title.title, results[i]!.title)) return results[i]!;
+ }
+ return null;
+}
diff --git a/src/CsvModal.ts b/src/CsvModal.ts
index 64052ae..5258ea5 100644
--- a/src/CsvModal.ts
+++ b/src/CsvModal.ts
@@ -28,28 +28,44 @@ function titlesToCsv(titles: WatchLogTitle[]): string {
return [header, ...rows].join('\n');
}
-function parseCsvRow(row: string): string[] {
- const fields: string[] = [];
+/** Full CSV parser: handles quoted fields with embedded newlines, escaped quotes, comma delimiters. */
+function parseCSV(text: string): string[][] {
+ const rows: string[][] = [];
+ let row: string[] = [];
let cur = '';
let inQuotes = false;
- for (let i = 0; i < row.length; i++) {
- const ch = row[i];
- if (ch === '"') {
- if (inQuotes && row[i + 1] === '"') {
- cur += '"';
- i++;
+ // Normalize CRLF to LF
+ const t = text.replace(/\r\n?/g, '\n');
+ for (let i = 0; i < t.length; i++) {
+ const ch = t[i];
+ if (inQuotes) {
+ if (ch === '"') {
+ if (t[i + 1] === '"') { cur += '"'; i++; }
+ else { inQuotes = false; }
} else {
- inQuotes = !inQuotes;
+ cur += ch;
}
- } else if (ch === ',' && !inQuotes) {
- fields.push(cur);
- cur = '';
} else {
- cur += ch;
+ if (ch === '"') {
+ inQuotes = true;
+ } else if (ch === ',') {
+ row.push(cur); cur = '';
+ } else if (ch === '\n') {
+ row.push(cur); cur = '';
+ rows.push(row); row = [];
+ } else {
+ cur += ch;
+ }
}
}
- fields.push(cur);
- return fields;
+ // Final field/row
+ if (cur.length > 0 || row.length > 0) {
+ row.push(cur);
+ rows.push(row);
+ }
+ // Drop trailing fully-empty rows
+ while (rows.length > 0 && rows[rows.length - 1]!.every((c) => c === '')) rows.pop();
+ return rows;
}
// ── Date parsing ─────────────────────────────────────────────────────────────
@@ -136,6 +152,7 @@ const WL_IMPORT_FIELDS: WlFieldDef[] = [
{ key: 'dateFinished', label: 'Date Finished', autoDetect: ['finished', 'datefinished', 'date finished', 'date_finished', 'end date', 'finish date', 'completed date'] },
{ key: 'releaseDate', label: 'Release Date', autoDetect: ['releasedate', 'release date', 'release_date', 'air date', 'airdate'] },
{ key: 'externalLink', label: 'Link', autoDetect: ['link', 'externallink', 'external link', 'external_link', 'url'] },
+ { key: 'notes', label: 'Notes', autoDetect: ['notes', 'note', 'comment', 'comments'] },
];
function autoMap(headers: string[]): Record {
@@ -148,19 +165,17 @@ function autoMap(headers: string[]): Record {
}
function applyMapping(
- lines: string[],
+ rows: string[][],
mapping: Record,
dataManager: DataManager,
): { entry: Partial; isDuplicate: boolean }[] {
- if (lines.length < 2) return [];
- const headerLine = lines[0] ?? '';
- const headers = parseCsvRow(headerLine).map((h) => h.trim());
+ if (rows.length < 2) return [];
+ const headers = (rows[0] ?? []).map((h) => h.trim());
const existingTitles = dataManager.getTitles();
- return lines.slice(1)
- .filter((l) => l.trim())
- .map((line) => {
- const values = parseCsvRow(line);
+ return rows.slice(1)
+ .filter((r) => r.some((c) => c.trim()))
+ .map((values) => {
const get = (fieldKey: string): string => {
const colName = mapping[fieldKey];
if (!colName) return '';
@@ -179,7 +194,10 @@ function applyMapping(
const priorityVal = get('priority');
if (priorityVal) entry.priority = priorityVal;
const ratingVal = get('rating');
- if (ratingVal) entry.rating = parseFloat(ratingVal) || 0;
+ if (ratingVal) {
+ const r = parseFloat(ratingVal) || 0;
+ entry.rating = Math.max(0, Math.min(5, r));
+ }
const epsVal = get('totalEpisodes');
if (epsVal) entry.totalEpisodes = parseInt(epsVal) || 0;
const durVal = get('episodeDuration');
@@ -192,11 +210,14 @@ function applyMapping(
if (releaseDateRaw) { const d = parseCsvDate(releaseDateRaw); if (d) entry.releaseDate = d; }
const linkVal = get('externalLink');
if (linkVal) entry.externalLink = linkVal;
+ const notesVal = get('notes');
+ if (notesVal) entry.notes = notesVal;
// Skip rows where all mapped fields resolved to empty
const isEmpty = !entry.title?.trim() && !entry.type && !entry.status
&& !entry.rating && !entry.totalEpisodes && !entry.episodeDuration
- && !entry.dateStarted && !entry.dateFinished && !entry.releaseDate && !entry.externalLink;
+ && !entry.dateStarted && !entry.dateFinished && !entry.releaseDate && !entry.externalLink
+ && !entry.notes;
if (isEmpty) return null;
const isDuplicate = !!existingTitles.find(
@@ -233,7 +254,7 @@ export class CsvModal extends Modal {
private selectedIds = new Set();
// Import state — shared across steps
- private csvLines: string[] = [];
+ private csvRows: string[][] = [];
private csvHeaders: string[] = [];
private columnMapping: Record = {};
private importRows: { entry: Partial; isDuplicate: boolean; selected: boolean }[] = [];
@@ -347,7 +368,7 @@ export class CsvModal extends Modal {
const csv = titlesToCsv(toExport);
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
+ const a = activeDocument.createElement('a');
const today = new Date();
const dateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
a.href = url;
@@ -418,9 +439,8 @@ export class CsvModal extends Modal {
const reader = new FileReader();
reader.onload = (ev) => {
const text = ev.target?.result as string;
- this.csvLines = text.split('\n');
- const headerLine = this.csvLines[0] ?? '';
- this.csvHeaders = parseCsvRow(headerLine).map((h) => h.trim());
+ this.csvRows = parseCSV(text);
+ this.csvHeaders = (this.csvRows[0] ?? []).map((h) => h.trim());
this.columnMapping = autoMap(this.csvHeaders);
this.showMappingStep();
};
@@ -474,7 +494,7 @@ export class CsvModal extends Modal {
this.columnMapping[field.key] = selectEls[field.key]?.value ?? '';
}
// Parse rows with current column mapping
- this.importRows = applyMapping(this.csvLines, this.columnMapping, this.dataManager)
+ this.importRows = applyMapping(this.csvRows, this.columnMapping, this.dataManager)
.map((r) => ({ ...r, selected: !r.isDuplicate }));
// Check for unknown status/type values and non-numeric ratings
@@ -502,13 +522,11 @@ export class CsvModal extends Modal {
// Detect non-numeric rating values (r.entry.rating would be 0 from parseFloat on non-numeric)
const ratingColName = this.columnMapping['rating'];
if (ratingColName) {
- const headerLine = this.csvLines[0] ?? '';
- const headers = parseCsvRow(headerLine).map((h) => h.trim());
+ const headers = (this.csvRows[0] ?? []).map((h) => h.trim());
const ratingIdx = headers.indexOf(ratingColName);
if (ratingIdx >= 0) {
const lineIdx = this.importRows.indexOf(r);
- const rawLine = this.csvLines[lineIdx + 1] ?? '';
- const rawValues = parseCsvRow(rawLine);
+ const rawValues = this.csvRows[lineIdx + 1] ?? [];
const rawRating = (rawValues[ratingIdx] ?? '').trim();
if (rawRating && isNaN(parseFloat(rawRating)) && !seenRatings.has(rawRating)) {
unknownRatings.push(rawRating);
@@ -658,16 +676,13 @@ export class CsvModal extends Modal {
}
// Rating — map non-numeric value to numeric
const rawRatingKey = Object.keys(this.ratingValueMap).find((k) => {
- // Find if this row's original CSV rating matched this key
const ratingColName = this.columnMapping['rating'];
if (!ratingColName) return false;
- const headerLine = this.csvLines[0] ?? '';
- const headers = parseCsvRow(headerLine).map((h) => h.trim());
+ const headers = (this.csvRows[0] ?? []).map((h) => h.trim());
const ratingIdx = headers.indexOf(ratingColName);
if (ratingIdx < 0) return false;
const rowIdx = this.importRows.indexOf(r);
- const rawLine = this.csvLines[rowIdx + 1] ?? '';
- const rawValues = parseCsvRow(rawLine);
+ const rawValues = this.csvRows[rowIdx + 1] ?? [];
return (rawValues[ratingIdx] ?? '').trim() === k;
});
if (rawRatingKey !== undefined) {
@@ -819,62 +834,62 @@ export class CsvModal extends Modal {
const CHUNK_SIZE = 10;
let added = 0;
- for (let chunkStart = 0; chunkStart < toImport.length; chunkStart += CHUNK_SIZE) {
- if (this.importCancelled) break;
-
- const chunkEnd = Math.min(chunkStart + CHUNK_SIZE, toImport.length);
- for (let i = chunkStart; i < chunkEnd; i++) {
+ try {
+ for (let chunkStart = 0; chunkStart < toImport.length; chunkStart += CHUNK_SIZE) {
if (this.importCancelled) break;
- const r = toImport[i]!;
- const titleName = r.entry.title!.trim();
- const entry: WatchLogTitle = {
- id: this.dataManager.generateId(titleName),
- title: titleName,
- type: r.entry.type?.trim() || this.plugin.settings.types[0]?.name || 'Anime',
- status: r.entry.status || 'Plan to watch',
- priority: r.entry.priority || 'Medium',
- review: '',
- rating: r.entry.rating ?? 0,
- notes: '',
- dateStarted: r.entry.dateStarted ?? null,
- dateFinished: r.entry.dateFinished ?? null,
- dateAdded: new Date().toISOString(),
- dateModified: new Date().toISOString(),
- totalEpisodes: r.entry.totalEpisodes ?? 0,
- episodeDuration: r.entry.episodeDuration ?? 0,
- releaseDate: r.entry.releaseDate ?? null,
- externalLink: r.entry.externalLink ?? '',
- seasons: [],
- watchedEpisodes: [],
- };
-
- if (entry.status === 'Completed' && entry.totalEpisodes > 0) {
- entry.watchedEpisodes = Array.from({ length: entry.totalEpisodes }, (_, k) => k + 1);
+ const chunkEnd = Math.min(chunkStart + CHUNK_SIZE, toImport.length);
+ const chunkEntries: WatchLogTitle[] = [];
+ for (let i = chunkStart; i < chunkEnd; i++) {
+ if (this.importCancelled) break;
+ const r = toImport[i]!;
+ const titleName = r.entry.title!.trim();
+ const entry: WatchLogTitle = {
+ id: this.dataManager.generateId(titleName),
+ title: titleName,
+ type: r.entry.type?.trim() || this.plugin.settings.types[0]?.name || 'Anime',
+ status: r.entry.status || 'Plan to watch',
+ priority: r.entry.priority || 'Medium',
+ review: '',
+ rating: r.entry.rating ?? 0,
+ notes: r.entry.notes ?? '',
+ dateStarted: r.entry.dateStarted ?? null,
+ dateFinished: r.entry.dateFinished ?? null,
+ dateAdded: new Date().toISOString(),
+ dateModified: new Date().toISOString(),
+ totalEpisodes: r.entry.totalEpisodes ?? 0,
+ episodeDuration: r.entry.episodeDuration ?? 0,
+ releaseDate: r.entry.releaseDate ?? null,
+ externalLink: r.entry.externalLink ?? '',
+ seasons: [],
+ watchedEpisodes: [],
+ };
+ if (entry.status === 'Completed' && entry.totalEpisodes > 0) {
+ entry.watchedEpisodes = Array.from({ length: entry.totalEpisodes }, (_, k) => k + 1);
+ }
+ chunkEntries.push(entry);
}
- this.plugin.importProgress = { current: added + 1, total: toImport.length, cancel: cancelFn };
- await this.dataManager.addTitleSilent(entry);
- added++;
+ if (chunkEntries.length === 0) break;
+ await this.dataManager.addTitleBatch(chunkEntries);
+ added += chunkEntries.length;
+ this.plugin.importProgress = { current: added, total: toImport.length, cancel: cancelFn };
const pct = Math.round((added / toImport.length) * 100);
if (this.progressBarFill) this.progressBarFill.style.width = `${pct}%`;
if (this.progressText) this.progressText.textContent = `${added} / ${toImport.length}`;
}
+ } finally {
+ this.isImporting = false;
+ this.plugin.importProgress = null;
+ this.dataManager.notifyChange();
}
- this.isImporting = false;
-
- // Clear header progress bar and trigger a final UI refresh
- this.plugin.importProgress = null;
- this.dataManager.notifyChange();
-
const newTypesMsg = typesToCreate.size > 0
? ` (${typesToCreate.size} new type${typesToCreate.size !== 1 ? 's' : ''} created)`
: '';
if (this.importCancelled) {
- // Import was cancelled — stay open so user can see what was imported
if (this.progressText) {
this.progressText.textContent = `Cancelled — ${added} / ${toImport.length} imported`;
}
@@ -884,7 +899,6 @@ export class CsvModal extends Modal {
}
new Notice(`Import cancelled. ${added} title${added !== 1 ? 's' : ''} imported.`);
} else {
- // All done — show success state, then close
if (this.progressText) {
this.progressText.textContent = `Done — ${added} imported`;
}
diff --git a/src/CustomListsTab.ts b/src/CustomListsTab.ts
index cfa20c3..cf4b90e 100644
--- a/src/CustomListsTab.ts
+++ b/src/CustomListsTab.ts
@@ -1,4 +1,4 @@
-import { App, Component, MarkdownRenderer, Modal, Notice, Platform, TFile, normalizePath, Setting } from 'obsidian';
+import { App, Component, MarkdownRenderer, Modal, Notice, TFile, normalizePath, Setting } from 'obsidian';
import type WatchLogPlugin from './main';
import type { DataManager } from './DataManager';
import type { CustomListColumn, CustomListRow, CustomList } from './types';
@@ -9,11 +9,25 @@ import { ConfirmModal } from './ConfirmModal';
// ─────────────────────────────────────────────────────────────────────────────
export class CustomListManager {
+ /** Names of lists whose ## Data JSON is corrupt — never overwrite these. */
+ corruptLists: Set = new Set();
+
+ /** Per-list save queue: serializes writes to the same list file so table
+ * and notes saves cannot race and clobber each other. */
+ private saveQueues: Map> = new Map();
+
constructor(
private readonly app: App,
private readonly plugin: WatchLogPlugin,
) {}
+ private async saveSerialized(listName: string, saveFn: () => Promise): Promise {
+ const prev = this.saveQueues.get(listName) ?? Promise.resolve();
+ const next = prev.then(saveFn).catch(e => console.error('[WL] Save error for list:', listName, e));
+ this.saveQueues.set(listName, next);
+ await next;
+ }
+
get folderPath(): string {
return this.plugin.settings.customListsFolder || 'WatchLog/CustomLists';
}
@@ -43,10 +57,13 @@ export class CustomListManager {
try {
const content = await this.app.vault.read(file);
return this.parse(name, content);
- } catch { return null; }
+ } catch (e) {
+ console.warn('[WL] Failed to read custom list', name, e);
+ return null;
+ }
}
- private parse(name: string, content: string): CustomList {
+ private parse(name: string, content: string): CustomList | null {
const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n## |\s*$)/);
const notes = (notesMatch?.[1] ?? '').trim();
@@ -68,48 +85,61 @@ export class CustomListManager {
typeof r === 'object' && r !== null && 'id' in r,
);
}
- } catch { /* invalid JSON */ }
+ } catch (e) {
+ console.warn('[WL] Custom list JSON parse failed for', name, e);
+ new Notice(`Custom list "${name}" has corrupt data and cannot be loaded.`);
+ this.corruptLists.add(name);
+ return null;
+ }
}
return { name, columns, rows, notes };
}
async saveList(list: CustomList): Promise {
- await this.ensureFolder();
- const path = normalizePath(`${this.folderPath}/${list.name}.md`);
- const content = this.serialize(list);
- const existing = this.app.vault.getAbstractFileByPath(path);
- try {
- if (existing instanceof TFile) {
- await this.app.vault.modify(existing, content);
- } else {
- await this.app.vault.create(path, content);
+ await this.saveSerialized(list.name, async () => {
+ if (this.corruptLists.has(list.name)) {
+ console.warn('[WL] Refusing to save corrupt custom list', list.name);
+ return;
}
- } catch (e) {
- console.error('WatchLog: failed to save custom list', e);
- }
+ await this.ensureFolder();
+ const path = normalizePath(`${this.folderPath}/${list.name}.md`);
+ const content = this.serialize(list);
+ const existing = this.app.vault.getAbstractFileByPath(path);
+ try {
+ if (existing instanceof TFile) {
+ await this.app.vault.modify(existing, content);
+ } else {
+ await this.app.vault.create(path, content);
+ }
+ } catch (e) {
+ console.error('WatchLog: failed to save custom list', e);
+ }
+ });
}
/** Saves only the ## Notes section without touching ## Data JSON. */
async saveNotes(name: string, notes: string): Promise {
- const path = normalizePath(`${this.folderPath}/${name}.md`);
- const file = this.app.vault.getAbstractFileByPath(path);
- if (!(file instanceof TFile)) return;
- try {
- const content = await this.app.vault.read(file);
- const notesMarker = '## Notes\n';
- const dataMarker = '\n## Data\n';
- const notesIdx = content.indexOf(notesMarker);
- if (notesIdx === -1) return;
- const afterNotes = notesIdx + notesMarker.length;
- const dataIdx = content.indexOf(dataMarker, afterNotes);
- const updated = dataIdx !== -1
- ? content.slice(0, afterNotes) + notes + content.slice(dataIdx)
- : content.slice(0, afterNotes) + notes + '\n';
- await this.app.vault.modify(file, updated);
- } catch (e) {
- console.error('WatchLog: failed to save notes', e);
- }
+ await this.saveSerialized(name, async () => {
+ const path = normalizePath(`${this.folderPath}/${name}.md`);
+ const file = this.app.vault.getAbstractFileByPath(path);
+ if (!(file instanceof TFile)) return;
+ try {
+ const content = await this.app.vault.read(file);
+ const notesMarker = '## Notes\n';
+ const dataMarker = '\n## Data\n';
+ const notesIdx = content.indexOf(notesMarker);
+ if (notesIdx === -1) return;
+ const afterNotes = notesIdx + notesMarker.length;
+ const dataIdx = content.indexOf(dataMarker, afterNotes);
+ const updated = dataIdx !== -1
+ ? content.slice(0, afterNotes) + notes + content.slice(dataIdx)
+ : content.slice(0, afterNotes) + notes + '\n';
+ await this.app.vault.modify(file, updated);
+ } catch (e) {
+ console.error('WatchLog: failed to save notes', e);
+ }
+ });
}
private serialize(list: CustomList): string {
@@ -157,58 +187,64 @@ function renderColumnList(
listEl: HTMLElement,
cols: CustomListColumn[],
existingRows: CustomListRow[],
- manager: CustomListManager,
app: App,
onReorder: (from: number, to: number) => void,
onDelete: (idx: number) => void,
onTypeChange: () => void,
+ onAdd?: () => void,
): void {
listEl.empty();
+ listEl.addClass('wl-editcol-card-grid');
let dragFromIndex = -1;
- if (cols.length === 0) {
- listEl.createDiv({ cls: 'wl-empty-state', text: 'No custom columns. Click "+ Add column" to add one.' });
- return;
- }
+ // Permanent auto-included columns — non-editable, non-deletable, non-draggable.
+ const renderAutoCard = (label: string): void => {
+ const card = listEl.createDiv({ cls: 'wl-editcol-card wl-editcol-card-auto' });
+ card.setAttribute('data-auto', label);
+ const nameWrap = card.createDiv({ cls: 'wl-editcol-card-name-wrap' });
+ nameWrap.createDiv({ cls: 'wl-editcol-card-name-label', text: label });
+ card.createDiv({ cls: 'wl-editcol-card-type-locked', text: 'Auto' });
+ card.createDiv({ cls: 'wl-editcol-card-spacer' });
+ };
+ renderAutoCard('#');
+ renderAutoCard('Name');
+ // User columns
cols.forEach((col, idx) => {
- const row = listEl.createDiv({ cls: 'wl-editcols-row' });
+ const card = listEl.createDiv({ cls: 'wl-editcol-card' });
- // ── Header line (handle | name | type | B/I toggles | × delete) ──
- const header = row.createDiv({ cls: 'wl-editcols-row-header' });
-
- // Drag handle
- const handle = header.createDiv({ cls: 'wl-editcols-handle', text: '⠿' });
- handle.addEventListener('mousedown', () => row.setAttribute('draggable', 'true'));
-
- row.addEventListener('dragstart', (e) => {
+ // Drag handle (top of card)
+ const handle = card.createDiv({ cls: 'wl-editcol-card-handle', text: '⠿' });
+ handle.title = 'Drag to reorder';
+ handle.addEventListener('mousedown', () => card.setAttribute('draggable', 'true'));
+ card.addEventListener('dragstart', (e) => {
dragFromIndex = idx;
e.dataTransfer?.setData('text/plain', String(idx));
- row.addClass('wl-cl-dragging');
+ card.addClass('wl-cl-dragging');
});
- row.addEventListener('dragend', () => {
- row.removeClass('wl-cl-dragging');
- row.setAttribute('draggable', 'false');
+ card.addEventListener('dragend', () => {
+ card.removeClass('wl-cl-dragging');
+ card.setAttribute('draggable', 'false');
});
- row.addEventListener('dragover', (e) => { e.preventDefault(); row.addClass('wl-cl-drag-over'); });
- row.addEventListener('dragleave', () => row.removeClass('wl-cl-drag-over'));
- row.addEventListener('drop', (e) => {
+ card.addEventListener('dragover', (e) => { e.preventDefault(); card.addClass('wl-cl-drag-over'); });
+ card.addEventListener('dragleave', () => card.removeClass('wl-cl-drag-over'));
+ card.addEventListener('drop', (e) => {
e.preventDefault();
- row.removeClass('wl-cl-drag-over');
+ card.removeClass('wl-cl-drag-over');
const from = dragFromIndex;
const to = idx;
if (from !== to && from >= 0) onReorder(from, to);
});
// Name input
- const nameInput = header.createEl('input', {
- cls: 'wl-modal-input wl-editcols-name',
+ const nameInput = card.createEl('input', {
+ cls: 'wl-modal-input wl-editcol-card-name',
attr: { type: 'text', placeholder: 'Column name', value: col.label },
});
nameInput.addEventListener('input', () => { col.label = nameInput.value; });
// Type select
- const typeSelect = header.createEl('select', { cls: 'wl-select wl-editcols-type' });
+ const typeSelect = card.createEl('select', { cls: 'wl-select wl-editcol-card-type' });
for (const t of ['text', 'number', 'select'] as const) {
const opt = typeSelect.createEl('option', {
value: t,
@@ -221,11 +257,11 @@ function renderColumnList(
onTypeChange();
});
- // Bold/Italic toggles (text/number only) — stay in header
+ // Bold/Italic toggles (text/number only)
if (col.type === 'text' || col.type === 'number') {
- const toggles = header.createDiv({ cls: 'wl-editcols-toggles' });
+ const toggles = card.createDiv({ cls: 'wl-editcol-card-toggles' });
const boldBtn = toggles.createEl('button', {
- cls: `wl-btn wl-btn-sm wl-editcols-toggle wl-editcols-bold-btn${col.bold ? ' is-active' : ''}`,
+ cls: `wl-btn wl-btn-sm wl-editcol-card-toggle wl-editcols-bold-btn${col.bold ? ' is-active' : ''}`,
text: 'B',
});
boldBtn.addEventListener('click', () => {
@@ -233,18 +269,30 @@ function renderColumnList(
boldBtn.toggleClass('is-active', !!col.bold);
});
const italicBtn = toggles.createEl('button', {
- cls: `wl-btn wl-btn-sm wl-editcols-toggle wl-editcols-italic-btn${col.italic ? ' is-active' : ''}`,
+ cls: `wl-btn wl-btn-sm wl-editcol-card-toggle wl-editcols-italic-btn${col.italic ? ' is-active' : ''}`,
text: 'I',
});
italicBtn.addEventListener('click', () => {
col.italic = !col.italic;
italicBtn.toggleClass('is-active', !!col.italic);
});
+
+ if (col.type === 'number') {
+ const autoTimeBtn = toggles.createEl('button', {
+ cls: `wl-btn wl-btn-sm wl-editcol-card-toggle wl-editcols-autotime-btn${col.autoTime ? ' is-active' : ''}`,
+ text: '⏱',
+ attr: { title: 'Auto-populate with remaining watch time from Watchlist' },
+ });
+ autoTimeBtn.addEventListener('click', () => {
+ col.autoTime = !col.autoTime;
+ autoTimeBtn.toggleClass('is-active', !!col.autoTime);
+ });
+ }
}
- // Delete column button — always at far right of header, clearly separated
- const delBtn = header.createEl('button', {
- cls: 'wl-btn wl-btn-sm wl-editcols-del',
+ // Delete card button
+ const delBtn = card.createEl('button', {
+ cls: 'wl-btn wl-btn-sm wl-editcol-card-del',
text: '×',
});
delBtn.title = 'Delete this column';
@@ -262,29 +310,32 @@ function renderColumnList(
}
});
- // ── Options area (select type only) — below the header line ──
+ // Options block (select type only)
if (col.type === 'select') {
- const optsArea = row.createDiv({ cls: 'wl-editcols-opts-area' });
+ const optsArea = card.createDiv({ cls: 'wl-editcol-card-opts' });
const renderOpts = (): void => {
optsArea.empty();
(col.options ?? []).forEach((opt, oi) => {
- const optRow = optsArea.createDiv({ cls: 'wl-editcols-opt-row' });
+ const optRow = optsArea.createDiv({ cls: 'wl-editcol-card-opt-row' });
const optInput = optRow.createEl('input', {
- cls: 'wl-modal-input',
- attr: { type: 'text', value: opt, placeholder: 'Option label' },
+ cls: 'wl-modal-input wl-editcol-card-opt-input',
+ attr: { type: 'text', value: opt, placeholder: 'Option' },
});
optInput.addEventListener('input', () => {
if (!col.options) col.options = [];
col.options[oi] = optInput.value;
});
const delOpt = optRow.createEl('button', {
- cls: 'wl-btn wl-btn-sm wl-editcols-opt-del',
+ cls: 'wl-btn wl-btn-sm wl-editcol-card-opt-del',
text: '×',
attr: { title: 'Remove this option' },
});
delOpt.addEventListener('click', () => { col.options?.splice(oi, 1); renderOpts(); });
});
- const addOpt = optsArea.createEl('button', { cls: 'wl-btn wl-btn-sm', text: '+ add option' });
+ const addOpt = optsArea.createEl('button', {
+ cls: 'wl-btn wl-btn-sm wl-editcol-card-opt-add',
+ text: '+ option',
+ });
addOpt.addEventListener('click', () => {
if (!col.options) col.options = [];
col.options.push('');
@@ -294,6 +345,13 @@ function renderColumnList(
renderOpts();
}
});
+
+ // Add-column card (last in row)
+ if (onAdd) {
+ const addCard = listEl.createDiv({ cls: 'wl-editcol-card wl-editcol-card-add' });
+ addCard.createDiv({ cls: 'wl-editcol-card-add-label', text: '+ add column' });
+ addCard.addEventListener('click', () => onAdd());
+ }
}
// ─────────────────────────────────────────────────────────────────────────────
@@ -323,6 +381,7 @@ export class EditColumnsModal extends Modal {
}
onOpen(): void {
+ this.modalEl.addClass('wl-editcol-modal');
this.titleEl.setText(`Edit Columns — ${this.list.name}`);
this.contentEl.addClass('wl-editcols-modal');
this.renderBody();
@@ -335,21 +394,6 @@ export class EditColumnsModal extends Modal {
this.listEl = this.contentEl.createDiv({ cls: 'wl-editcols-list' });
this.renderCols();
- const addBtn = this.contentEl.createEl('button', {
- cls: 'wl-btn wl-editcols-add-btn',
- text: '+ add column',
- });
- addBtn.addEventListener('click', () => {
- this.cols.push({
- id: this.manager.generateColId([...this.list.columns.filter(c => !c.locked), ...this.cols]),
- label: '',
- type: 'text',
- bold: false,
- italic: false,
- });
- this.renderBody();
- });
-
const footer = this.contentEl.createDiv({ cls: 'wl-modal-btn-row wl-editcols-footer' });
const cancelBtn = footer.createEl('button', { cls: 'wl-btn', text: 'Cancel' });
cancelBtn.addEventListener('click', () => this.close());
@@ -362,7 +406,6 @@ export class EditColumnsModal extends Modal {
this.listEl,
this.cols,
this.list.rows,
- this.manager,
this.app,
(from, to) => {
const [moved] = this.cols.splice(from, 1);
@@ -371,6 +414,16 @@ export class EditColumnsModal extends Modal {
},
(idx) => { this.cols.splice(idx, 1); this.renderBody(); },
() => this.renderBody(),
+ () => {
+ this.cols.push({
+ id: this.manager.generateColId([...this.list.columns.filter(c => !c.locked), ...this.cols]),
+ label: '',
+ type: 'text',
+ bold: false,
+ italic: false,
+ });
+ this.renderBody();
+ },
);
}
@@ -409,6 +462,7 @@ export class DefaultColumnsModal extends Modal {
}
onOpen(): void {
+ this.modalEl.addClass('wl-editcol-modal');
this.titleEl.setText('Default columns');
this.contentEl.addClass('wl-editcols-modal');
this.renderBody();
@@ -426,21 +480,6 @@ export class DefaultColumnsModal extends Modal {
this.listEl = this.contentEl.createDiv({ cls: 'wl-editcols-list' });
this.renderCols();
- const addBtn = this.contentEl.createEl('button', {
- cls: 'wl-btn wl-editcols-add-btn',
- text: '+ add column',
- });
- addBtn.addEventListener('click', () => {
- this.cols.push({
- id: this.manager.generateColId(this.cols),
- label: '',
- type: 'text',
- bold: false,
- italic: false,
- });
- this.renderBody();
- });
-
const footer = this.contentEl.createDiv({ cls: 'wl-modal-btn-row wl-editcols-footer' });
const cancelBtn = footer.createEl('button', { cls: 'wl-btn', text: 'Cancel' });
cancelBtn.addEventListener('click', () => this.close());
@@ -453,7 +492,6 @@ export class DefaultColumnsModal extends Modal {
this.listEl,
this.cols,
[],
- this.manager,
this.app,
(from, to) => {
const [moved] = this.cols.splice(from, 1);
@@ -462,6 +500,16 @@ export class DefaultColumnsModal extends Modal {
},
(idx) => { this.cols.splice(idx, 1); this.renderBody(); },
() => this.renderBody(),
+ () => {
+ this.cols.push({
+ id: this.manager.generateColId(this.cols),
+ label: '',
+ type: 'text',
+ bold: false,
+ italic: false,
+ });
+ this.renderBody();
+ },
);
}
@@ -485,7 +533,7 @@ export class DefaultColumnsModal extends Modal {
}
// ─────────────────────────────────────────────────────────────────────────────
-// NotesModal — Bug 2: replaces "open raw .md" with a simple textarea modal
+// NotesModal — edits a list's notes in a simple textarea modal
// ─────────────────────────────────────────────────────────────────────────────
class NotesModal extends Modal {
@@ -582,7 +630,7 @@ function wrapSelection(ta: HTMLTextAreaElement, before: string, after: string):
}
// ─────────────────────────────────────────────────────────────────────────────
-// ListNameModal — replaces window.prompt() (blocked in Electron)
+// ListNameModal — text-input modal (window.prompt() is blocked in Electron)
// ─────────────────────────────────────────────────────────────────────────────
class ListNameModal extends Modal {
@@ -631,78 +679,6 @@ class ListNameModal extends Modal {
onClose(): void { this.contentEl.empty(); }
}
-// ─────────────────────────────────────────────────────────────────────────────
-// MobileCellEditModal — replaces inline input editing on iOS to avoid keyboard overlay
-// ─────────────────────────────────────────────────────────────────────────────
-
-class MobileCellEditModal extends Modal {
- private inputType: 'text' | 'number';
- private initialValue: string;
- private label: string;
- private getSuggestions: (() => { title: string; type: string; status: string }[]) | undefined;
- private onSave: (value: string) => void | Promise;
-
- constructor(
- app: App,
- label: string,
- inputType: 'text' | 'number',
- initialValue: string,
- onSave: (value: string) => void | Promise,
- getSuggestions?: () => { title: string; type: string; status: string }[],
- ) {
- super(app);
- this.label = label;
- this.inputType = inputType;
- this.initialValue = initialValue;
- this.onSave = onSave;
- this.getSuggestions = getSuggestions;
- }
-
- onOpen(): void {
- this.titleEl.setText(this.label);
- const { contentEl } = this;
-
- const input = contentEl.createEl('input', {
- cls: 'wl-cl-cell-input wl-mobile-edit-input',
- attr: { type: this.inputType, value: this.initialValue },
- });
-
- if (this.getSuggestions) {
- const suggestionsEl = contentEl.createDiv({ cls: 'wl-cl-autofill-dropdown wl-mobile-suggestions' });
- input.addEventListener('input', () => {
- suggestionsEl.empty();
- const q = input.value.toLowerCase();
- if (!q) return;
- const matches = this.getSuggestions!()
- .filter(t => t.title.toLowerCase().includes(q))
- .slice(0, 10);
- for (const s of matches) {
- const item = suggestionsEl.createDiv({ cls: 'wl-result-item' });
- item.createDiv({ cls: 'wl-result-title', text: s.title });
- item.createDiv({ cls: 'wl-result-meta', text: `${s.type} · ${s.status}` });
- item.addEventListener('mousedown', (e) => {
- e.preventDefault();
- input.value = s.title;
- suggestionsEl.empty();
- });
- }
- });
- }
-
- const btnRow = contentEl.createDiv({ cls: 'wl-modal-btn-row' });
- btnRow.createEl('button', { cls: 'wl-btn mod-cta', text: 'Save' })
- .addEventListener('click', () => { void this.onSave(input.value); this.close(); });
-
- input.addEventListener('keydown', (e) => {
- if (e.key === 'Enter') { e.preventDefault(); void this.onSave(input.value); this.close(); }
- });
-
- window.setTimeout(() => { input.focus(); input.select(); }, 50);
- }
-
- onClose(): void { this.contentEl.empty(); }
-}
-
// ─────────────────────────────────────────────────────────────────────────────
// CustomListsTab
// ─────────────────────────────────────────────────────────────────────────────
@@ -722,6 +698,15 @@ export class CustomListsTab {
private duplicatedRowIds: Set = new Set();
private _escapeKeyHandler: ((e: KeyboardEvent) => void) | null = null;
+ // Cleanup functions for inline-editor document/visualViewport listeners
+ private activeCleanups: Array<() => void> = [];
+
+ // Generation counter to guard against stale renders / race conditions
+ private renderGeneration = 0;
+
+ // Per-list save queue to prevent overlapping table + notes writes
+ private saveQueues: Map> = new Map();
+
// Kept for keyboard navigation (set at buildTable time)
private _countEl: HTMLElement | null = null;
private _tableContainer: HTMLElement | null = null;
@@ -741,7 +726,33 @@ export class CustomListsTab {
return [...ordered, ...unordered];
}
+ /** Tear down active inline-editor listeners (called on rerender / view close). */
+ destroy(): void {
+ for (const fn of this.activeCleanups) {
+ try { fn(); } catch { /* ignore */ }
+ }
+ this.activeCleanups = [];
+ if (this._escapeKeyHandler) {
+ try { activeDocument.removeEventListener('keydown', this._escapeKeyHandler); } catch { /* ignore */ }
+ this._escapeKeyHandler = null;
+ }
+ }
+
+ addCleanup(fn: () => void): void {
+ this.activeCleanups.push(fn);
+ }
+
+ /** Serializes saves per-list so notes + table writes don't overlap. */
+ saveSerialized(listName: string, saveFn: () => Promise): Promise {
+ const prev = this.saveQueues.get(listName) ?? Promise.resolve();
+ const next = prev.then(saveFn).catch((e) => console.error('[WL] custom-list save failed:', e));
+ this.saveQueues.set(listName, next);
+ return next;
+ }
+
async render(): Promise {
+ const gen = ++this.renderGeneration;
+ this.destroy();
this.container.empty();
this.container.addClass('wl-custom-lists');
@@ -761,6 +772,7 @@ export class CustomListsTab {
this.currentList = await this.manager.loadList(this.activeListName);
}
+ if (gen !== this.renderGeneration) return; // stale render
this.container.empty();
this.buildSubTabs();
@@ -828,7 +840,7 @@ export class CustomListsTab {
});
}
- const addBtn = tabBar.createEl('button', { cls: 'wl-cl-sub-tab-add', text: '+' });
+ const addBtn = tabBar.createEl('button', { cls: 'wl-cl-sub-tab-add wl-btn-success', text: '+' });
addBtn.addEventListener('click', () => this.promptCreateList());
}
@@ -906,7 +918,7 @@ export class CustomListsTab {
const countEl = header.createSpan({ cls: 'wl-results-count wl-cl-count' });
const toolbar = header.createDiv({ cls: 'wl-cl-toolbar' });
- // Notes — Bug 2: opens NotesModal instead of raw file
+ // Notes — opens NotesModal
const notesBtn = toolbar.createEl('button', { cls: 'wl-btn wl-btn-sm', text: 'Notes' });
notesBtn.addEventListener('click', () => {
new NotesModal(
@@ -945,12 +957,17 @@ export class CustomListsTab {
const colIds = new Set(cols.map(c => c.id));
for (const row of list.rows) {
for (const key of Object.keys(row)) {
- if (key !== 'id' && key !== 'name' && !colIds.has(key)) {
+ if (key !== 'id' && key !== 'name' && key !== 'checked' && !colIds.has(key)) {
delete (row as Record)[key];
}
}
}
list.columns = cols;
+ for (const col of cols) {
+ if (col.type === 'number' && col.autoTime) {
+ this.autoPopulateTimeColumn(list, col);
+ }
+ }
await this.manager.saveList(list);
await this.render();
},
@@ -963,19 +980,25 @@ export class CustomListsTab {
cls: 'wl-search-input',
attr: { type: 'text', placeholder: 'Search...', value: this.searchQuery },
});
+ let searchDebounce: number | null = null;
searchInput.addEventListener('input', () => {
this.searchQuery = searchInput.value;
- if (this._tableContainer && this._countEl) {
- this._tableContainer.empty();
- this.buildTable(this._tableContainer, list, this._countEl);
- }
+ if (searchDebounce !== null) window.clearTimeout(searchDebounce);
+ searchDebounce = window.setTimeout(() => {
+ searchDebounce = null;
+ if (this._tableContainer && this._countEl) {
+ this._tableContainer.empty();
+ this.buildTable(this._tableContainer, list, this._countEl);
+ }
+ }, 250);
});
- // Draft warning
- view.createDiv({
- cls: 'wl-cl-draft-banner',
- text: '⚠ This is a draft list. Titles here are not included in any stats or counts.',
- });
+ if (this.plugin.settings.showHintBanners) {
+ view.createDiv({
+ cls: 'wl-cl-draft-banner',
+ text: '⚠ This is a draft list. Titles here are not included in any stats or counts.',
+ });
+ }
// Table
const tableWrap = view.createDiv({ cls: 'wl-cl-table-wrap' });
@@ -1074,7 +1097,22 @@ export class CustomListsTab {
hRow.createDiv({ cls: 'wl-cl-th wl-cl-th-tick' }); // tick column header
hRow.createDiv({ cls: 'wl-cl-th wl-cl-th-num', text: '#' });
hRow.createDiv({ cls: 'wl-cl-th', text: 'Name' });
- for (const col of nonLockedCols) hRow.createDiv({ cls: 'wl-cl-th', text: col.label });
+ for (const col of nonLockedCols) {
+ const th = hRow.createDiv({ cls: 'wl-cl-th' });
+ th.createSpan({ text: col.label });
+ if (col.type === 'number' && col.autoTime) {
+ const refreshBtn = th.createSpan({ cls: 'wl-cl-autotime-refresh', text: '↻' });
+ refreshBtn.title = 'Refresh remaining time values';
+ refreshBtn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ this.autoPopulateTimeColumn(list, col);
+ void this.manager.saveList(list).then(() => {
+ container.empty();
+ this.buildTable(container, list, countEl);
+ });
+ });
+ }
+ }
hRow.createDiv({ cls: 'wl-cl-th wl-cl-th-actions' });
// Body
@@ -1086,7 +1124,7 @@ export class CustomListsTab {
const tr = tbody.createDiv({ cls: `wl-cl-tr wl-cl-tr-body${isChecked ? ' wl-cl-tr-checked' : ''}` });
tr.dataset.rowId = row.id;
- // Tick button (Feature 5)
+ // Tick button
const tickCell = tr.createDiv({ cls: 'wl-cl-td wl-cl-td-tick' });
const tickBtn = tickCell.createDiv({ cls: `wl-cl-tick-btn${isChecked ? ' is-checked' : ''}` });
tickBtn.title = isChecked ? 'Uncheck row' : 'Check row';
@@ -1161,7 +1199,7 @@ export class CustomListsTab {
// Add row
const addRowEl = container.createDiv({ cls: 'wl-cl-add-row' });
- addRowEl.createEl('button', { cls: 'wl-btn wl-btn-sm', text: '+ add row' })
+ addRowEl.createEl('button', { cls: 'wl-btn wl-btn-sm wl-btn-success', text: '+ add row' })
.addEventListener('click', () => {
const newRow: CustomListRow = { id: this.manager.generateRowId(list.rows), name: '' };
list.rows.push(newRow);
@@ -1214,25 +1252,6 @@ export class CustomListsTab {
): void {
const prevVal = String((row as Record)['name'] ?? '');
- if (Platform.isMobile) {
- new MobileCellEditModal(
- this.plugin.app,
- 'Edit Name',
- 'text',
- prevVal,
- async (value) => {
- const newVal = value.trim();
- const isDup = this.duplicatedRowIds.has(row.id);
- (row as Record)['name'] = newVal;
- if (isDup && newVal !== prevVal) this.duplicatedRowIds.delete(row.id);
- await this.manager.saveList(list);
- this.renderNameCell(cell, row, list, countEl, nonLockedCols, tableContainer);
- },
- () => this.dataManager.getTitles(),
- ).open();
- return;
- }
-
cell.empty();
cell.addClass('wl-cl-editing');
this._escapeKeyHandler = (e: KeyboardEvent) => {
@@ -1244,6 +1263,11 @@ export class CustomListsTab {
};
activeDocument.addEventListener('keydown', this._escapeKeyHandler, true);
activeDocument.addEventListener('keyup', this._escapeKeyHandler, true);
+ const escHandler = this._escapeKeyHandler;
+ this.addCleanup(() => {
+ activeDocument.removeEventListener('keydown', escHandler, true);
+ activeDocument.removeEventListener('keyup', escHandler, true);
+ });
// Mobile viewport: scroll active element into view when virtual keyboard shrinks viewport
const vpResizeHandler = (): void => {
@@ -1251,6 +1275,9 @@ export class CustomListsTab {
if (active) active.scrollIntoView({ behavior: 'smooth', block: 'center' });
};
if (window.visualViewport) window.visualViewport.addEventListener('resize', vpResizeHandler);
+ this.addCleanup(() => {
+ if (window.visualViewport) window.visualViewport.removeEventListener('resize', vpResizeHandler);
+ });
window.setTimeout(() => cell.scrollIntoView({ block: 'nearest', behavior: 'smooth' }), 50);
const wrap = cell.createDiv({ cls: 'wl-cl-autofill-wrap' });
@@ -1270,8 +1297,14 @@ export class CustomListsTab {
clearDropdown();
const q = input.value.toLowerCase();
if (!q) return;
- const suggestions = this.dataManager.getTitles()
- .filter(t => t.title.toLowerCase().includes(q))
+ // Suggestions come from both the Watchlist and the Reading list (Books + Manga).
+ const reading = this.plugin.readingDataManager;
+ const suggestions: { title: string; meta: string }[] = [
+ ...this.dataManager.getTitles().map(t => ({ title: t.title, meta: `${t.type} · ${t.status}` })),
+ ...reading.getBooks().map(b => ({ title: b.title, meta: `Book · ${b.status}` })),
+ ...reading.getMangaList().map(m => ({ title: m.title, meta: `Manga · ${m.status}` })),
+ ]
+ .filter(s => s.title.toLowerCase().includes(q))
.slice(0, 10);
if (!suggestions.length) return;
const rect = input.getBoundingClientRect();
@@ -1279,11 +1312,11 @@ export class CustomListsTab {
dropdown.style.top = `${rect.bottom}px`;
dropdown.style.left = `${rect.left}px`;
dropdown.style.width = `${rect.width}px`;
- for (const title of suggestions) {
+ for (const sug of suggestions) {
const item = dropdown.createDiv({ cls: 'wl-result-item' });
- item.createDiv({ cls: 'wl-result-title', text: title.title });
- item.createDiv({ cls: 'wl-result-meta', text: `${title.type} · ${title.status}` });
- item.addEventListener('mousedown', (e) => { e.preventDefault(); input.value = title.title; clearDropdown(); });
+ item.createDiv({ cls: 'wl-result-title', text: sug.title });
+ item.createDiv({ cls: 'wl-result-meta', text: sug.meta });
+ item.addEventListener('mousedown', (e) => { e.preventDefault(); input.value = sug.title; clearDropdown(); });
}
});
@@ -1313,7 +1346,7 @@ export class CustomListsTab {
input.addEventListener('blur', () => void doSave(false));
input.addEventListener('keydown', (e) => {
- // QoL 2: keyboard navigation in suggestion dropdown
+ // Keyboard navigation in suggestion dropdown
if (dropdown) {
const items = Array.from(dropdown.querySelectorAll('.wl-result-item'));
if (e.key === 'ArrowDown') {
@@ -1393,29 +1426,6 @@ export class CustomListsTab {
): void {
const currentVal = (row as Record)[col.id];
- if (Platform.isMobile && col.type !== 'select') {
- const initialStr = currentVal !== undefined && currentVal !== null ? String(currentVal) : '';
- new MobileCellEditModal(
- this.plugin.app,
- `Edit ${col.label}`,
- col.type === 'number' ? 'number' : 'text',
- initialStr,
- async (value) => {
- let newVal: string | number | undefined;
- if (col.type === 'number') {
- const n = parseFloat(value);
- newVal = isNaN(n) ? undefined : n;
- } else {
- newVal = value === '' ? undefined : value;
- }
- (row as Record)[col.id] = newVal;
- await this.manager.saveList(list);
- this.renderCustomCell(cell, row, col, list);
- },
- ).open();
- return;
- }
-
cell.empty();
cell.addClass('wl-cl-editing');
@@ -1425,6 +1435,9 @@ export class CustomListsTab {
if (active) active.scrollIntoView({ behavior: 'smooth', block: 'center' });
};
if (window.visualViewport) window.visualViewport.addEventListener('resize', vpResizeHandler);
+ this.addCleanup(() => {
+ if (window.visualViewport) window.visualViewport.removeEventListener('resize', vpResizeHandler);
+ });
window.setTimeout(() => cell.scrollIntoView({ block: 'nearest', behavior: 'smooth' }), 50);
let getValue: () => string | number | undefined;
@@ -1499,7 +1512,24 @@ export class CustomListsTab {
}
}
- // ── Keyboard navigation (QoL 1) ────────────────────────────────────────────
+ // ── Auto-populate time column ──────────────────────────────────────────────
+
+ private autoPopulateTimeColumn(list: CustomList, col: CustomListColumn): void {
+ const titles = this.dataManager.getTitles();
+ for (const row of list.rows) {
+ const rowName = String(row['name'] ?? '').trim();
+ if (!rowName) continue;
+ const match = titles.find(t => t.title === rowName);
+ if (!match) {
+ row[col.id] = 'Not found';
+ continue;
+ }
+ const remainingMinutes = this.dataManager.calcTimeRemainingForModal(match);
+ row[col.id] = remainingMinutes;
+ }
+ }
+
+ // ── Keyboard navigation ─────────────────────────────────────────────────────
/** Returns editable cells (Name + custom) in a row, skipping # and actions. */
private getEditableCells(tr: Element): Element[] {
diff --git a/src/DashboardTab.ts b/src/DashboardTab.ts
index 465a2ca..7dfea22 100644
--- a/src/DashboardTab.ts
+++ b/src/DashboardTab.ts
@@ -1,88 +1,244 @@
import type WatchLogPlugin from './main';
import type { DataManager } from './DataManager';
-import type { TagDefinition } from './types';
-import { formatTime, getThemedColor } from './types';
+import type { ReadingDataManager } from './ReadingDataManager';
+import type { Book, Manga, TagDefinition, WatchLogTitle } from './types';
+import { formatTime, getThemedColor, getReadingTypeColor } from './types';
const RING_CIRCUMFERENCE = 2 * Math.PI * 45; // r=45
+/** Aggregated reading stats for one kind (books or manga). */
+interface ReadingAggregate {
+ left: number; // count of Reading + Plan to Read (To be released excluded)
+ read: number; // sum of pages (books) / chapters (manga) read
+ total: number; // sum of total pages / chapters
+ volumesRead: number;
+ totalVolumes: number;
+}
+
export class DashboardTab {
private container: HTMLElement;
private plugin: WatchLogPlugin;
private dataManager: DataManager;
+ private readingData: ReadingDataManager;
- constructor(container: HTMLElement, plugin: WatchLogPlugin, dataManager: DataManager) {
+ constructor(
+ container: HTMLElement,
+ plugin: WatchLogPlugin,
+ dataManager: DataManager,
+ readingData: ReadingDataManager,
+ ) {
this.container = container;
this.plugin = plugin;
this.dataManager = dataManager;
+ this.readingData = readingData;
}
render(): void {
this.container.empty();
this.container.addClass('wl-dashboard');
- this.renderCards();
- this.renderSummaryMetrics();
- this.renderSuggestions();
+ // Precompute shared collections once per render so each subrenderer
+ // doesn't trigger another full scan of the titles array.
+ const allTitles = this.dataManager.getTitles();
+ const byType = new Map();
+ for (const t of allTitles) {
+ const arr = byType.get(t.type);
+ if (arr) arr.push(t);
+ else byType.set(t.type, [t]);
+ }
+ const planTitles = allTitles.filter((t) => t.status === 'Plan to watch');
+
+ // Reading data sourced from ReadingDataManager so footer counts span both datasets.
+ const books = this.readingData.getBooks();
+ const manga = this.readingData.getMangaList();
+
+ // Library + Completed counts sum watch + reading (books + manga). A reading item
+ // only counts as completed when its status is exactly 'Completed', matching the watch side.
+ const titleCount = allTitles.length + books.length + manga.length;
+ const completedCount =
+ allTitles.reduce((n, t) => n + (t.status === 'Completed' ? 1 : 0), 0) +
+ books.reduce((n, b) => n + (b.status === 'Completed' ? 1 : 0), 0) +
+ manga.reduce((n, m) => n + (m.status === 'Completed' ? 1 : 0), 0);
+
+ // Reading aggregates computed once per render (not per data change).
+ const booksAgg = this.aggregateBooks(books);
+ const mangaAgg = this.aggregateManga(manga);
+
+ this.renderCards(allTitles, byType, booksAgg, mangaAgg);
+ this.renderSummaryMetrics(titleCount, completedCount);
+ this.renderSuggestions(planTitles);
this.renderRecentlyWatched();
this.renderRecentlyAdded();
}
- private renderCards(): void {
+ private statsFor(titles: WatchLogTitle[]): { watched: number; total: number } {
+ const EXCLUDED = new Set(['Dropped', 'To be released']);
+ let total = 0;
+ let watched = 0;
+ for (const t of titles) {
+ if (EXCLUDED.has(t.status)) continue;
+ total++;
+ if (t.status === 'Completed') watched++;
+ }
+ return { watched, total };
+ }
+
+ private renderCards(
+ allTitles: WatchLogTitle[],
+ byType: Map,
+ booksAgg: ReadingAggregate,
+ mangaAgg: ReadingAggregate,
+ ): void {
const isRect = this.plugin.settings.dashboardCardStyle === 'rectangles';
const grid = this.container.createDiv({ cls: 'wl-rings-grid' });
- // Always-present Total card
- const totalStats = this.dataManager.getStatsByType('All');
+ // Unified Total / Time card, anchored at the top (spans full width).
+ const totalStats = this.statsFor(allTitles);
const totalPercent =
totalStats.total === 0
? 0
: Math.round((totalStats.watched / totalStats.total) * 100);
- if (isRect) {
- this.renderRect(grid, 'Total', '#7F77DD', totalPercent, totalStats);
- } else {
- this.renderRing(grid, 'Total', '#7F77DD', totalPercent, totalStats);
- }
+ const timeWatched = this.dataManager.getTotalTimeWatched();
+ const timeRemaining = this.dataManager.getTotalTimeRemaining();
+ this.renderUnifiedCard(
+ grid, isRect, totalPercent, totalStats,
+ formatTime(timeWatched), formatTime(timeRemaining),
+ );
// One card per type
for (const type of this.plugin.settings.types) {
- const stats = this.dataManager.getStatsByType(type.name);
+ const stats = this.statsFor(byType.get(type.name) ?? []);
const percent =
stats.total === 0 ? 0 : Math.round((stats.watched / stats.total) * 100);
if (isRect) {
- this.renderRect(grid, type.name, type.color, percent, stats);
+ const item = grid.createDiv({ cls: 'wl-rect-item' });
+ this.fillRect(item, type.name, type.color, percent, stats);
+ // Reserve the sublabel line so type cards match the taller Books/Manga cards.
+ item.createDiv({ cls: 'wl-rect-subline', text: ' ' });
} else {
- this.renderRing(grid, type.name, type.color, percent, stats);
+ const item = grid.createDiv({ cls: 'wl-ring-item' });
+ this.fillRing(item, type.name, type.color, percent, stats);
+ item.createDiv({ cls: 'wl-ring-subtitle wl-ring-subline', text: ' ' });
}
}
- // Time Watched card
- const timeWatched = this.dataManager.getTotalTimeWatched();
+ // Reading cards (Books + Manga) at the end of the flow.
+ this.renderReadingCard(grid, isRect, 'Books', booksAgg);
+ this.renderReadingCard(grid, isRect, 'Manga', mangaAgg);
+ }
+
+ // ── Unified Total / Time card ──────────────────────────────────────────────────
+
+ private renderUnifiedCard(
+ grid: HTMLElement,
+ isRect: boolean,
+ totalPercent: number,
+ totalStats: { watched: number; total: number },
+ timeWatchedStr: string,
+ timeRemainingStr: string,
+ ): void {
+ // Rect cards carry an outer border; ring cards don't — match the active style.
+ const card = grid.createDiv({
+ cls: isRect ? 'wl-dash-unified wl-dash-unified-bordered' : 'wl-dash-unified',
+ });
+
+ // Segment 1 — Total (full card content, matching the chosen card style).
+ // Ring content is centered; rect content stretches so its bar spans the segment.
+ const seg1 = card.createDiv({ cls: isRect ? 'wl-dash-seg' : 'wl-dash-seg wl-dash-seg-center' });
if (isRect) {
- this.renderTimeRect(grid, 'Time Watched', '#1D9E75', formatTime(timeWatched));
+ // Rect content goes straight into the segment so its border is the card's.
+ this.fillRect(seg1, 'Total', '#7F77DD', totalPercent, totalStats);
} else {
- this.renderTimeRing(grid, 'Time Watched', '#1D9E75', formatTime(timeWatched));
+ // Rings are borderless; wrap so the decorative accent ring matches type cards.
+ this.fillRing(seg1.createDiv({ cls: 'wl-ring-item' }), 'Total', '#7F77DD', totalPercent, totalStats);
}
- // Time Remaining card
- const timeRemaining = this.dataManager.getTotalTimeRemaining();
+ // Segments 2 & 3 — Time Watched / Remaining: label + value, no bar.
+ this.renderTimeSegment(card, 'Time Watched', timeWatchedStr);
+ this.renderTimeSegment(card, 'Time Remaining', timeRemainingStr);
+ }
+
+ private renderTimeSegment(card: HTMLElement, label: string, value: string): void {
+ const seg = card.createDiv({ cls: 'wl-dash-seg wl-dash-seg-center' });
+ seg.createDiv({ cls: 'wl-dash-seg-label', text: label });
+ seg.createDiv({ cls: 'wl-dash-seg-value', text: value });
+ }
+
+ // ── Reading cards (Books / Manga) ──────────────────────────────────────────────
+
+ private aggregateBooks(books: Book[]): ReadingAggregate {
+ const agg: ReadingAggregate = { left: 0, read: 0, total: 0, volumesRead: 0, totalVolumes: 0 };
+ for (const b of books) {
+ if (b.status === 'To be released') continue; // excluded entirely
+ if (b.status === 'Reading' || b.status === 'Plan to Read') agg.left++;
+ agg.read += b.pagesRead;
+ agg.total += b.totalPages;
+ }
+ return agg;
+ }
+
+ private aggregateManga(manga: Manga[]): ReadingAggregate {
+ const agg: ReadingAggregate = { left: 0, read: 0, total: 0, volumesRead: 0, totalVolumes: 0 };
+ for (const m of manga) {
+ if (m.status === 'To be released') continue; // excluded entirely
+ if (m.status === 'Reading' || m.status === 'Plan to Read') agg.left++;
+ agg.read += m.chaptersRead;
+ agg.total += m.totalChapters;
+ agg.volumesRead += m.volumesRead;
+ agg.totalVolumes += m.totalVolumes;
+ }
+ return agg;
+ }
+
+ private renderReadingCard(
+ grid: HTMLElement,
+ isRect: boolean,
+ label: 'Books' | 'Manga',
+ agg: ReadingAggregate,
+ ): void {
+ const percent = agg.total > 0 ? Math.round((agg.read / agg.total) * 100) : 0;
+ // Configured Reading type color (Books → book, Manga → manga), mirroring how
+ // watchlist type cards use their type color.
+ const color = getReadingTypeColor(label === 'Books' ? 'book' : 'manga', this.plugin.settings);
+
+ // Single inline subline under the bar: Books → pages; Manga → chapters · vol.
+ const subline =
+ label === 'Books'
+ ? `${agg.read} / ${agg.total} pages`
+ : `${agg.read} / ${agg.total} chapters · ${agg.volumesRead} / ${agg.totalVolumes} vol`;
+
if (isRect) {
- this.renderTimeRect(grid, 'Time Remaining', '#BA7517', formatTime(timeRemaining));
+ const item = grid.createDiv({ cls: 'wl-rect-item' });
+ const topRow = item.createDiv({ cls: 'wl-rect-top' });
+ topRow.createSpan({ cls: 'wl-rect-label', text: label });
+ topRow.createSpan({ cls: 'wl-rect-unwatched', text: `${agg.left} left` });
+ item.createDiv({ cls: 'wl-rect-value', text: `${percent}%` });
+ const barWrap = item.createDiv({ cls: 'wl-rect-bar-wrap' });
+ const bar = barWrap.createDiv({ cls: 'wl-rect-bar' });
+ bar.style.width = `${percent}%`;
+ bar.style.backgroundColor = color;
+ item.createDiv({ cls: 'wl-rect-subline', text: subline });
} else {
- this.renderTimeRing(grid, 'Time Remaining', '#BA7517', formatTime(timeRemaining));
+ const item = grid.createDiv({ cls: 'wl-ring-item' });
+ const svg = this.makeRingSvg(percent, color, `${percent}%`, '', true);
+ item.appendChild(svg);
+ item.createDiv({ cls: 'wl-ring-label', text: label });
+ item.createDiv({ cls: 'wl-ring-subtitle', text: `${agg.left} left` });
+ item.createDiv({ cls: 'wl-ring-subtitle wl-ring-subline', text: subline });
}
}
- // ── Ring (circle) cards ───────────────────────────────────────────────────────
+ // ── Shared card content builders ───────────────────────────────────────────────
- private renderRing(
- parent: HTMLElement,
- label: string,
- color: string,
+ /** Builds the progress-ring SVG. When `themeStroke` is set, the arc colour is
+ * applied via inline style so CSS vars (e.g. var(--wl-accent)) resolve. */
+ private makeRingSvg(
percent: number,
- stats: { watched: number; total: number },
- ): void {
- const item = parent.createDiv({ cls: 'wl-ring-item' });
-
+ color: string,
+ centerText: string,
+ subText: string,
+ themeStroke = false,
+ ): SVGSVGElement {
const svgNS = 'http://www.w3.org/2000/svg';
const svg = activeDocument.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 120 120');
@@ -104,102 +260,59 @@ export class DashboardTab {
arc.setAttribute('cy', '60');
arc.setAttribute('r', '45');
arc.setAttribute('fill', 'none');
- arc.setAttribute('stroke', color);
+ if (themeStroke) arc.style.stroke = color;
+ else arc.setAttribute('stroke', color);
arc.setAttribute('stroke-width', '10');
arc.setAttribute('stroke-linecap', 'round');
arc.setAttribute('stroke-dasharray', String(RING_CIRCUMFERENCE));
- const dashOffset = RING_CIRCUMFERENCE * (1 - percent / 100);
- arc.setAttribute('stroke-dashoffset', String(dashOffset));
+ arc.setAttribute('stroke-dashoffset', String(RING_CIRCUMFERENCE * (1 - percent / 100)));
arc.setAttribute('transform', 'rotate(-90 60 60)');
arc.addClass('wl-ring-arc');
svg.appendChild(arc);
const text = activeDocument.createElementNS(svgNS, 'text');
text.setAttribute('x', '60');
- text.setAttribute('y', '55');
+ text.setAttribute('y', subText ? '55' : '60');
text.setAttribute('text-anchor', 'middle');
text.setAttribute('dominant-baseline', 'middle');
text.addClass('wl-ring-percent-text');
- text.textContent = `${percent}%`;
+ text.textContent = centerText;
svg.appendChild(text);
- const subText = activeDocument.createElementNS(svgNS, 'text');
- subText.setAttribute('x', '60');
- subText.setAttribute('y', '72');
- subText.setAttribute('text-anchor', 'middle');
- subText.addClass('wl-ring-sub-text');
- subText.textContent = `${stats.watched}/${stats.total}`;
- svg.appendChild(subText);
+ if (subText) {
+ const sub = activeDocument.createElementNS(svgNS, 'text');
+ sub.setAttribute('x', '60');
+ sub.setAttribute('y', '72');
+ sub.setAttribute('text-anchor', 'middle');
+ sub.addClass('wl-ring-sub-text');
+ sub.textContent = subText;
+ svg.appendChild(sub);
+ }
+ return svg;
+ }
+
+ private fillRing(
+ item: HTMLElement,
+ label: string,
+ color: string,
+ percent: number,
+ stats: { watched: number; total: number },
+ ): void {
+ const svg = this.makeRingSvg(percent, color, `${percent}%`, `${stats.watched}/${stats.total}`);
item.appendChild(svg);
item.createDiv({ cls: 'wl-ring-label', text: label });
const unwatched = stats.total - stats.watched;
item.createDiv({ cls: 'wl-ring-subtitle', text: `${unwatched} unwatched` });
}
- private renderTimeRing(
- parent: HTMLElement,
- label: string,
- color: string,
- timeStr: string,
- ): void {
- const item = parent.createDiv({ cls: 'wl-ring-item' });
-
- const svgNS = 'http://www.w3.org/2000/svg';
- const svg = activeDocument.createElementNS(svgNS, 'svg');
- svg.setAttribute('viewBox', '0 0 120 120');
- svg.setAttribute('width', '110');
- svg.setAttribute('height', '110');
- svg.addClass('wl-ring-svg');
-
- const bgCircle = activeDocument.createElementNS(svgNS, 'circle');
- bgCircle.setAttribute('cx', '60');
- bgCircle.setAttribute('cy', '60');
- bgCircle.setAttribute('r', '45');
- bgCircle.setAttribute('fill', 'none');
- bgCircle.setAttribute('stroke-width', '10');
- bgCircle.addClass('wl-ring-track');
- svg.appendChild(bgCircle);
-
- const arc = activeDocument.createElementNS(svgNS, 'circle');
- arc.setAttribute('cx', '60');
- arc.setAttribute('cy', '60');
- arc.setAttribute('r', '45');
- arc.setAttribute('fill', 'none');
- arc.setAttribute('stroke', color);
- arc.setAttribute('stroke-width', '10');
- arc.setAttribute('stroke-linecap', 'round');
- arc.setAttribute('stroke-dasharray', String(RING_CIRCUMFERENCE));
- arc.setAttribute('stroke-dashoffset', '0');
- arc.setAttribute('transform', 'rotate(-90 60 60)');
- arc.addClass('wl-ring-arc');
- svg.appendChild(arc);
-
- const text = activeDocument.createElementNS(svgNS, 'text');
- text.setAttribute('x', '60');
- text.setAttribute('y', '60');
- text.setAttribute('text-anchor', 'middle');
- text.setAttribute('dominant-baseline', 'middle');
- text.addClass('wl-ring-time-text');
- text.textContent = timeStr;
- svg.appendChild(text);
-
- item.appendChild(svg);
- item.createDiv({ cls: 'wl-ring-label', text: label });
- item.createDiv({ cls: 'wl-ring-subtitle', text: 'total' });
- }
-
- // ── Rectangle cards ───────────────────────────────────────────────────────────
-
- private renderRect(
- parent: HTMLElement,
+ private fillRect(
+ item: HTMLElement,
label: string,
color: string,
percent: number,
stats: { watched: number; total: number },
): void {
- const item = parent.createDiv({ cls: 'wl-rect-item' });
-
const topRow = item.createDiv({ cls: 'wl-rect-top' });
topRow.createSpan({ cls: 'wl-rect-label', text: label });
const unwatched = stats.total - stats.watched;
@@ -213,33 +326,12 @@ export class DashboardTab {
bar.style.backgroundColor = color;
}
- private renderTimeRect(
- parent: HTMLElement,
- label: string,
- color: string,
- timeStr: string,
- ): void {
- const item = parent.createDiv({ cls: 'wl-rect-item' });
-
- const topRow = item.createDiv({ cls: 'wl-rect-top' });
- topRow.createSpan({ cls: 'wl-rect-label', text: label });
- topRow.createSpan({ cls: 'wl-rect-unwatched', text: 'total' });
-
- item.createDiv({ cls: 'wl-rect-value', text: timeStr });
-
- const barWrap = item.createDiv({ cls: 'wl-rect-bar-wrap' });
- const bar = barWrap.createDiv({ cls: 'wl-rect-bar wl-rect-bar-full' });
- bar.style.backgroundColor = color;
- }
-
// ── Summary metrics ───────────────────────────────────────────────────────────
- private renderSummaryMetrics(): void {
+ private renderSummaryMetrics(titleCount: number, completedCount: number): void {
const section = this.container.createDiv({ cls: 'wl-summary-metrics' });
- const titles = this.dataManager.getTitles();
- const completed = this.dataManager.getCompletedCount();
- this.renderMetricRow(section, 'Titles in library', String(titles.length));
- this.renderMetricRow(section, 'Completed', String(completed));
+ this.renderMetricRow(section, 'Titles in library', String(titleCount));
+ this.renderMetricRow(section, 'Completed', String(completedCount));
}
private renderMetricRow(parent: HTMLElement, label: string, value: string): void {
@@ -250,8 +342,7 @@ export class DashboardTab {
// ── Suggestions ──────────────────────────────────────────────────────────────
- private renderSuggestions(): void {
- const planTitles = this.dataManager.getTitles().filter((t) => t.status === 'Plan to watch');
+ private renderSuggestions(planTitles: WatchLogTitle[]): void {
if (planTitles.length === 0) return;
const section = this.container.createDiv({ cls: 'wl-suggestions' });
diff --git a/src/DataManager.ts b/src/DataManager.ts
index 5c9d1b1..443a65b 100644
--- a/src/DataManager.ts
+++ b/src/DataManager.ts
@@ -1,15 +1,42 @@
import { App, TFile, normalizePath } from 'obsidian';
import type WatchLogPlugin from './main';
-import type { WatchLogData, WatchLogTitle, WatchLogGroup, AirtimeEntry, MaybeTitle, SavedFilterPreset } from './types';
+import type { WatchLogData, WatchLogTitle, WatchLogGroup, AirtimeEntry, MaybeTitle, SavedFilterPreset, WatchLogPluginSettings, Book, Manga } from './types';
import type { HistoryManager } from './HistoryManager';
import { formatHistoryDate } from './HistoryManager';
+function isValidWatchLogData(data: unknown): data is WatchLogData {
+ return (
+ data !== null &&
+ typeof data === 'object' &&
+ Array.isArray((data as { titles?: unknown }).titles)
+ );
+}
+
export class DataManager {
private plugin: WatchLogPlugin;
private app: App;
private data: WatchLogData;
private changeListeners: Array<() => void> = [];
private historyManager: HistoryManager | null = null;
+ // Debounced save state for high-frequency edits (e.g. rapid episode clicks).
+ // In-memory mutations are applied immediately; the disk write coalesces.
+ private pendingSaveTimer: number | null = null;
+ private pendingMdTitleIds: Set = new Set();
+ private readonly EPISODE_SAVE_DEBOUNCE_MS = 500;
+ // Timestamp of the last self-initiated saveOnly. The 'raw' file watcher uses
+ // this to ignore the echo of our own writes, which otherwise would re-load
+ // data.json and notify listeners on every debounced episode-click save.
+ private lastSelfSaveTime = 0;
+ private readonly SELF_SAVE_ECHO_WINDOW_MS = 2000;
+
+ // Debounced batched save for poster URL updates. Updates land in memory
+ // immediately; the disk write coalesces to ~once every 5s.
+ private posterSaveTimer: number | null = null;
+ private readonly POSTER_SAVE_DEBOUNCE_MS = 5000;
+
+ // Generic queued-save debounce for cross-cutting saves (settings, drafts, lists)
+ private queuedSaveTimer: number | null = null;
+ private readonly QUEUED_SAVE_DEBOUNCE_MS = 100;
setHistoryManager(hm: HistoryManager): void {
this.historyManager = hm;
@@ -22,38 +49,41 @@ export class DataManager {
}
async load(): Promise {
- const loaded = (await this.plugin.loadData()) as WatchLogData | null;
- if (loaded) {
- this.data = loaded;
- } else {
- this.data = { titles: [], groups: [], settings: {} };
+ const raw: unknown = await this.plugin.loadData();
+ this.data = isValidWatchLogData(raw)
+ ? raw
+ : { titles: [], groups: [], settings: {} };
+ const changed = this.migrateData();
+ if (changed) {
+ await this.saveOnly();
}
- this.migrateData();
}
- private migrateData(): void {
- // Ensure core arrays exist (data.json may be missing or partial on first install)
+ private migrateData(): boolean {
+ let changed = false;
if (!Array.isArray(this.data.titles)) {
this.data.titles = [];
+ changed = true;
}
if (!Array.isArray((this.data as unknown as Record)['groups'])) {
this.data.groups = [];
+ changed = true;
}
- // Ensure airtime array exists
if (!Array.isArray(this.data.airtime)) {
this.data.airtime = [];
+ changed = true;
}
for (const title of this.data.titles) {
- // Ensure dateAdded is a full ISO timestamp (old data may be date-only "YYYY-MM-DD")
if (!title.dateAdded) {
title.dateAdded = new Date().toISOString();
+ changed = true;
} else if (!title.dateAdded.includes('T')) {
title.dateAdded = new Date(title.dateAdded).toISOString();
+ changed = true;
}
- // Migrate lastInteracted → dateModified
if (!title.dateModified) {
const raw = title as unknown as Record;
const lastInteracted = raw['lastInteracted'];
@@ -62,19 +92,194 @@ export class DataManager {
} else {
title.dateModified = title.dateAdded;
}
+ changed = true;
}
+
+ for (const season of (title.seasons ?? [])) {
+ if (!Array.isArray(season.skippedEpisodes)) {
+ season.skippedEpisodes = [];
+ changed = true;
+ }
+ }
+
+ if (title.posterUrl === undefined) { title.posterUrl = ''; changed = true; }
+ if (title.manualPosterUrl === undefined) { title.manualPosterUrl = ''; changed = true; }
+ if (title.anilistId === undefined) { title.anilistId = 0; changed = true; }
+ if (title.communityRating === undefined) { title.communityRating = 0; changed = true; }
+ if (title.communityVotes === undefined) { title.communityVotes = 0; changed = true; }
+ if (title.communitySource === undefined) { title.communitySource = ''; changed = true; }
+ if (title.communityRatingLastFetched === undefined) { title.communityRatingLastFetched = ''; changed = true; }
+ }
+
+ if (!this.data.posterRetryDone) {
+ for (const title of this.data.titles) {
+ if (title.posterUrl === 'none') {
+ title.posterUrl = '';
+ }
+ }
+ this.data.posterRetryDone = true;
+ changed = true;
+ }
+ return changed;
+ }
+
+ /**
+ * Silent poster URL update. Writes the value in memory and schedules a
+ * debounced disk save (5s). Does NOT notify listeners — the card that
+ * triggered the fetch updates its own
directly.
+ */
+ updatePosterUrl(titleId: string, url: string): void {
+ const title = this.data.titles.find((t) => t.id === titleId);
+ if (title) {
+ title.posterUrl = url;
+ this.schedulePosterSave();
}
}
+ /**
+ * Silent community-rating update. Writes the values in memory and saves
+ * to disk without notifying listeners — the caller is responsible for
+ * any targeted UI refresh.
+ */
+ updateCommunityRating(
+ id: string,
+ rating: number,
+ votes: number,
+ source: '' | 'imdb' | 'mal' | 'anilist' | 'tmdb',
+ ): void {
+ const title = this.data.titles.find((t) => t.id === id);
+ if (!title) return;
+ title.communityRating = rating;
+ title.communityVotes = votes;
+ title.communitySource = source;
+ title.communityRatingLastFetched = new Date().toISOString();
+ void this.saveOnly();
+ }
+
+ private schedulePosterSave(): void {
+ if (this.posterSaveTimer !== null) return;
+ this.posterSaveTimer = window.setTimeout(() => {
+ this.posterSaveTimer = null;
+ void this.saveOnly();
+ }, this.POSTER_SAVE_DEBOUNCE_MS);
+ }
+
+ /** Flush a pending debounced poster save now (e.g. on plugin unload). */
+ flushPosterSave(): void {
+ if (this.posterSaveTimer !== null) {
+ window.clearTimeout(this.posterSaveTimer);
+ this.posterSaveTimer = null;
+ void this.saveOnly();
+ }
+ }
+
+ /** Synchronously flush a pending poster save during plugin unload. */
+ flushPosterSaveSync(): void {
+ if (this.posterSaveTimer !== null) {
+ window.clearTimeout(this.posterSaveTimer);
+ this.posterSaveTimer = null;
+ this.lastSelfSaveTime = Date.now();
+ void this.plugin.saveData(this.data);
+ }
+ }
+
+ /** Public access to the full in-memory data snapshot. */
+ getData(): WatchLogData {
+ return this.data;
+ }
+
+ /** Centralized debounced save for cross-cutting paths (settings, drafts, lists). */
+ queueSave(): void {
+ if (this.queuedSaveTimer !== null) {
+ window.clearTimeout(this.queuedSaveTimer);
+ }
+ this.queuedSaveTimer = window.setTimeout(() => {
+ this.queuedSaveTimer = null;
+ void this.saveOnly();
+ }, this.QUEUED_SAVE_DEBOUNCE_MS);
+ }
+
+ flushQueuedSaveSync(): void {
+ if (this.queuedSaveTimer !== null) {
+ window.clearTimeout(this.queuedSaveTimer);
+ this.queuedSaveTimer = null;
+ this.lastSelfSaveTime = Date.now();
+ void this.plugin.saveData(this.data);
+ }
+ }
+
+ /** Save settings through the centralized data path. */
+ async saveSettings(settings: WatchLogPluginSettings): Promise {
+ this.data.settings = settings;
+ await this.saveOnly();
+ }
+
private async saveOnly(): Promise {
+ this.lastSelfSaveTime = Date.now();
await this.plugin.saveData(this.data);
+ this.lastSelfSaveTime = Date.now();
}
async save(): Promise {
+ // An immediate save supersedes any pending debounced one — the in-memory
+ // state already contains those changes.
+ if (this.pendingSaveTimer !== null) {
+ window.clearTimeout(this.pendingSaveTimer);
+ this.pendingSaveTimer = null;
+ this.pendingMdTitleIds.clear();
+ }
await this.saveOnly();
this.notifyListeners();
}
+ /**
+ * Schedule a debounced silent save (no listener notification, no full re-render).
+ * Subsequent calls within the debounce window reset the timer. Callers are
+ * responsible for any targeted DOM updates.
+ */
+ scheduleEpisodeSave(titleIdForMd: string): void {
+ this.pendingMdTitleIds.add(titleIdForMd);
+ if (this.pendingSaveTimer !== null) {
+ window.clearTimeout(this.pendingSaveTimer);
+ }
+ this.pendingSaveTimer = window.setTimeout(() => {
+ void this.flushPendingSave();
+ }, this.EPISODE_SAVE_DEBOUNCE_MS);
+ }
+
+ /** Flush a pending debounced save now (e.g. on view close / plugin unload). */
+ async flushPendingSave(): Promise {
+ if (this.pendingSaveTimer !== null) {
+ window.clearTimeout(this.pendingSaveTimer);
+ this.pendingSaveTimer = null;
+ }
+ if (this.pendingMdTitleIds.size === 0) return;
+ const ids = Array.from(this.pendingMdTitleIds);
+ this.pendingMdTitleIds.clear();
+ await this.saveOnly();
+ for (const id of ids) {
+ const t = this.getTitle(id);
+ if (t) await this.updateMarkdownFile(t);
+ }
+ }
+
+ /** Synchronously flush a pending episode-save during plugin unload (no awaits). */
+ flushPendingSaveSync(): void {
+ if (this.pendingSaveTimer !== null) {
+ window.clearTimeout(this.pendingSaveTimer);
+ this.pendingSaveTimer = null;
+ }
+ if (this.pendingMdTitleIds.size === 0) return;
+ const ids = Array.from(this.pendingMdTitleIds);
+ this.pendingMdTitleIds.clear();
+ this.lastSelfSaveTime = Date.now();
+ void this.plugin.saveData(this.data);
+ for (const id of ids) {
+ const t = this.getTitle(id);
+ if (t) void this.updateMarkdownFile(t);
+ }
+ }
+
getTitles(): WatchLogTitle[] {
return this.data.titles ?? [];
}
@@ -95,7 +300,10 @@ export class DataManager {
}
await this.updateMarkdownFile(title);
const now = new Date().toISOString();
- void this.historyManager?.log(`${title.title} (${title.type}) was added on ${formatHistoryDate(now)}`);
+ void this.historyManager?.log(
+ `${title.title} (${title.type}) was added on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'added', titleName: title.title },
+ );
}
/** Add without triggering a UI re-render. Caller must call notifyChange() after the batch. */
@@ -112,6 +320,42 @@ export class DataManager {
await this.updateMarkdownFile(title);
}
+ /** Batch-add: push all titles into memory, save once, then write all MD files. */
+ async addTitleBatch(titles: WatchLogTitle[]): Promise {
+ const upcoming: WatchLogTitle[] = [];
+ for (const title of titles) {
+ if (this.canAutoAddToUpcoming(title)) {
+ title.status = 'To be released';
+ upcoming.push(title);
+ }
+ this.data.titles.push(title);
+ }
+ await this.saveOnly();
+ for (const title of upcoming) {
+ await this.autoAddToUpcoming(title);
+ }
+ for (const title of titles) {
+ await this.updateMarkdownFile(title);
+ }
+ }
+
+ /** Batch-update via mutator. Single save + single notify; MD files updated. */
+ async batchUpdate(ids: string[], mutator: (t: WatchLogTitle) => void): Promise {
+ const updated: WatchLogTitle[] = [];
+ for (const id of ids) {
+ const t = this.getTitle(id);
+ if (!t) continue;
+ mutator(t);
+ t.dateModified = new Date().toISOString();
+ updated.push(t);
+ }
+ if (updated.length === 0) return;
+ await this.save();
+ for (const t of updated) {
+ await this.updateMarkdownFile(t);
+ }
+ }
+
/** Remove multiple titles in one batch: single save, MD files deleted in chunks of 10. */
async removeTitlesBatch(ids: string[]): Promise {
const CHUNK_SIZE = 10;
@@ -125,7 +369,7 @@ export class DataManager {
group.titleIds = group.titleIds.filter((tid) => tid !== id);
}
if (this.data.airtime) {
- this.data.airtime = this.data.airtime.filter((e) => e.titleId !== id);
+ this.data.airtime = this.data.airtime.filter((e) => e.titleId !== id || e.source === 'reading');
}
const d = this.data as unknown as Record;
if (d['collapsedSeasons']) {
@@ -172,24 +416,77 @@ export class DataManager {
/** Remove all Upcoming entries for a given title (e.g. when date goes past). */
async removeAirtimeEntriesForTitle(titleId: string): Promise {
const before = (this.data.airtime ?? []).length;
- this.data.airtime = (this.data.airtime ?? []).filter((e) => e.titleId !== titleId);
+ this.data.airtime = (this.data.airtime ?? []).filter((e) => e.titleId !== titleId || e.source === 'reading');
if ((this.data.airtime ?? []).length !== before) {
await this.save();
}
}
+ // ── Reading → Upcoming bridge ──────────────────────────────────────────────────
+ // Reading items (Book/Manga) live in reading.json, but their Upcoming entries
+ // share the same airtime list as watch titles — mirroring autoAddToUpcoming.
+
+ /**
+ * Auto-adds a reading item with a future release date to Upcoming, mirroring
+ * the watchlist `autoAddToUpcoming`. No-op if an entry already exists.
+ */
+ async autoAddReadingToUpcoming(item: Book | Manga, kind: 'book' | 'manga'): Promise {
+ if (!this.data.airtime) this.data.airtime = [];
+ if (this.data.airtime.some((e) => e.source === 'reading' && e.titleId === item.id)) return;
+ const entry: AirtimeEntry = {
+ id: this.generateReadingAirtimeId(item.id),
+ titleId: item.id,
+ source: 'reading',
+ readingKind: kind,
+ schedule: {
+ recurrence: 'once',
+ releaseDate: item.releaseDate ?? undefined,
+ },
+ dateAdded: new Date().toISOString(),
+ };
+ this.data.airtime.push(entry);
+ await this.plugin.saveData(this.data);
+ }
+
+ /** Remove all Upcoming entries for a given reading item (book/manga id). */
+ async removeReadingAirtimeEntries(itemId: string): Promise {
+ const before = (this.data.airtime ?? []).length;
+ this.data.airtime = (this.data.airtime ?? []).filter(
+ (e) => !(e.source === 'reading' && e.titleId === itemId),
+ );
+ if ((this.data.airtime ?? []).length !== before) {
+ await this.save();
+ }
+ }
+
+ generateReadingAirtimeId(itemId: string): string {
+ const base = `airtime-reading-${itemId}`;
+ const existing = (this.data.airtime ?? []).map((e) => e.id);
+ if (!existing.includes(base)) return base;
+ let counter = 2;
+ while (existing.includes(`${base}-${counter}`)) counter++;
+ return `${base}-${counter}`;
+ }
+
async updateTitle(updated: WatchLogTitle): Promise {
updated.dateModified = new Date().toISOString();
+ if (updated.status === 'Completed') {
+ updated.priority = '';
+ }
const idx = this.data.titles.findIndex((t) => t.id === updated.id);
if (idx >= 0) {
const old = this.data.titles[idx]!;
const now = new Date().toISOString();
if (old.rating !== updated.rating) {
- void this.historyManager?.log(`${updated.title} (${updated.type}) was reviewed on ${formatHistoryDate(now)}`);
+ void this.historyManager?.log(
+ `${updated.title} (${updated.type}) was reviewed on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'rating', titleName: updated.title },
+ );
}
if (old.status !== updated.status && updated.status === 'Completed') {
void this.historyManager?.log(
- `${updated.title} (${updated.type}) was marked as watched on ${formatHistoryDate(now)}`
+ `${updated.title} (${updated.type}) was marked as watched on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'completed', titleName: updated.title },
);
}
this.data.titles[idx] = updated;
@@ -198,11 +495,42 @@ export class DataManager {
}
}
+ /** Update without saving or notifying listeners. Caller must call
+ * save() (or saveOnly + notifyChange) once after the batch completes,
+ * and is responsible for updating the markdown file per title if needed. */
+ updateTitleSilent(updated: WatchLogTitle): void {
+ updated.dateModified = new Date().toISOString();
+ if (updated.status === 'Completed') {
+ updated.priority = '';
+ }
+ const idx = this.data.titles.findIndex((t) => t.id === updated.id);
+ if (idx >= 0) {
+ const old = this.data.titles[idx]!;
+ const now = new Date().toISOString();
+ if (old.rating !== updated.rating) {
+ void this.historyManager?.log(
+ `${updated.title} (${updated.type}) was reviewed on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'rating', titleName: updated.title },
+ );
+ }
+ if (old.status !== updated.status && updated.status === 'Completed') {
+ void this.historyManager?.log(
+ `${updated.title} (${updated.type}) was marked as watched on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'completed', titleName: updated.title },
+ );
+ }
+ this.data.titles[idx] = updated;
+ }
+ }
+
async removeTitle(id: string): Promise {
const title = this.getTitle(id);
if (title) {
const now = new Date().toISOString();
- void this.historyManager?.log(`${title.title} (${title.type}) was deleted on ${formatHistoryDate(now)}`);
+ void this.historyManager?.log(
+ `${title.title} (${title.type}) was deleted on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'deleted', titleName: title.title },
+ );
}
this.data.titles = this.data.titles.filter((t) => t.id !== id);
// Remove from any groups
@@ -211,7 +539,7 @@ export class DataManager {
}
// Remove airtime entries for this title
if (this.data.airtime) {
- this.data.airtime = this.data.airtime.filter((e) => e.titleId !== id);
+ this.data.airtime = this.data.airtime.filter((e) => e.titleId !== id || e.source === 'reading');
}
// Remove collapsed seasons for this title
const d = this.data as unknown as Record;
@@ -333,6 +661,13 @@ export class DataManager {
await this.save();
}
+ async removeAirtimeEntriesBatch(ids: string[]): Promise {
+ if (ids.length === 0) return;
+ const removeSet = new Set(ids);
+ this.data.airtime = (this.data.airtime ?? []).filter((e) => !removeSet.has(e.id));
+ await this.save();
+ }
+
// ── Maybe CRUD ────────────────────────────────────────────────────────────────
getMaybeTitles(): MaybeTitle[] {
@@ -378,7 +713,7 @@ export class DataManager {
return title.watchedEpisodes.includes(1) ? title.episodeDuration : 0;
}
if (title.status === 'Completed') {
- return title.totalEpisodes * title.episodeDuration;
+ return this.getEffectiveTotal(title) * title.episodeDuration;
}
// Watching, Dropped, Plan to watch: only watched episodes
return title.watchedEpisodes.length * title.episodeDuration;
@@ -392,12 +727,12 @@ export class DataManager {
if (title.type === 'Movie') {
return title.watchedEpisodes.includes(1) ? 0 : title.episodeDuration;
}
+ const effective = this.getEffectiveTotal(title);
if (title.status === 'Plan to watch') {
- return Math.max(0, title.totalEpisodes - title.watchedEpisodes.length) * title.episodeDuration;
+ return Math.max(0, effective - title.watchedEpisodes.length) * title.episodeDuration;
}
if (title.status === 'Watching') {
- const unwatched = Math.max(0, title.totalEpisodes - title.watchedEpisodes.length);
- return unwatched * title.episodeDuration;
+ return Math.max(0, effective - title.watchedEpisodes.length) * title.episodeDuration;
}
// Completed or other: 0
return 0;
@@ -409,14 +744,15 @@ export class DataManager {
if (title.type === 'Movie') {
return title.watchedEpisodes.includes(1) ? 0 : title.episodeDuration;
}
+ const effective = this.getEffectiveTotal(title);
if (title.status === 'Dropped') {
- return Math.max(0, title.totalEpisodes - title.watchedEpisodes.length) * title.episodeDuration;
+ return Math.max(0, effective - title.watchedEpisodes.length) * title.episodeDuration;
}
if (title.status === 'Plan to watch') {
- return Math.max(0, title.totalEpisodes - title.watchedEpisodes.length) * title.episodeDuration;
+ return Math.max(0, effective - title.watchedEpisodes.length) * title.episodeDuration;
}
if (title.status === 'Watching') {
- return Math.max(0, title.totalEpisodes - title.watchedEpisodes.length) * title.episodeDuration;
+ return Math.max(0, effective - title.watchedEpisodes.length) * title.episodeDuration;
}
// Completed or other: 0
return 0;
@@ -441,6 +777,13 @@ export class DataManager {
}
).on('raw', (path: string) => {
if (path.endsWith('watchlog/data.json')) {
+ // Suppress the echo of our own saveOnly() — Obsidian fires 'raw' for
+ // any write to the plugin data file, including our debounced
+ // episode-click saves. Without this guard each click cascades into
+ // load() + notifyListeners(), forcing a full tab re-render.
+ if (Date.now() - this.lastSelfSaveTime < this.SELF_SAVE_ECHO_WINDOW_MS) {
+ return;
+ }
void (async () => {
await this.load();
this.notifyListeners();
@@ -488,11 +831,96 @@ export class DataManager {
await this.save();
}
+ // ── Skip helpers ──────────────────────────────────────────────────────────────
+
+ getTotalSkippedCount(title: WatchLogTitle): number {
+ return title.seasons.reduce((sum, s) => sum + (s.skippedEpisodes?.length ?? 0), 0);
+ }
+
+ /** totalEpisodes minus all season-defined skipped episodes. */
+ getEffectiveTotal(title: WatchLogTitle): number {
+ return Math.max(0, title.totalEpisodes - this.getTotalSkippedCount(title));
+ }
+
+ /** Returns true if absoluteEpNum falls in any season's skippedEpisodes list. */
+ isEpisodeSkipped(title: WatchLogTitle, absoluteEpNum: number): boolean {
+ for (const season of title.seasons) {
+ const rel = absoluteEpNum - season.offset;
+ if (rel >= 1 && rel <= season.episodes && (season.skippedEpisodes ?? []).includes(rel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * In-memory toggle of an episode's watched state plus auto-complete logic.
+ * Schedules a debounced save and does NOT notify listeners — caller is
+ * responsible for targeted DOM updates. Returns the (mutated) title.
+ */
+ applyEpisodeWatchedToggle(
+ id: string,
+ episodeNumber: number,
+ watched: boolean,
+ ): WatchLogTitle | null {
+ const title = this.getTitle(id);
+ if (!title) return null;
+
+ if (watched) {
+ if (!title.watchedEpisodes.includes(episodeNumber)) {
+ title.watchedEpisodes.push(episodeNumber);
+ title.watchedEpisodes.sort((a, b) => a - b);
+ }
+ const now = new Date().toISOString();
+ if (title.totalEpisodes > 1) {
+ void this.historyManager?.log(
+ `${title.title} (${title.type}) episode ${episodeNumber} was marked as watched on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'watched', titleName: title.title },
+ );
+ } else {
+ void this.historyManager?.log(
+ `${title.title} (${title.type}) was marked as watched on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'watched', titleName: title.title },
+ );
+ }
+ } else {
+ title.watchedEpisodes = title.watchedEpisodes.filter((e) => e !== episodeNumber);
+ }
+
+ title.dateModified = new Date().toISOString();
+ this.applyAutoCompleteRules(title);
+ this.scheduleEpisodeSave(id);
+ return title;
+ }
+
+ /** Shared auto-complete / un-complete rules. Mutates title in place. */
+ private applyAutoCompleteRules(title: WatchLogTitle): void {
+ const effectiveTotal = this.getEffectiveTotal(title);
+ if (
+ this.plugin.settings.autoCompleteOnLastEpisode &&
+ effectiveTotal > 0 &&
+ title.watchedEpisodes.length >= effectiveTotal
+ ) {
+ if (title.status !== 'Completed') {
+ title.status = 'Completed';
+ // Match updateTitle(): completed titles drop priority.
+ title.priority = '';
+ }
+ if (this.plugin.settings.setFinishDateAutomatically && !title.dateFinished) {
+ title.dateFinished = new Date().toISOString().split('T')[0] ?? null;
+ }
+ } else if (title.status === 'Completed' && title.watchedEpisodes.length < effectiveTotal) {
+ title.status = 'Watching';
+ title.dateFinished = null;
+ }
+ }
+
// ── Progress helpers ──────────────────────────────────────────────────────────
getProgress(title: WatchLogTitle): number {
- if (title.totalEpisodes === 0) return 0;
- return Math.round((title.watchedEpisodes.length / title.totalEpisodes) * 100);
+ const effective = this.getEffectiveTotal(title);
+ if (effective === 0) return 0;
+ return Math.min(100, Math.round((title.watchedEpisodes.length / effective) * 100));
}
getNextUnwatchedEpisode(title: WatchLogTitle): number | null {
@@ -514,24 +942,31 @@ export class DataManager {
}
const now = new Date().toISOString();
if (title.totalEpisodes > 1) {
- void this.historyManager?.log(`${title.title} (${title.type}) episode ${episodeNumber} was marked as watched on ${formatHistoryDate(now)}`);
+ void this.historyManager?.log(
+ `${title.title} (${title.type}) episode ${episodeNumber} was marked as watched on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'watched', titleName: title.title },
+ );
} else {
- void this.historyManager?.log(`${title.title} (${title.type}) was marked as watched on ${formatHistoryDate(now)}`);
+ void this.historyManager?.log(
+ `${title.title} (${title.type}) was marked as watched on ${formatHistoryDate(now)}`,
+ { source: 'Watchlist', action: 'watched', titleName: title.title },
+ );
}
} else {
title.watchedEpisodes = title.watchedEpisodes.filter((e) => e !== episodeNumber);
}
+ const effectiveTotal = this.getEffectiveTotal(title);
if (
this.plugin.settings.autoCompleteOnLastEpisode &&
- title.totalEpisodes > 0 &&
- title.watchedEpisodes.length >= title.totalEpisodes
+ effectiveTotal > 0 &&
+ title.watchedEpisodes.length >= effectiveTotal
) {
title.status = 'Completed';
if (this.plugin.settings.setFinishDateAutomatically && !title.dateFinished) {
title.dateFinished = new Date().toISOString().split('T')[0] ?? null;
}
- } else if (title.status === 'Completed' && title.watchedEpisodes.length < title.totalEpisodes) {
+ } else if (title.status === 'Completed' && title.watchedEpisodes.length < effectiveTotal) {
title.status = 'Watching';
title.dateFinished = null;
}
@@ -552,16 +987,17 @@ export class DataManager {
title.watchedEpisodes = title.watchedEpisodes.filter((e) => !remove.has(e));
}
+ const effectiveTotal = this.getEffectiveTotal(title);
if (
this.plugin.settings.autoCompleteOnLastEpisode &&
- title.totalEpisodes > 0 &&
- title.watchedEpisodes.length >= title.totalEpisodes
+ effectiveTotal > 0 &&
+ title.watchedEpisodes.length >= effectiveTotal
) {
title.status = 'Completed';
if (this.plugin.settings.setFinishDateAutomatically && !title.dateFinished) {
title.dateFinished = new Date().toISOString().split('T')[0] ?? null;
}
- } else if (title.status === 'Completed' && title.watchedEpisodes.length < title.totalEpisodes) {
+ } else if (title.status === 'Completed' && title.watchedEpisodes.length < effectiveTotal) {
title.status = 'Watching';
title.dateFinished = null;
}
@@ -591,6 +1027,8 @@ export class DataManager {
}
}
+ private lastMarkdownPathById: Map = new Map();
+
async updateMarkdownFile(title: WatchLogTitle): Promise {
const root = this.plugin.settings.rootFolder;
const folderPath = normalizePath(`${root}/${title.type}`);
@@ -599,12 +1037,28 @@ export class DataManager {
const filePath = normalizePath(`${folderPath}/${safeTitle}.md`);
const progress = this.getProgress(title);
const content = this.buildMarkdownContent(title, progress);
+
+ // If a different path was previously written for this title (rename/type change),
+ // remove the stale file before writing the new one.
+ const previousPath = this.lastMarkdownPathById.get(title.id);
+ if (previousPath && previousPath !== filePath) {
+ try {
+ const oldFile = this.app.vault.getAbstractFileByPath(previousPath);
+ if (oldFile instanceof TFile) {
+ await this.app.fileManager.trashFile(oldFile);
+ }
+ } catch {
+ // best-effort cleanup
+ }
+ }
+
const existing = this.app.vault.getAbstractFileByPath(filePath);
if (existing instanceof TFile) {
await this.app.vault.modify(existing, content);
} else {
await this.app.vault.create(filePath, content);
}
+ this.lastMarkdownPathById.set(title.id, filePath);
}
async createMarkdownFileIfMissing(title: WatchLogTitle): Promise {
diff --git a/src/DraftChoiceModal.ts b/src/DraftChoiceModal.ts
new file mode 100644
index 0000000..bce3f56
--- /dev/null
+++ b/src/DraftChoiceModal.ts
@@ -0,0 +1,46 @@
+import { App, Modal } from 'obsidian';
+
+export type DraftAddChoice = 'watchlist' | 'book' | 'manga';
+
+/**
+ * Small choice modal shown when adding a draft. Offers three inline options
+ * (Watchlist / Book / Manga), each routing to its own corresponding Add modal.
+ * The draft's text is shown as a subtitle to identify what's being added.
+ */
+export class DraftChoiceModal extends Modal {
+ constructor(
+ app: App,
+ private draftText: string,
+ private onChoice: (choice: DraftAddChoice) => void,
+ ) {
+ super(app);
+ }
+
+ onOpen(): void {
+ const { contentEl } = this;
+ contentEl.empty();
+ contentEl.addClass('wl-draft-choice-modal');
+
+ this.titleEl.setText('Add draft');
+ contentEl.createDiv({ cls: 'wl-draft-choice-subtitle', text: this.draftText });
+
+ const grid = contentEl.createDiv({ cls: 'wl-draft-choice-grid' });
+
+ const makeOption = (label: string, choice: DraftAddChoice): void => {
+ const btn = grid.createEl('button', { cls: 'wl-draft-choice-btn' });
+ btn.createDiv({ cls: 'wl-draft-choice-label', text: label });
+ btn.addEventListener('click', () => {
+ this.close();
+ this.onChoice(choice);
+ });
+ };
+
+ makeOption('Add in Watchlist', 'watchlist');
+ makeOption('Add book', 'book');
+ makeOption('Add manga', 'manga');
+ }
+
+ onClose(): void {
+ this.contentEl.empty();
+ }
+}
diff --git a/src/DraftsTab.ts b/src/DraftsTab.ts
index 75801af..3c9b09a 100644
--- a/src/DraftsTab.ts
+++ b/src/DraftsTab.ts
@@ -1,381 +1,449 @@
-import Fuse from 'fuse.js';
-import type { EventRef } from 'obsidian';
-import type WatchLogPlugin from './main';
-import type { DataManager } from './DataManager';
-import type { DraftPersistState, WatchLogTitle } from './types';
-import { AddTitleModal } from './AddTitleModal';
-
-interface LiveDraftEntry {
- titleKey: string;
- titleDisplay: string;
- sources: string[];
- firstSeen: string;
- added: boolean;
-}
-
-export class DraftsTab {
- private plugin: WatchLogPlugin;
- private dataManager: DataManager;
- private containerEl: HTMLElement;
- private onCountChange: (count: number) => void;
-
- private eventRef: EventRef | null = null;
- private destroyed = false;
- private scanDebounceTimer: ReturnType | null = null;
- private lastScanEntries: LiveDraftEntry[] = [];
- private persistState: DraftPersistState = {
- dismissed: [],
- added: [],
- firstSeen: {},
- titleDisplay: {},
- };
-
- constructor(
- containerEl: HTMLElement,
- plugin: WatchLogPlugin,
- dataManager: DataManager,
- onCountChange: (count: number) => void,
- ) {
- this.containerEl = containerEl;
- this.plugin = plugin;
- this.dataManager = dataManager;
- this.onCountChange = onCountChange;
- }
-
- async render(): Promise {
- this.destroyed = false;
- await this.loadPersistState();
- if (this.destroyed) return;
-
- const entries = await this.scanVault();
- if (this.destroyed) return;
-
- this.renderUI(entries);
- this.registerChangeListener();
- }
-
- destroy(): void {
- this.destroyed = true;
- if (this.scanDebounceTimer) {
- clearTimeout(this.scanDebounceTimer);
- this.scanDebounceTimer = null;
- }
- if (this.eventRef) {
- this.plugin.app.metadataCache.offref(this.eventRef);
- this.eventRef = null;
- }
- }
-
- // ── Persistence ───────────────────────────────────────────────────────────────
-
- private async loadPersistState(): Promise {
- const data = (await this.plugin.loadData()) as Record | null;
- const drafts = data?.['drafts'] as Partial | undefined;
- if (drafts) {
- this.persistState = {
- dismissed: drafts.dismissed ?? [],
- added: drafts.added ?? [],
- firstSeen: drafts.firstSeen ?? {},
- titleDisplay: drafts.titleDisplay ?? {},
- };
- }
- }
-
- private async savePersistState(): Promise {
- const data = ((await this.plugin.loadData()) as Record | null) ?? {};
- await this.plugin.saveData({ ...data, drafts: this.persistState });
- }
-
- // ── Vault scanning ────────────────────────────────────────────────────────────
-
- private getTag(): string {
- return this.plugin.settings.draftsVaultTag ?? '#watchlog';
- }
-
- private async scanVault(): Promise {
- const tag = this.getTag();
- const files = this.plugin.app.vault.getMarkdownFiles();
- // Map: lowercase title key → { sources, displayTitle }
- const liveMap: Map; displayTitle: string }> = new Map();
-
- for (const file of files) {
- const cache = this.plugin.app.metadataCache.getFileCache(file);
- const hasTag = cache?.tags?.some((t) => t.tag === tag) ?? false;
- if (!hasTag) continue;
-
- try {
- const content = await this.plugin.app.vault.cachedRead(file);
- const lines = content.split('\n');
- for (const line of lines) {
- if (!line.includes(tag)) continue;
- const tagIdx = line.indexOf(tag);
- const afterTag = line.slice(tagIdx + tag.length).trim();
- if (!afterTag) continue;
-
- // Each comma-separated segment after the tag is a separate title
- const segments = afterTag.split(',');
- for (const seg of segments) {
- const displayTitle = seg.trim();
- if (!displayTitle || displayTitle.length > 100) continue;
- const titleKey = displayTitle.toLowerCase();
- if (!liveMap.has(titleKey)) {
- liveMap.set(titleKey, { sources: new Set(), displayTitle });
- }
- liveMap.get(titleKey)!.sources.add(file.basename);
- }
- }
- } catch {
- // Skip unreadable files
- }
- }
-
- // Record firstSeen and displayTitle for newly discovered entries
- let stateChanged = false;
- const now = new Date().toISOString();
- for (const [key, { displayTitle }] of liveMap) {
- if (!this.persistState.firstSeen[key]) {
- this.persistState.firstSeen[key] = now;
- stateChanged = true;
- }
- if (!this.persistState.titleDisplay[key]) {
- this.persistState.titleDisplay[key] = displayTitle;
- stateChanged = true;
- }
- }
- if (stateChanged) {
- await this.savePersistState();
- }
-
- // Build visible entries (dismissed entries are excluded entirely)
- const entries: LiveDraftEntry[] = [];
- for (const [key, { sources }] of liveMap) {
- if (this.persistState.dismissed.includes(key)) continue;
- entries.push({
- titleKey: key,
- titleDisplay: this.persistState.titleDisplay[key] ?? key,
- sources: Array.from(sources),
- firstSeen: this.persistState.firstSeen[key] ?? now,
- added: this.persistState.added.includes(key),
- });
- }
-
- // Sort oldest-first (FIFO queue)
- entries.sort((a, b) => a.firstSeen.localeCompare(b.firstSeen));
- this.lastScanEntries = entries;
- return entries;
- }
-
- // ── Event listener ────────────────────────────────────────────────────────────
-
- private triggerDebouncedRender(): void {
- if (this.scanDebounceTimer) clearTimeout(this.scanDebounceTimer);
- this.scanDebounceTimer = setTimeout(() => {
- this.scanDebounceTimer = null;
- void this.render();
- }, 500);
- }
-
- private registerChangeListener(): void {
- if (this.eventRef) {
- this.plugin.app.metadataCache.offref(this.eventRef);
- }
- this.eventRef = this.plugin.app.metadataCache.on('changed', (_file) => {
- this.triggerDebouncedRender();
- });
- }
-
- // ── Fuzzy match helper ────────────────────────────────────────────────────────
-
- private buildFuse(titles: WatchLogTitle[]): Fuse {
- return new Fuse(titles, {
- keys: ['title'],
- threshold: 0.35,
- includeScore: true,
- });
- }
-
- private fuzzyMatchesWatchlist(displayTitle: string, fuse: Fuse): boolean {
- const results = fuse.search(displayTitle);
- return results.length > 0 && (results[0]?.score ?? 1) <= 0.35;
- }
-
- // ── Rendering ─────────────────────────────────────────────────────────────────
-
- private renderUI(entries: LiveDraftEntry[]): void {
- const el = this.containerEl;
- el.empty();
-
- const tag = this.getTag();
- const watchlistTitles = this.dataManager.getTitles();
- const fuse = this.buildFuse(watchlistTitles);
-
- // Compute fuzzy match results once per entry and cache them
- const fuzzyCache = new Map();
- for (const entry of entries) {
- if (entry.added) {
- fuzzyCache.set(entry.titleKey, false);
- } else {
- fuzzyCache.set(entry.titleKey, this.fuzzyMatchesWatchlist(entry.titleDisplay, fuse));
- }
- }
-
- // Pending = not added AND not already present in the Watchlist (exact or fuzzy)
- const pendingCount = entries.filter((e) => {
- if (e.added) return false;
- return !fuzzyCache.get(e.titleKey);
- }).length;
- this.onCountChange(pendingCount);
-
- // Notice banner — matches Custom Lists draft banner style (Fix 3)
- el.createDiv({
- cls: 'wl-drafts-notice',
- text: `⚠ Write ${tag} Movie Name in any note and it appears here automatically. Hit Add when you're ready to add it to your Watchlist.`,
- });
-
- // Count pill
- const countWrap = el.createDiv({ cls: 'wl-list-title-wrap' });
- countWrap.createSpan({ cls: 'wl-list-count', text: String(pendingCount) });
- countWrap.createSpan({
- cls: 'wl-drafts-count-label',
- text: ` Pending draft${pendingCount !== 1 ? 's' : ''}`,
- });
-
- if (entries.length === 0) {
- el.createDiv({
- cls: 'wl-drafts-empty',
- text: `No drafts found. Add ${tag} followed by a title in any vault note.`,
- });
- return;
- }
-
- // Sort: non-Watchlist entries first (oldest-first), Watchlist entries last (oldest-first)
- entries.sort((a, b) => {
- const aDup = fuzzyCache.get(a.titleKey) ?? false;
- const bDup = fuzzyCache.get(b.titleKey) ?? false;
- if (aDup !== bDup) return aDup ? 1 : -1;
- return a.firstSeen.localeCompare(b.firstSeen);
- });
-
- // Cards (Fix 2 — Upcoming tab card style)
- const cards = el.createDiv({ cls: 'wl-drafts-cards' });
- for (const entry of entries) {
- this.renderCard(cards, entry, fuzzyCache.get(entry.titleKey) ?? false);
- }
- }
-
- private renderCard(cards: HTMLElement, entry: LiveDraftEntry, isDuplicate: boolean): void {
- // Build class list (Fix 4 — dim watchlist rows; style added rows)
- let cls = 'wl-drafts-card';
- if (isDuplicate) cls += ' wl-drafts-card-watchlist';
- if (entry.added) cls += ' wl-drafts-card-added';
-
- const card = cards.createDiv({ cls });
-
- // Title (left, bold)
- card.createDiv({ cls: 'wl-drafts-card-title', text: entry.titleDisplay });
-
- // Source link (center)
- const sourceEl = card.createDiv({ cls: 'wl-drafts-card-source' });
- if (entry.sources.length > 0) {
- const primaryNote = entry.sources[0]!;
- const link = sourceEl.createSpan({
- cls: 'wl-drafts-source-link',
- text: `[[${primaryNote}]]`,
- });
- link.addEventListener('click', (e) => {
- e.stopPropagation();
- void this.plugin.app.workspace.openLinkText(primaryNote, '');
- });
- if (entry.sources.length > 1) {
- sourceEl.createSpan({
- cls: 'wl-drafts-source-count',
- text: ` (${entry.sources.length})`,
- attr: { title: entry.sources.join('\n') },
- });
- }
- }
-
- // "In Watchlist" indicator (center-right, only when applicable) — Fix 4
- const dupEl = card.createDiv({ cls: 'wl-drafts-card-dup' });
- if (isDuplicate) {
- dupEl.createSpan({
- text: 'In Watchlist',
- attr: { title: 'This title already exists in your Watchlist' },
- });
- }
-
- // Actions (far right)
- const actions = card.createDiv({ cls: 'wl-drafts-card-actions' });
-
- if (entry.added || isDuplicate) {
- actions.createSpan({ cls: 'wl-drafts-added-label', text: 'Added' });
- } else {
- const addBtn = actions.createEl('button', {
- cls: 'wl-btn wl-btn-sm wl-btn-primary',
- text: 'Add',
- });
- addBtn.addEventListener('click', () => this.openAddModal(entry));
- }
-
- const dismissBtn = actions.createEl('button', {
- cls: 'wl-btn wl-btn-sm wl-drafts-dismiss',
- text: '✕',
- attr: { title: 'Dismiss' },
- });
- dismissBtn.addEventListener('click', () => void this.dismissEntry(entry.titleKey));
- }
-
- private rerenderFromCache(): void {
- const entries = this.lastScanEntries
- .filter((e) => !this.persistState.dismissed.includes(e.titleKey))
- .map((e) => ({
- ...e,
- added: this.persistState.added.includes(e.titleKey),
- }));
- this.renderUI(entries);
- }
-
- // ── Actions ───────────────────────────────────────────────────────────────────
-
- private async dismissEntry(titleKey: string): Promise {
- if (!this.persistState.dismissed.includes(titleKey)) {
- this.persistState.dismissed.push(titleKey);
- }
- this.persistState.added = this.persistState.added.filter((k) => k !== titleKey);
- await this.savePersistState();
- this.rerenderFromCache();
- }
-
- private openAddModal(entry: LiveDraftEntry): void {
- const modal = new AddTitleModal(
- this.plugin.app,
- this.plugin,
- this.dataManager,
- () => void this.afterAdded(entry.titleKey),
- {
- searchQuery: entry.titleDisplay,
- title: entry.titleDisplay,
- type: 'Anime',
- episodes: 0,
- duration: 0,
- releaseDate: '',
- link: '',
- seasons: [],
- },
- );
- modal.open();
- }
-
- private async afterAdded(titleKey: string): Promise {
- const behavior = this.plugin.settings.draftsAfterAdding ?? 'keep';
- if (behavior === 'remove') {
- await this.dismissEntry(titleKey);
- } else {
- if (!this.persistState.added.includes(titleKey)) {
- this.persistState.added.push(titleKey);
- }
- await this.savePersistState();
- this.rerenderFromCache();
- }
- }
-}
+import Fuse from 'fuse.js';
+import type { EventRef } from 'obsidian';
+import type WatchLogPlugin from './main';
+import type { DataManager } from './DataManager';
+import type { DraftPersistState, WatchLogTitle } from './types';
+import { AddTitleModal } from './AddTitleModal';
+import { AddReadingModal } from './AddReadingModal';
+import { DraftChoiceModal } from './DraftChoiceModal';
+
+interface LiveDraftEntry {
+ titleKey: string;
+ titleDisplay: string;
+ sources: string[];
+ firstSeen: string;
+ added: boolean;
+}
+
+export class DraftsTab {
+ private plugin: WatchLogPlugin;
+ private dataManager: DataManager;
+ private containerEl: HTMLElement;
+ private onCountChange: (count: number) => void;
+
+ private eventRef: EventRef | null = null;
+ private destroyed = false;
+ private scanDebounceTimer: number | null = null;
+ private lastScanEntries: LiveDraftEntry[] = [];
+ private persistState: DraftPersistState = {
+ dismissed: [],
+ added: [],
+ firstSeen: {},
+ titleDisplay: {},
+ };
+
+ constructor(
+ containerEl: HTMLElement,
+ plugin: WatchLogPlugin,
+ dataManager: DataManager,
+ onCountChange: (count: number) => void,
+ ) {
+ this.containerEl = containerEl;
+ this.plugin = plugin;
+ this.dataManager = dataManager;
+ this.onCountChange = onCountChange;
+ }
+
+ async render(): Promise {
+ this.destroyed = false;
+ await this.loadPersistState();
+ if (this.destroyed) return;
+
+ const entries = await this.scanVault();
+ if (this.destroyed) return;
+
+ this.renderUI(entries);
+ this.registerChangeListener();
+ }
+
+ destroy(): void {
+ this.destroyed = true;
+ if (this.scanDebounceTimer) {
+ window.clearTimeout(this.scanDebounceTimer);
+ this.scanDebounceTimer = null;
+ }
+ if (this.eventRef) {
+ this.plugin.app.metadataCache.offref(this.eventRef);
+ this.eventRef = null;
+ }
+ }
+
+ // ── Persistence ───────────────────────────────────────────────────────────────
+
+ private async loadPersistState(): Promise {
+ const data = this.plugin.dataManager.getData() as unknown as Record;
+ const drafts = data['drafts'] as Partial | undefined;
+ if (drafts) {
+ this.persistState = {
+ dismissed: drafts.dismissed ?? [],
+ added: drafts.added ?? [],
+ firstSeen: drafts.firstSeen ?? {},
+ titleDisplay: drafts.titleDisplay ?? {},
+ };
+ }
+ }
+
+ private async savePersistState(): Promise {
+ const data = this.plugin.dataManager.getData() as unknown as Record;
+ data['drafts'] = this.persistState;
+ this.plugin.dataManager.queueSave();
+ }
+
+ // ── Vault scanning ────────────────────────────────────────────────────────────
+
+ private getTag(): string {
+ return this.plugin.settings.draftsVaultTag ?? '#watchlog';
+ }
+
+ /**
+ * Scans the vault for inline-tag draft mentions, returning a map of
+ * lowercase title key → { sources, displayTitle }. Pure (no persistState side
+ * effects) so it can back both the full render and the count-only badge scan.
+ * Inline-tag matching only — frontmatter tags are intentionally ignored.
+ */
+ private static async scanTaggedFiles(
+ plugin: WatchLogPlugin,
+ tag: string,
+ ): Promise