From b441347d1f3f425048a98c1d1ff9fe20a55af810 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 30 Jun 2025 14:04:35 +0000 Subject: [PATCH 01/60] Update manifest.json to version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 38975a1..c4f709d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes", - "version": "1.8.12", + "version": "1.8.13", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From 6e9a7c7e177cc85f86da90302da11d30cc46b80d Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sun, 6 Jul 2025 12:31:49 +0200 Subject: [PATCH 02/60] Fix #256 --- src/events/MutationObserver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/events/MutationObserver.ts b/src/events/MutationObserver.ts index 5b7aea8..f4b5295 100644 --- a/src/events/MutationObserver.ts +++ b/src/events/MutationObserver.ts @@ -161,6 +161,7 @@ async function updateFolderNamesInPath(plugin: FolderNotesPlugin, titleContainer const excludedFolder = getExcludedFolder(plugin, folderPath, true); if (excludedFolder?.disableFolderNote) return; const folderNote = getFolderNote(plugin, folderPath); + if (!folderNote) return; if (folderNote) breadcrumb.classList.add('has-folder-note'); breadcrumb?.setAttribute('data-path', path.slice(0, -1)); From 7d61dcf049d7209340e23b085fbc3d6044d33c8a Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sun, 6 Jul 2025 13:18:12 +0200 Subject: [PATCH 03/60] Fix click on folder note in folder overview --- src/main.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main.ts b/src/main.ts index fe8c5c4..9203c20 100644 --- a/src/main.ts +++ b/src/main.ts @@ -157,21 +157,15 @@ export default class FolderNotesPlugin extends Plugin { this.registerView(FOLDER_OVERVIEW_VIEW, (leaf: WorkspaceLeaf) => { return new FolderOverviewView(leaf, this); }); - if (this.settings.frontMatterTitle.enabled) { + if (this.app.plugins.getPlugin('obsidian-front-matter-title-plugin')) { this.fmtpHandler = new FrontMatterTitlePluginHandler(this); } this.tabManager = new TabManager(this); this.tabManager.updateTabs(); - const fileExplorerLeaf = this.app.workspace.getLeavesOfType('file-explorer')[0]; - if (fileExplorerLeaf) { - const container = fileExplorerLeaf.view.containerEl; - if (container) { - this.registerDomEvent(container, 'click', (evt: MouseEvent) => { - this.handleFileExplorerClick(evt); - }, true); - } - } + this.registerDomEvent(document, 'click', (evt: MouseEvent) => { + this.handleFileExplorerClick(evt); + }, true); const fileExplorerPlugin = this.app.internalPlugins.getEnabledPluginById('file-explorer'); if (fileExplorerPlugin) { @@ -255,6 +249,13 @@ export default class FolderNotesPlugin extends Plugin { if (!folderTitleEl) return; const onlyClickedOnFolderTitle = !!target.closest('.nav-folder-title-content'); + // const folderTitleContentEl = target.closest('.nav-folder-title-content') as HTMLElement; + // if (folderTitleContentEl) { + // const rect = folderTitleContentEl.getBoundingClientRect(); + // const clickOffsetX = evt.clientX - rect.left; + // // Ignore clicks within the first N pixels, e.g., 20px where the icon is displayed + // if (clickOffsetX < 20) return; + // } if (!this.settings.stopWhitespaceCollapsing && !onlyClickedOnFolderTitle) return; // Ignore clicks on the collapse icon From a2aca093ed055f7845c84f81af74baedf3406725 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sun, 6 Jul 2025 18:22:09 +0200 Subject: [PATCH 04/60] Update docs for folder overview --- docs/docs/Folder overview.md | 54 +++++++++++++++++- .../screenshots/Obsidian_nAqAIrlZFW.png | Bin 0 -> 48540 bytes docs/docs/assets/screenshots/P7yvNZmF5e.png | Bin 0 -> 103947 bytes 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 docs/docs/assets/screenshots/Obsidian_nAqAIrlZFW.png create mode 100644 docs/docs/assets/screenshots/P7yvNZmF5e.png diff --git a/docs/docs/Folder overview.md b/docs/docs/Folder overview.md index ed18c9c..0977b28 100644 --- a/docs/docs/Folder overview.md +++ b/docs/docs/Folder overview.md @@ -1,5 +1,6 @@ # Folder overview [The folder overview feature can be installed as a standalone plugin and works without the folder notes plugin by clicking on this.](https://obsidian.md/plugins?id=folder-overview) +**Don't install "folder notes" & the "folder overview" plugin instead only install the folder notes plugin otherwise you can't enable both and there can be other bugs.** This feature/plugin creates a [code block](https://help.obsidian.md/Editing+and+formatting/Basic+formatting+syntax#Code+blocks) that provides an overview of your entire vault or specific folders. Instructions for creating or editing a folder overview, along with additional guidance, can be found further down the page. Currently there are two styles available: **file explorer** & **list style**, with more coming soon. The **overview updates** itself **automatically** when you create, delete or modify a file. You can also integrate it into **templates**, as explained further down below this page. @@ -16,16 +17,63 @@ If the overview shows no files or folders yet you can just click on the button t ![type:video](../assets/n5AGi3VCxF5JcNx2Wm5O.mp4) ## Default settings -To edit the default values for new folder overviews you can either use the command "Edit folder overview" or open the plugins settings tab and then the folder overview tab to the edit the default settings. +To edit the default values for new folder overviews you can either use the command "Edit folder overview" or open the plugins settings tab and then the folder overview tab to the edit the default settings. This won't change the settings of already existing folder overviews. ![Screenshot](../assets/screenshots/b4QOtkzJs0.png) (On the left is the plugin settings tab and the right the view you see when you use the "Edit folder overview" command) -## How can I use this in a template? +## FAQ +### How can I use this in a template? Just create the folder overview as normal in a template and then change the settings to be what you want to use as a default for the files you apply the templates to. It also doesn't matter that the id of the overview in the template will be the same as the id of the overview that gets created when the template has been used. -## What's the id in the code block for? +### What's the id in the code block for? They're there to differentiate the overviews from each other so that you can use "Edit folder overview" command to edit the overviews from one file (there can be multiple overviews in one file). It's especially useful on mobile because the button to edit the code block on mobile is quite small. +## How can I make the links appear in the graph view? +[Edit the folder overview](#edit-overview) and then enable "Use actual links" to let the links appear in the graph view. The file will be then edited under the code block and there will be another list added which is hidden by default. You can choose to either hide the code block or the list under it in the folder overview settings. The list of links under the code block will also be visible in other apps that support markdown. + +![Screenshot](../assets/screenshots/Obsidian_nAqAIrlZFW.png) +![Screenshot](../assets/screenshots/P7yvNZmF5e.png) + +## Settings + +### Auto sync +When this is enabled and you rename/create/delete a file/folder which is a children of the selected folder the overview will automatically update. +### Title +The title is above the overview when you enable "Show the title". You can use variables like this: {{variableName}} which will be replaced with something and the list of variables you can use is below this text. + +| Variable | Explanation | Example | +| ------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| folderName | The name of the folder depending on what folder you choose to display | Folder1 | +| folderPath | The path of the choosen folder | Folder1/Folder2 | +| fileName | The name of the file where the overview is located in | File1 | +| filePath | The path of the file where the overview is located in | Folder1/File1 | +| fmtpFileName | The changed file name using the [front matter title plugin](https://github.com/snezhig/obsidian-front-matter-title) | Real file name: 1234, changed name: File1 | +| properties.\ | Choose any property from a file | properties.name => File1
properties.date => e.g. 01.01.2001 | +### Folder path +The overview will show the children of the selected folder. + +When you leave the path empty it will use the parent folder of where the overview is located in. The same is true for "File's parent folder path". If you're using the folder notes plugin and have the storage location set to parent folder you'll need to use "Path of folder linked to the file" to show the children of the folder which is linked to the file. Otherwise if you would for example have the overview located in "File1" which is linked to "Folder1" it would choose all the files/folders of the vault. + +### Use actual links +This allows the links of the overview to show up in the grap view, see [How can I make the links appear in the graph view?](#how-can-i-make-the-links-appear-in-the-graph-view?). + +#### Hide link list/folder overview +Either hide the code block or the list under it. When you hide the link list every list item will have a span item added to it. It looks like on the image at [How can I make the links appear in the graph view?](#how-can-i-make-the-links-appear-in-the-graph-view?). + +### Include types +Only the file types which are selected here will show up in the overview. + +### Show folder notes +Show the file itself which is linked to one folder. + +### File depth +Limit the depth of files/folders which are shown in the overview. When it is at depth one only the first level files/folders will be shown. When you can't see the childrens of folder the name won't be shown except when you enable ["Show folder names of folders that appear empty in the folder overview"](#show-folder-names-of-folders-that-appear-empty-in-the-folder-overview) or the folder has a file linked to it. + +### Sort files by +Choose the order of files and folders. + +### Show folder names of folders that appear empty in the folder overview +When folders don't have any childrens they don't show up until you enable this setting but they also don't show up until you set the "File depth" setting until the level of the children of a folder. For example you can see "Folder1" at level 2 and their children at level 3 and the folder name gets shown at level 3 or when you enable this setting. \ No newline at end of file diff --git a/docs/docs/assets/screenshots/Obsidian_nAqAIrlZFW.png b/docs/docs/assets/screenshots/Obsidian_nAqAIrlZFW.png new file mode 100644 index 0000000000000000000000000000000000000000..92ab98e561cbb4b35931339d7139918f4ef7f4e8 GIT binary patch literal 48540 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≀!f<6#~|6V~z{vGcSxaGxALozEjzUr={dT zR~h%5;Ro7(bv=6nn8Eiw#R2uGlJSZ5HB-T-H@{y4yXl|v$D5&AKXE&3l_gfGMQlA6 z0e#B)>mYMeBJvIP^Z=E}MjH>J6g=6}Q_PFxsgW4Heg>+KG4zGMHxZrUg?KYNG%qU4 zo%L7t?q)?y47RU-cd2JS13S7Se-5*c`z}nXqSZ_(Tw=Yn6{O>;tc58H_etmK&T{Cb zCcR4YVys6YX53M?5LKRS`Es*{>x`1Zu+ig)(bl_hLY`eV=R*wRm9E{lkE53 zNcd=cR&J|+_VnYh($^7EON*+=U$4YoH+s2Hv$ZSPDg_~%H_&w(TRRp&Qak#>0xY~^a2&R=vxZqU@Qf7)TG~c$~^#6559B@VL7IA{l#z^%{Q!9?8^;85;6Zo_oV=Oke1@H4Ph9=@#A=Y zA5u}0c;V^jetlWWw(tZ^)y_cWJ~I%A2>izwVK^WQ`HOC>lBbt2USbxB*kav$=lOCm z0BYJ*QVa-Q^Gb)OO4P#x07Egs0rx$DitS^o>8eWJvsOZWG)*N^O~xW?n3c1WAo6ZU z=!7wtYN#yDhDOQG>&=}LbbWG2qdnQPmHu;#iY*{W)B+t5Ze#IA0WLtSK

Wwg%K> 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

  • Db{vX1-9M;CpHBWZF1i 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 ->qSCXk(Y=W)b)C^WtEt%Mtp%OUCbF z8IW95(&h9|nBINn-_g$tB!h{EVVaavHm5LFb zhMVg_D(cuC{kgw|iFE&T6+_HnX(h_30l0l+D z5;)oGj{x_l$6}rUL;qpBVx_43WXD26S6k$c<|Y`2Mcu;GF;9mi&lrDMa0{C$n1ZVe zJZaUd|MMOFc2>=N)QuPa&iB^WjPuyqP^$3OJSAP#g@Xjw=-Wz1Lsju(mkBKnfBp#e zQWC#TuA+lSU7=|X1xA<*)s^8%DA~#B*w(HOp8(IAIVLLY6GpJ169VqnImXwj(flE# z21rYX(WNoZ9g|q1#raLZPVQ4?u6u;W$Pb#K!<0ketq9!*HK^BQPZc*u3W?{}?v`OuM@18q)=4JAYv z#M;Xfqbc&}#PU`36x3F}uOc$nBy<7)HDO8dq_DMr7zPllF(3(HzJ#K zyNU?9+Vzd@;^uBJ@xZjy#RmEm9wM+_ep{kUc&XvU<;6mN17y+%ui9qCGcExmWNP|n(~yHAmq3wG_TUDzU&q|0J7VD=gq7Nr{%G&$@TccD!Hh}xp-o#D6NZk^4$-`KiEGcyv;%pXjM7{H+swS?=gTC{y=oLxs zdx3v%2($}nD&Jr{Y&AP>jJ7PJ1EVRmn)>+vy!uh z;Ib$eiVtfU7r&*pb*XF}h0(XCO7#tr%rJA+mc|?o+j=M0rcCSsBJ~Q2G)8_-KBohB zWt)RT0&A4(c_wthC?CG~@QQ|C;$*?@-fh3S!J&-GhrCx2spiHF3l81IjvC~3atamp zk;IOI*j{)2I<}=PqwfCw{@CEGWmj*dmk|_MO4QBoR2;MCU=Q0Tcy>A5$7J!EueUio z-K(1p%kI>szCDi??~I$~**1#io?fOYUd)VbU$qGAO0<~?VZ9)8H^#eyV!!n^ZpN5s zwrpDzt91Ef&9(2{r(4L`zl5MYk{U^wikz`CAhW}zEE5k14%-tO&W)bbDp2J{hr)Ty zmM7DSn^yT|;ETKMRS$SH3isPq?*4uF{w4@d%sof>$v!${);1$nsE&I{I7ld zPY`)oohJbUx>MWf6U@|4&(Z(>3;&xQpwsUH|6Q@H4(Zv+W|o`WV3g7A;c##O5TaOg z)js(a){McYwYSOByP?JRyT#l|8ZQrXT~CQZJ9q%Vd^_2+bSWbXmut!vMY27dob7s#2OWfdL+PjXjXR2^VBa}sqIie^)vorP9e&K;(Y zCPj2@bhv;#*{W_dg*xu`lgTQZfsl8`KhE#W{znrdm_3+v3fZ;i~IztPeimgca3&E$nC=#}#^q56WX4`g^icNJ>< z&EWPUT}yAY(I=^t+;Z1Fj2=e?aYO)qmD5C!Lp;Ey_d}-TwM9qky&KEJ93+^`ahh_c zswb9iX_Lyqk#o%`4hIWYl8qt*dB3>#lA7-m+#(gDk}j;c_BNJUR4<>rx~F5>Vuhsc@4W0SEnn?y60zYbK)Q$`5W)z|dgpW;&#jD2k^9 zkI__^e~??TW4I8E>2S~>MZ&DhQXTh>4jh$S-HcaE5$`r(VF{ zWK)`jeYvRe1RaIm&pWVTBLM>W+2lny_|ZPZDrprkd`iN7x=Qv z3!-2or4j*~ZEfW_s)4EY6Vu-;%Cnj%u`VgbSBuS0W$C7mmZp{>kphcxd5!P=EBl|( z&M$l@Q`JaVaA#0hQJk;$+K?_XT??EQAFMF*WY&1`pnDL1-bFe(PYBtWb2|APlMAbC z$cEBfJt^7PT0UZOj_yylgbfdTJd}CcQQg7d^R>HW*9*|d@J3o6Nr=U52(24r1`el`E!-mxao@N z@5fT=x}AAPTl!+_hxj=hSk(li{JovB6Q1VT`3`&2;dgdqhkv=m(AL8a3hxBC8V? z|ISKgWa6`7#7Z2mXxX>BVuq-m)UwN1;y#++fTdpo*g*OCV>O91>JqcX4*Vl-X!td= z)nkZw$QGz=j@-n1ERaaLfon8WZt6T4=z!G>DLQu5`M-EyYPd`il|W5x!I>0D+#U0v zJmBUI*Z#k`igsyB7x@YWsGeVx;zpw@Wg;<6DO_8Mm}u2TKZICFsEniFj#smK0ryM6wv zU{1~W@qfTSe40ZTCX0cgd!lOPpZxRvsYt3EI*gZW|Drv1Vzj*Ny@DYW@h>^;{MJ-? zhU7N`jvF99O`aXF_BZ_&imo+T;lM8_4|$&Qb!zJ@#-p1jV){>8l4xm&HS!a%v#>Kw zjqQ)``-T8V&)QgOQJ-8(dY zj`lwlM?Oq z^gI6ql|6G8lpPF&u-Hs)02A|AV;;FwG}@OV&rhI~jRoygH{o8+?NUZtdIylunYp7b z_(7L_DaYMN#l|9y6mdV2797jZXvWu2cUX00QP)Aepw@(!Se6IAt_lK4c}Si&*RF%H0qxsHfk z{#!%NDIM{3A&eXXAQu6R@{VHEtaa4#qj8}5nQS-jmdj37X3C-anYlF?sZ{yQn9?l> z6bOYh+!{c&+LUUVwF7!{K3|8IQf}JNogyZn^cE(#@TA8z%DVEdY#3V;wN5^K+&NqD zA32iDO%z1}6sf=Z;4y!p!nfx?h4JpqZ)T+&xPgit<q+4R{kMSjVWRwYJUTLk`E?Knuz3!P=J9WI{1=Rn_>na`Y6-mkvNJ;LWthN!m+ z{r*hnulM**%OY(<9*r4ZaJJUl#i-?zj{+PVsk;+)V$bBZ7NQXkaQpzp3%6!q4ct!dBoav<`X+3xR>?8ZPTrN8tgRGPtgUF(tR&kDO z)dZ0$N!|N)hi`DynB;(v^R>%UmhU|e7Vny)nOmKlna>YDPV@B#k5Cm-0ebp#*K_Qq zuHlkSuVX^1K%Y)}>0#Amv8;zr%)ZWAEQXh!o8pmLcQ}nff8|8_aBRR?HiMGLl#*si zb5f(*FFhhOa5-gqkUO{^!_blj$ra1GCu3<$FnoGqQCAh=7+gC^M05xmHm2`==>r8d z$dX|>%%crPMPqylB~XXtGU77FGn_-b=nU+x4kI8Z4sxktEF<^As$!!^4kpA?OEe6C zw$(!4-BHcE*0e6EZRWcH*UX6K61jkw3p*2@Sk)xiMxLB!$2ArHq>gcsi$S_vU5WGq z+7Yx{0OKs$3`EhYZk-LFrbON1addQDXxO*z{lxOu9RHR{|964F@FgJbE?~q5h#6esf1+WX|W4<-q@;fCeji${wfHC(2TH5`XmV*6;|>K@`5%>qF_90mq3!r$A02<+JH8`39jQ> zBznA#hDj8o?3esVDUv~ilQKs8`+^#gpoHL?aw4g=DJ}OBwt{`c8A~Tm>D4Y|JIykG z-S}6&7Gzu5NQw)0TpilXvbIO4f1tV;YN?~zu_prjxot!z*7V7gxQa`s|3Nt~R;qYQ zb(QZ?2|qeYNa!jThy+qSyqd|k|15&U4>L03oBYL)x-i-H&Dq3m?`~0A)ojZxs_sd{ zfSkA_t-ST+w6P4KNg0KLZ;uu;hLeKbT@~5<&N0FG37CF5VrpLi!MH8X+dW#2WVYv) z(UGo$y!`_ohbSWyV0c$Mn&xf}{l9iXCQ_u5d z0;9Drlcf2|Z%+Fxd!;j+O(kQSS(8U9AUl=g8>g-=JI{jcZB@^%D{H43`NLWaVDWDY zEFw#%OdC8TAb`uBnz7^2QE1Fq`gaD(hQ@4}z0me#kR{oi3k&9+W=Y)|K;2bPNk*4Y zjR1Q_7a7Ky#q`S?Jc?Uoy@pN(-pK+4y7G%<5r(%Sw08yC2e}wayCw&EWh3by`Vm>3qjLFR`2{+fepPfkOxnTZ91&dNG zE-#wH&x>pd@B-b>a8Ir4;rtG&nPL_|@ zewWI`)LDV9hb!R>W|NDUrJ4~*5e-lBebg}4^4A!;7k3O8BEoj`U9+bVJK}#zO=y9% z$okXB>K7`h&hpo4mHC`bfycD9mYHbzQ{=>1NXB`XuTV89Vkm-e)qQ-d#$>#z36v(Y zzzN2`j%fCz%4x=zY~)#QSF5BqL(s#O1ziA>a9VR2r-q9R%8mtB;gKQ+{?H9FyEaK3 zU(b<u8zzz6o>^=Jwk zk+ZvORkj!#&7(!sfwyYiHm6?NzJfbZPFs6>51L3pzM|{b@B@%w#c1=rSg++5oFW{{ z(Zme}ehTsc;Izey1y0**qwx15r>a*=$DMgauV^2%Mrm8?tjm9^F)#?KebKPm@}`X5 z#Jgt?hzs4_KE0oujh!gi{#6Z}52);T;ByAjjVjob-S%BV! zvmZU`n4WAgV6n=j5|qo<(z|~eKKc|@+w_dGbF*Yu6yXpn#2szJVDb7H^Q8T8F-dON z;(C+kY_L@>9Id=UC1$`a^j6`?W+|voT#ho%Ch$Alg+l{1E~m`Dx9O*;PC{7fx@OHf z?x|4xZL0X49a6Pc} z>##YpKi9-RL6FMS&lKznyB3jQ#~%fY1eIQAc_jZ_-~S|euTS;3{pq*)hsgeKtD!@t zmMBp88uNEjd5(JdW)VK{N8S26`(x?+@BEqd^-e+ifCWFy((O>vP+v48bP13#4eIjm z0A(PcG$U}IpUNj-NB~surku!D7r}+j!e>BvNwe*Crx<^Q{&f;gKB+VOKfS2$6Y8}h z?c3vPWLrz98k4Q$?FT@I0X*J&55L1hh)C~55?c!sQI#{aEb_; zEs^^Qhf)6Lu!5EfBk1Q zZ+}T>qg{gGuh^Hb104C$Jy33-z&OaWu++6dvdyofy);B=0)uB;g46HRS{l75~)t7y>f z`#Jw!k)ovI%9;A7?}z{XMLOVCJ=!Jp;~+%W+Qc}hJN~FZe}yA9AZOdYb~3S`f)|3`NR^meKu$G~WmaH$n8IqZ)+Bt9G-JtY58sm=*1)!lhP z6NumgU$6DSk6OnyOBnS3@%h$x&e1~7oY~Qg*^gRuZZwV_lGOfAp?cJPUgPZ~uc6+n z!rKUGHHszMl<9F@L1)H&`!>@71j~JAN4^b-8B5Xdjg2E6FJAmHJX-Yd91X2m;Pa?WlL03VZ05UdICYaH zmP#zQ*9`9psmxopv{p8Hm+>ezuo}hYAp~Z`cQ}eD-w>XKwwOM_GWVYCTXSGb-Vi|5 zdaV_t^wRk&xo$vApo%)*?|&=wwM4GIj?@c-YI(ZMuo|c1bd=`hu*xEXtndaNlBF>9 z%!<{P+!k`Ft=@P0(NrZ3G*xNCz{STG-*-ezVmWe_HWj>eGJu9;W=|zb+tqTtiO)A3UDnDXZ18$c#|48cGFD{TblI6kpMfdvf&A!i z@(|tMg#Xa}Tbb*NksqkoZ#BVH>(I)v-ph7*cs^j>UCGaapLc#ROfz9Mgj`;tb~(Or zgLh&rr+o)nVVae)o(P*E+^;aBivdPA2d$_`&X@{6BYsov$7SjaDfcV~fF8_wrU~c|J zJ5iq(DbeOKuqN@A+^bwzGZwA4yt-BEBRQR7nhx9Y_$0kxxp)@xL@$8&F-6fA)UuWEG z+2|_2NNIS1-|plS*Tg3zTI|v2b_3=oHD(08L{fF#(!;*W4TOP78E-^ zkTvb%wS@Vk8Z?H|+7qhM2@%{AeVlfMi6m(H>+ah}$Qw-=9``XM$?)tMUdw#goulX= zq1I^at*KS1zyKxP-1jSCQaOI~uUOr~B;`Y9RqZboQ`{-im55vIiwb#8O^;Npzcb1T z(GuA$e%)A#_ch3hQT5%fYSiv_HMl%zxhSf7*w`%4@6vlAAFE^r9f@iJ$y3%G+*VGHxeLGoDN9fGG`EE#CJ+zGr6qnL!n z>M`I7?fS)b5ignx!xJ@Rby=FHPhop8f^zIa|Fta1N$XLahPf(Uj;Qj{U@c&XG%6X6T_MIS)IK6J(jAj`b6&(%>Wg{!sV3VRHM8Z{6dbFj z`MJ<7TTGzpY1O4v^OU*<{-Sxy8P*-L(a+lo@lXBjdZxL1#V(EIcRp;jQEzQ#`CNRL zPj|y~&=JRXlvb1YtBsZ>HTPl@A6D$82aKKAH?L*uWh%j_*Gjfc>CA6So!m(=pl>k! zOSw~$bZ%ivDf31j?uRgTZANBZr77+PK6v^IPo1ms!rbKC^@+m3#TSaA*S)S)%&5erjLC+1r1tkpd();v`hExe-wE4`M zHN%Wq)=?pLoLsC7p~NBc$o0LYF;UOk6&8$_GIdhAVyq@!;kh1K`IqqNRd|;exL#2Y zBaf|*7sKQAR&R0;upeGHENGm&KuMIGbq?VwSz(LDXHnx<%{<50oR88oj~>h ze{+VAI9sO>-f#hCsfMUmWCFSIk}KI$%VSd(W^pfwA)oLO&F^|AU1@#a~mga^mfbGwDqjQ)`-Yb_bCd6yoh z43bp!K?+uE&q3D;0{8sG2!pZ+HhE!ugeZBZ=bJ~b4~u%2J>dx~UvRmmN~gHb$HXly zD>soY`i!ixNjbCE7EBEmJu)U{Gzd%(6)3@HdnI4pu+}VqWdzgyo;R{KflKIo%0CZx za;VX;XM4#?;nm442M~GJF4M{4|>*g58a7MhVJgq)PxkkKuzTNLh;x+Vvd@myJ-*6O;OtyCmOVm2AW`j(e4bgbSR!IFqMisvgMWCRbcls^7 zOvcV9em0)O_`LzCuxxr!h zuzR)&rXP>_8mHBF!csH}?UU#FUmezI)0B3j?F&Co+3AR+B|@ zCO;AZ1`u!mbB}=mh3F`|Mi30}u`l^!iNe4@+WCIQ>^3K)r8T~Z3Ve`C_*I3h{Qe1ib%(9N)FEg}aWBLe78*{nJF6E3rWP`aA2M*Aqh$fvC zr6y+~eFlg@C3^*`{^S$QN8rPBXjMEY#6Dm0x*nzf6+X?R9Z7e6yq)$EpE)vjFO^q7 zB_+0Y+=HO+>dhVP5PAE&`_VFIp(y4@y8CDBoFGBH0622|1d^BCAZ2c3I3jHBICzpe zXh4H^?&eUUNGqk)7i`v?ZjzgidfHvis*awmGe$^rkKC?vAO5Cv9d2(jpxMU-UO;Tkb|6LG?m!h=d*k8WnUv8jIj%H@Yy75dr}ReKif4e9$$$sp;YNx<@ZM`Sgzt%%8Z^(P8*w^R+@{NO|nVTq7Iis|2ejY z#Cqu&jF&S%s>;B^4N~-HFY5wb-0OELbZ+O2>?Bqb-HB6|IY&8TJ8V;mA~4drLSlvt z8SM2)ZwOng;MGWYSkoKyK{>vvDaBRlYUJEongr0xWwLn72j|fjSfc4Y35>~$lJMxI$M3A=E;GS0O(hU6FOZjQ z94D?zgx;e4V-r|R_jT3Obv{Q>R3Ds6l-+sFhS^B)gvv)hDq*c6l<7i0dHQ^|7^6XhZ+TofeAWDQjC{&gVC!L}uBtaeLDY+nD%4;3rI7W9O`+4eE} z_c5p#q!sh`XQx!9|9Edh4?#8Wf1GObD)`6U=E!~>Xt%@)pMqmdS$4`fhxFHieeCNb z1R7KRk9@j(6ARBtJPe##wc;zCYuc3`s@0};Z@~LU+fV5`IW8aco6WE9US8gm(neIy zy4ZFT$E`AuQ)teAs;||RY1~{eV`~*>MB4B9`&Tm5)fL|EC<%!zDhw;<)hN!33(YB? z_g#%HGT8Vsn6Enyl0-~$c8EIugXp@iM_PM?Y`ig;)Tx^Il&GgmL{(B1?qwZ(lX zvAjcl4Z$@_-6QpxK-H^+nLV{JO3+7+*?zfkeCxe7sBFQ(x2>!4AaH2*Z4Z?lwVZ_} z_G9RNXu8AueYEnPb6kaVl)h)onu=ns6zhIC{*zJn?Q{G&(oRxJba>p8WqycN8|vSQ zox{0AZ?eTzf-Xmcr|5QNP1diSgmA|j=bviA`zT2`uADEv_o^n%x`-MsQJ3TL;{GB_ zug6$T85pq1d05}~qwi0#m@MJtbG2i-HCol!rvnL@dY29uapch7Z%l|FOT^E1lA?S! zwj(yOQ%dkNLjokPXYA2Ebh8o!Fv(+U$99a-M$TqqlQC0jmtH2fVSQ8U9422FY-9PM zKb*p(4i_fhOJhv&J%2K1-yOoPL!T>{zS!^1+h`@iouOeLmSP46 z4#CbMhkc)%vgrp1i8!W}6wU7p;56oi_e*QmcD2~QJ9ISQj<|atGO>ovZfCx4k>fp{ zvOU8G;5_b74SlZHUEV79^M*7j7ax%@h)~t4ecQRN@gkSPm>Z9JB$+Awd8`T7#^B(a z%e28B$=jU}}G9BtJAdiA&_!%)r5B!mbt_0L%f(FOo(uvRY`Z&&|=9Npz zn{cF#!{07RzU`lwo}=Jp85_?ueL(E`;OfF_t#W2b zEhQP+0LFI`w5)`OHG=z#FLWLpZjF-0BY85tQ-*myYbf~&Uo{iD0YW!dW(R2UGenc% zTLMekl9W`|@LM8cW-w@ne!8RPGRJb`+Uu!T*BrQEDbMFSzu8W_tlmh}&_|UlhBU!@ zOwP%z4|Ca9+k?Wb)8jZ$Gnv(?EzNkudPT3_KN-+nct;|JUVI%{uXB|o0h>W!b?$vi zkgOHo7|tD|T@DR;L}(&gKS5kKh;7RIQPA{}R*q73QvX;@Kc!8k6P1ryw7UM$N5b4| zx~AZMz^pbE@!%>SK7rCZe&EV~49mk6Qrg27ytxf*=Z~aSD##mOO#Q*X%{=kXkU$q^syK=R>kt|ABHect_Wls%BN#cOJg=q9KU}6pZsei0q&z z=eLG2TKhLE?3-C)av%D#b*w%NXD&59e6a|w{Z_llEsg7gAzHWzj?i4T(b6&6SS8yq z*4`^lbw1@V70+g^k~Sl*tBB&dY8BYITkFcR(NE}vKF!S9>tg6;r)aFg-=tz|rsnF) z(zphJHML8|g9oVDR)ZFD7O7A*QD?Sg6kUYBqC zy{7Xb(PPyR#Mi+#TurvZ;M}rv+=Dv}IIxLa57oo^r?h5$j0-`N=Wg|LrGC0kvi4Y` zE>iuw9Lqa$iVr9Er4I7(O%Pi}&M^Ta`mmsg`E|xaTjaCblmWpT;;wHf=oq(h9cAE= zhgo98S(299bWnIll?N(ItYtZ;+pm@#WTHt5;9|A7yo&AzfpzNVsMg6!sFXUa(t8-) zBd2y9S_AH6;f@;7&e%sAe$Kmroh_TCVVb7+{7`;X>Cqr(n(`U1i zv}wWdGsIx3bn)foU}$$s2UBn5iSzwD%sUW2m5MAsHT#Lhv^-uIVNo&CPU%z~54dTv zfYD1WO^qQIBWuht%{0`s<%a*w&Ws<8M|Z~)nJO}&=}TDS_{N#DXd@d~R=Il0ZpcuSqeve^YJ!~7{xOynas z>L683N1k|GlVx?SafhMI0niSaHjdi5nW469p2-cH>I%&d)syojlg=9b6<70h-?Zwg zGcG7&<>cMoc6nKM#GikVK~Q?V&aSihJjk}A3ihkY#hxUVX~u5?`06j#ck9dpD9Tt z*{`I(9cL>#{owS=VuhDPN|69MiVpmQjs{O#CgW@mx|fhtj&b2;l~Ya?_TH zhKFYcmdlto%;u48ow;d`zS3vlNF-jm9~KZ$-RKVCmo3%YE^VIt5Gvu8K|l5|MZB)6 zUvFrlE;^;&CAS>^im^B@{@>&4`aw%zHp@Yla zs~(i&Ov1#wJMz8)YmLu=JC-gbRP{pHmL0rWUef!oH;t{7S?-HAUWfTSG46`y<@k-Pp%3b1O2|0 zS-o_KUTZT2x4H#&n?I(qQFB&av{rTQ1I&6!K`$+1g&emcfq=P+FA?FcJCFG%Pd}5_MD%j-PmNrPr>phnf zO8i1ZF;eLzVC5FZIiJVtIA?(H#a3Mrq4L7knTUG=v=X0M9^6&XY*;L59PThJ$h$Eh z;sYTt>dVqruM!gR;^{FbQKxBovqn|5T)%FFsRmjV*@O}oy3-pwkk!wbEPk#bX&n{V z(5z%WaXs$?;3__B zs0V7OI}#JxJ6v1l%qpPV`gLKu9FCk=(d6CX*YGMC$<(qNo%*CAhn&cNbODRsvUG^k z%DJ^Pcrv7tl4^yA8jZXBogwlb-J{R0V@q2<2x@?=r*CpauMW>+D=;K>hu%AJ2K<96=&=8JI;@pFDkm19j!nlSuQK_e+0Iw^SFo)kP&3Ty<}6L zhd+K1a$YnsvP#s3~c8F`^_g!}a&z>P5eg^2c=9PQgi*3XH6f-svRB!ZA z0wq;G1U-ETemIKjo<6%TTXodC)?o!BKZ%M3|1nyJ1k0|89kR$^Yp z{*;NF@xX5ZwK**JJWp5H>~T-))=JtG*e-zwlr+#(MXXkB>EV{4Zq9PLL+Ci`2cF+N$bl|thsT$t0K+z&L^e#iHiZiv6i zLyQO?g}rmi=CF$ zFQJQGcb6R3#%hK`C1ljrD%5n}x(aW990>EZOX}0Yf-Z6sS?=A%gjQHZsQ7lR$Yd%c8IGuu*8?N;35RJN35f3&jL^Rv3_4r zD~E&NFl-ReDQ_Nl#i_XJA{1hCs_~+x+aPO%hCAOe3bgJ=j0dA<=kOa!<1~{x&B@Kj z2ETP>?<46A@a37W&Bg+mAUYnsc$B`=lNOdo^c*Z|4&fmq^oWmTUb`t-yHUrv zb}A}GI~v($e%sEV(U!`Q0p*ubqj#oFLzHER^**jDpHFQJp98BDa*qt6^91af`Dd$& zD5+Zawb8GwMdIJ6H>I8l(KpqQMbg((pm(vU@Pb;v%6lf*0nn-1R&fm@XD+}dc0R$Y zajCAo=Asg#hj(n~nL&%mhg%+46s44&(|yI$i;{j}8P~zC^uGSKsNmvD(blBe zC|7T6)k3)}aoN$pgCW!b9^jnF<>i_zk?Yvbqea0o#MO!hNngUic?_A;klmBA)I`t8 zsb843$AF=IOp!(JdUb-t<)HUYt~f<59Zb)j10|wLlB8V+8b*GJ^{5Vz^8I;bvDCJ?zt>BwGpo3Wam0#H05SLhS4x<*A zJr|Jeo=o8G5w&a)4NpL}19cvPFvs|+%Q1RqlKNl%OnKas`1$Lw1s-2haXH3o)IzMI zlZimLa3>n&h2^@1rSiTb#olXh1|;#^*Z6Q!-HRxHF{M5 zzWQI*(w4{{3kaQa>;78$$Cuum`nCFM>Q^}b50VsSw3DylHx|241t?@KkVwUMO_dv! z|I!0LT-*ByzNy{tMEC+NJ$;?}{`Y+NPTyYMXy^1A?ZA9Q?QUN*s$W6hslv2xC&L=f z$8U$oVH^x|Lv!H1#-iklV>Y2RBlyU^O0UotckV-`c4Z=o6PPZVb%kP;4I6Ur3phss z!&J)RtztamlGBQ%F?@&-rnrh}-&L`{#)aE|G{6F!ORpsTzb_+$S2U5QQ2_2jL$Z~WTr`=&ZvA} zIkFA`JsKvaNM8OvN4%Yxkp-v6tHLv!!i#H%0&tK+tPDjR6+Mz9D zAHDXcc5gGPB-1-L2dt=Av{U~+7C#(&k0nHniYw_T(6#pq*ge;`Op&*HPpxZlzH$$( zsz83sY!=I9OkP0{hZxg289YJe>b~(OMG-u=sSZKG4+d5p4WdNzpN=XmgIm~EIxVzg zDu|@qZ-8TX@xIbIC?h^;B%X+3v3lM{4!wv<1Dhxn+>qoXEAAp&kG@JOSSUGfs@fEt z_M^XY$=i^lO@olFFAIYU$6QU7W^u|ppOt^WpA-?Ebw@{~*|1KtDZXPLpNb9)&;?ys z!YazPGYx)_1}RLl@0tf>`D7?{K~%*-x68iGQd6)g(sFa4LT5im^t#o!OzF~k0Vn_B zka*m3*dPw*LPE%aAh%&x9eX+{z2JDMEh%i$o~?9?OXH<=YP>KG9!bzvO4XYJb-sXt z;l#m?VUK2$YlH6wLxQ%>g^fRbh-SiTm^}1+4=2~7P{u2=zg6K|VF4!s8*rtJD>8$I z%5^(9?dZ$U4vb|5mOSgx$o=~D2~T`IS`}P6LRe9^SAk5*gb^0hcDf%HmZ>a0O;02c zo*?Ab^z7{A@`B^VPKuVHY9P!Gvf>j}}Ep|&`f z+RH7vb(-HwgBumtU_kZZ|98nw$FR4EF=QAsO;WXt#pUxfcH9byuIH_r#DJ_;iSR2P zcfS*$5NK2F1xW6}ecG(%vM#o=!El|Im)M0d_=gspp%jQ>RBx-ZY_C=}r*Ypfr~Pxq zs|YS8`7ieZqM6$~K)a3MhPB4gfcVc%&blB+AV)E$VrNv=2%S`^C9eyOo7Z5&ntNC) zYz*nd`KsRFz}P~^;~-s+#3s#enjCgw0|S(5u{kncm>_RjoJ8#GnZW2v)T+$b`b(kA z(xzT#@HBnM^ih~SjA0f%OaWdgm{R6kqk#KQmlw%kazC+3KocheISlr8>RKy|JkXPo zXCsIIC+fV-m=_q0tTN)BP<#&B$2Bw4uqwE2W8zj=-0f!jdIe)Q)Mi^!Ww?S9g2?Qy zh*~_432;9|CZ7x3{+&MnZi<&IfbOBjlX_;Cv!~HH0a|j(l25e+u7KdK%R>va3qpi-<-VO6 zx3Kn#eE6vQVhf4wj&!TpIjD?5V(~p`UUvwmNSAK?2>vZZ_VM8rhW8YWU$~jH{?O-X z>EI0}Yd6#i&Z;)-_UhX48mTBz6WKx8+=Jd8KZqrm=v*F zn1m*Yxr7spuN?<)pR+HXQn-8`_ANJ7n<~ug337rr zEMoe6vvHiJAXxO{&xcg6f0O<<;r|0@%7l5le-$3+>J28u2gks7Pib_a2K3io5p#T( z?B)Y_Z(JC>B=zDW*zV;a|Ar-!ss0LCJ!E3j3~n4cms?61S-os9e&a;^PXlEY5oodv zz2ij7Az$YbsTg8&zbwkjY^|?xalg1q63)cm{Lt(wtZ!KOpewhcpDtYrRT>FzG&WkR zXfm%Fu`8*#Gq6JlO1SotoX@|=2uv*04Xt9%;kzq?e72N_ZLVV;k|{_sn<*PB*=CPi z_7cse);ss`|0C&7>#FCO7En2?{|X(DB!RKv)Vr4_cIzbsnj9A9GLRd7KQVR>FXu(B z0%m?AI|Q-$Vf03_8_?-yHF3{EA3gpHkzgttx2Yp@LoXqOBu>2Y|1-$dNwTg5|9>&l z`Pcj@@%2AM-rBI(ALtYfWb5r-3ly)jIwRA`MxFl+(|*n9vbF)Htb>Y+@~pK=Fei_S z7u@TBd@~vXA-x0b;+96u*lUxc8BREYf7xb#X4*vFaU}d`ViDM;F8=F(9jW~OvQ?fz zzJyIWhr4Se-Y$jFT2FH`4^MfD&j>-Bni{W5+U>BpN#4*_-nJUQsyT9xu;4Zp6s-vJ z&c9=Q?s1pFDech*OV^RDcWpF(!g;^s)3~OX+UMBFB59~38X@)Oi;&?PwYB5k#EAE& zq_-{mPZ0$Fm4f2kDyEhE5LBqQwg#*n)U~s$AWHKp0;yLmyc#M z{43vjtL1_Bzi4~wu&B3%ZP)~rQYmREksPEOR2l?9q#0VeYiI>&=|(~tq`L-?PC-E$ zqy_1rn{N%e-RyJD^*-l)-s}5Mab$kE*1hgpTI0-=M<;ZzhG)>AmGhF{^u@jDM;M{N z(~2sjF1n=ww1t0Nrh)&4d%-bO=JDtX&K5rtm>hWw5w%FikYDxFs^pNQaV4a1fwbZP z5%29(I=i0o9i2Mh|EVTWIrsl0C@lB)k26!NthJCltANo;{)c1E?@H~-Ut z1h1!8tDLXs(*CioD?MK?zy9rD2QmtB)%V$xVjn>YeZ=@9!cn*H57rJ*x8HBr#zd-Vw$M^>Rs50kzP4SN_5Gj7KgI@`vx^D(9 zE`NkD{^PH82)~M2m)eE8#xqOl@N>TifL}&ympkwki^bOt$C5)M@C3ml#)R{APnW4m ziVeY0(-;=nq^TLiS!wi{=BY86Yq@67`Ul{OJoo${R}rubgIKWeNvhM&&79)tp7&S& zX}`JQM7Cnz_H?Y{=tl0}=FOIN)TNNs0*w9*u={TP64{TC<~(1dhBWP8&-+`}@w)L! zxi~H)?Z_kg`HjXlc9?XUlKa!>wrImlVvF1^KCxsoywHdfvdIrgn|5F)K~GnU%BDp9 zt{l&XE+8`{5@dW&bTG>6AwKmf|2F00^XO{c zy2^{11T<-+!ABVwe0YwhK0OUSV!DYHW@r6DOFFoG)|=|1Nf>*u`zS&QQ2I*Tb46V< zYO;fDHb=>bXQ&V!7nUHOGPu>dZ!8kZxL&G1w+$x~%sw8hI>P%~qZfs8JfmIl7nhoo zZ7U6ET#rD8qPEzR&iHtF`*YV=)-L=}9>|`s48CxbgXqfg?lf{7p0*4Hp087A#XO!y zoop`;N7L+ZQNl*RS~tOw0%_K1Jv)>cCHc{qV0$`mV&POygp_o@6pGlfIsu(;zlw;D z>dhMiR|L;0lMs)>)NAmh1TC3MTZ}dMm8i?)5E-~p!l#UWaKO&CEvI*^9Y5=auCb!N zsIwDc^qM;*Sbns}i34A$5sLhYHEul*JJ*n~GwL`HR)@TWKZe|K4&lqR1;Oy(PfLFj z)V~WIxyM;Ix=|p}frY|g#o}Jk5>VC?j|Z{$)hdueL6_rx(hxwBHLMSpYHQ)x^azJr z&oi#0Ktvtqc-8fQMW=g?8>~z`7)&&DRMmr`u0aZ9`UfOM6trea$>vvx^ptvM{8B2bA|Ld zVfZfz?WiBkXNP=@x?C;tI*>kEv4u4|;uR#*16vd6(BbNznGz{`>*pS|ymO5uNBcTN z5e*-cun+9l!zdWWOo8LOhzDs!v&?ho$eOnod+dF~K5kUzp_Wza7^CX=CGqtFf~d+P ztqV>ais^R)P8m%@^_=Ks7sj0Q9u$Zw&Zn&Nz|qoPt*y$2{M%gdE^U?U;JIrMV)Rl5 z&}G@zte`xFrs6U?$8)mv@pj{|H@2QKTD3QW9H23Hg zviED$g=gy5h}_fn6!``!ORr>~6&GBmPt@VYu8>AJTn%dom>{7wLNqF%-Fwut;_)rz zQmb2Az@2hmhX$C}mC*Pv6hBGlO!8*1$7R6lQ08Zl7%~o|z&FD8s5~R#(=LZ`coTNE zzS9`@UL4D7@Rt4@-|1!Xq`C$%d-ID&MhcOok!bbLIUT3-%{BV2PlG+IFhg;TxL2?+ zSH3(^JXQk>0uLPSq#}*=<2R18ES;_q)1KS{#D~LM`V}#EAT+xXHIWSg7MgP|KkUJo z8CDk)=1+}8K9g+;KeHxthz*JC>aO26P~h{_@BsB)_>DLg^Ezg!pe9?HID~7~6^LeU zrsn%AQU)0vr)7TFqra+1{lX`2zHt}ogOn+nW}?($$y4rpYS((8;$y+5HnyB}^~3le z_YLUim0tv-JPk-TsTJ1^81k3H{OAts4I3hDN*bBym-En|<7TmQZ0zy5UE}8CPpD&u z+=2r%+??ZDZV1hlKeHdcV>UMKjtkSfMq4eNcLVCO03in}7{zN4+Wd_JdgoUa0hPEy zQ~Y=Ux1E&4E0)e=H$y&u22P$889fk2QHeHiA#R65+BiFIDl0go@=HFn6$dM0od&5* zk4g9jbtTEuP$qHtR~vU7oNq-uL)xUEAJOC8|59Sb<_fG1JmTUR3sgw!rl_HI583qxPVuG*cdwHdzkg?ewBWG74j4&J zV~fZXK?y#|AtQ<4{pwrP{oR&S>P(rD62yl4L%{NL7?Y;`teTNsrY8xxASu-gU4XFV zkxUxvNz|ysOi+i_@7EVnuF6a+Jm|MDJNVJ^<7_6Wu&$_mva{v%M@ndEi&Wp|c6G(> zbu?YYPdB0d-sBATd~qoQpeJKRbq(#VneUvEq!jnHBb$_yb`pt7azaml4-rs6 z6~PDc8hL2>XT+NVS91R3HUR(nhDR7ohRkfeH=&Z!P%P_8c0LWpOP#sd1!-%E%bNcN zZG0?6RP7S{&sgYvk{y>RUZ+q*O zn^@cI4!1(PmN)hME$9>S8)Owz0#XI1-sgrO?(fRcj+jrz7$|6u1nV9P&Tj!X_kRn@ z&Q6$j_+jrk$RcM;a`j?8xESOw_afP80M&jnZm-#T1z}Yoec6BCi6z60KGxe_cXR7$ z2#_7(B=vxX>YV2h60kf6W~Bg_Z3AEyAprC9nLmhXT7C+4&!ETn7i#%>p327N<%DeY z4<|kQYn1EQUw$XKBmUTG&Nl;jGq+$)PFDF;PecvOi|);s6e++>^Ll>sQAQIM=9BHp z^)O3v6&h5`)@?&xb5K(GKxJz`lf<1J2j0AVtWwR7>wj-7n%wUeXPDV$T^vUKV zzlr1Qy)6AOX1ZH>fLpHP9#hyF+k#~VLcYFHLqV0s!-?t zo^To?u<`lz$h-vBojR8E;a}}nXnBFTd@B#fi^+(&_`Sdu*g3Z#Fpa#s z;|{oTqKh7<_P#O78tT-v%1VsTOZ*3AxR*Y)5m-%kb zzTLl^I4bhgmk6%Br5t%S0FrUb(QMn6nO-%79gZd56PukU8Y&w)QBtP5xPZtz*}hlV zzf#QVST|otUlZ0ADOw>Sd5rH~Qk-=bwyq)uIkTJmXc)KUMOR@Zg(quC;fe2{N(|46 zVn+042;DCsv{$065%K2!YxX zf|7SNSQ0tpRf5;OxAeT}L>dXcI%TB(24~kw$$m>ZzrY##_I_(7qc)EOc!7VPmAK4C zW5eSv5B!pxXVP9b>q1&MW%5}qhWAe6!0VxNXzYpa=rZFPN*<8tKHq?LW>^0t(!ah- zgzDqHQ4I<{Tv_&>tmlLpv=Le@tRKgn=o5)UU}_nFAol6ce<~4xUyI583zLb~%~BJ{ zRJT=jQ9S_;Aj42wtWGueHTxMwh@uJhgQpR%k)X^(*pTufD7$mx9=lt1KFO%<#$hj7 z*loD&wclq1!tE*eOV#Jw*%lN`wU99kU-m&qQN4h~tbwno0&eX_4-ZN9qi7_OtpcTc z?UpT^NmUb(sF1k)TyJ{VuI3MZT-p5ROZoWfKiMW7Ll5xJ-uW98>I3#>|4GbDp8ATp z7$XP++wM`YKD>Doh2pwLwCtq6YBE)WnD>-Dx?%+-LCQ7Yi%$IIizdda81F>C6L?7! zg|&mnWp-o~dPaO{zP#u0M&F&k{DcH><#UHj=$adjRZ~n)2-Y}xaSEeP!d_@|q{D%E z{Rig%PP5 zjpp@A=1HTk(d9RuL-3wu9DKJ5)kM4#W>jIBL{QXV$0EJZ2i*twdtZ*MUBct1s4yTErEMTJ1JNe3$Ngco^m~6DVuSh91`nhq5Dfw*X z%;Rf9Bd8XbuX8LqsNc_AF&F;Ii3hTUag*w7VdXH1YJ#Fq$92rFu8$+n&ENLwEffk7 zV4t+;*?lqw{`FB2KY!k$Q*D>8g&RvbIr354eS|bSH|-=n`%d%Wm@;n#)e14 z@J;?3-1@gqTZ*XJh5Mgb)B7}(F?mG0yLh@8yUj)tM{N~I?kVtHR&8(vR_8UO)%nWk zKbR%X(w^!Y&_M;{rv{;W>Svn*mskG*{fNL8A7=l>Pya>s|0gGgL-{@iz_wCP_}K;9 zfzF)gBfxC&wjW{rO~wIg?B4(DN3H&kPUoR84e!4=FtATq!*V@eoiHzd|2?whNVBu^ zXq+QVbY=2s7ji9c?LzYz1kJoEjJg1%Dkc6MFzZKxtupn?zrYqUht~S^sN-Kx#K+X# zEXLc%$7uiMSz-Omcj@%_-|Q)cNY_?AU<<5e*Ye7aInF5%aIA) z1b+7@iiMcHmv0SS5j(7W(qm^|SyysXoBs!ue{V7IM-wvBywym$6h5o=n9PPrNzF46 zIC18$tU9zhkU5_HY3cwp75`NUYU|4SwwQ7t__xkFfWo9I;BQ8q{q1i)^>+@Htm3v1 zEiCkI2rxEctrPaCJ^Du1E4q=gJ3nl(XsMNiA_A1lAunV}3n>R1iEplD)vE>fS4dJS zi{<1EZynl&OM~3$66}W}zm9UU#f-6Cxn&#X!i@RLYB;_s=?HZGmp|*f8SjTpk(=L1 zZqd*Qv#UFNZ>!pOs|Cd1v~+%ZnWw0olXR?Zr(L;T_C1S1?n?+}*N#``7gG?(ml(>f z!1tp2iSREE&1YRuCYqP{aYz4ArpQA7gc5D3EK=KXJclvNSJGZUOjFl2-KaNbmcQ;N zCTy>E@n&rvV>-lb^0=n9)gRw-J!*W;lDA2LRWAeVH?F8J`R2vriIX+YjNKV9)r67K zzJNpGuoth>v72h$?khewPS@D(`e1>v^%3*eK>zwMy_)s@D+o{?^e5Fcc;fa|#v>WC zFXSkw7)~dxPPy|ljtV1uW0pTSD^hFFV#mH{WNuedEVFkSMMrwl@_#oaz^TBAd$b!| zp}SU4H6gUIIojN2Yc~_}BHt8=Gl0IhzQ66D^||2e!PATP7fe=_UR|JEKFQhegZpa~ zIGBXR1n&8qOaA`OfCB0kdV)a{iHKLc#|?HJ^RKOCQu!I&`PF}lIw8O5hRHB2QxZx5 zB2A%Drt5N)kQ}fRbS9(Z!2zx=wjaQBy1Gl|d zF!Q{(s6U!!Vzt&r-;XfRd-iAK*-Ezws~Q~r#pG31kaSxy7ys>GuFluvek4FuNph!= z>Q*_dXmGg{IINrENKsjw#*h~5fU-mcwEptCj8c|-(bZO@%(K(UR@*A*aL%2{)8ah1 z_+*06mxh1eAd@@Nf#PF#qp3g8h@=^-NquBMBenZ??2Z2L4kxVWM-d%@TU+SW#!R`J zgeST~+{zE^nTR2(u5j*nO>f-mkRNz({pLRRo!CrFdbJ|iL%5C#3dzQ+U3XM zO}<+9!?1OPyF{knO;DtFtiHX1XW;T?r;~+unc4H&yO> z9ei`SUfgm11XcboAaE}7W7|QNk}uEJGJ!Janv}87-|t}*?gR{Y}q z)~fi;I6X%eAJ;Jxs||`lny&94oK7rdmw29b>-z2TZFHX~P{>L-Yg`Yv&19yFtMjJ` zmZA%hLwlV@xwzwS1G%^H8GN!QvwH& zs_z)0rlVLF4EZY+M$!;KP#BY^UY*nOE75oAZ)bn+3-BeN5Y>T%8_I$#_dI)togU^w zbOSn6&mqy5#ImxD_edG|8Qyx}IB>7mIabaGB3~ZopO+q_dKjGOe^)}x{ruP^tngF1 zT6GS@7D2Sl;byVN?`YdY5N%`sE82D)Y0HLrM1N+r5XsXD+sl4?y+W6+@9qPW+T!k5 zNMj_@p5RR}#N2=R9r~;7(&x-28AI;Gq}Nr~V&*Q~I|8^R*nv%va$w@&T3>(7SCxG1 zIZvzegcJ_dFs@~O_xngQ0+dpwzf8`O0A`~X^Kmyh>KA%MS6&ZxWKggqpUmrv`ek~4 z(PTmbr>bnk&svF%e6~}f^5z}%0pUxIgN4!*g3n{7Ev$FT6gvR9iLTHMyv}+;y504M zijc(DUNuCn)jk}kPkj{5o!XqR>G>2UA;)PcOWp^prbZ%2ao=nQ39YpS2dLZd)AZyX z2NLuRLk8?}2X+cPlH)hp?_an_VEhG^GR_f~^0mg&BYL@G7br^KGjq@1AeW zINm7$&)^{Ey^bi}L<{^*RSUk{s8Id>T!-&^z~)jJ*d4uOtYLYw5V}Y9w~Z1|O26hd z?*Xd|BEIUSh?pJu2|f+mldgXQ4VGV0s%2Uj$>x}Idw%KoY3aXo{6MCUuq(NV3qZrH zUObHc*Dvz!fdOMm**6hf+1KntA|Mp^zk^%<6LoeSRI?bO{tY9}Q)j5UK)kgN`PX9t z5x4)RkItG~PknN*J@}+y;4;TVBDkx@0~ZN`ctoFIkXW`BP}iXIx`UtXmE9ZUDuP1`}5C3LMZLB1B?-2<}@X*3=vG77} zVVWywNO1{jVQKUqxQPm(qe5JhQO}=3Ai9?mu|D$;j%*op0N4s;7e5!bf^N%3bgO_eVJwa)#^8X5McPbe{a&+R(5N;`%h9W??W(mbWYymUQo(p z{brYUwkG)R`z$X8S{dJ@-Q^yi#0~_?qoNgn$dhk%s%J+01VF~y2f^V7)vvU=rn;Dgbd(Jw)z{zn}x|gh4j3T1q(p#{H@m^ zG{nEV-}m~hQ-BR+lQZ2zoH*MN{A`7QtaJLIX|mbCb?!mZw5p(=|H|j^GJVqgas&C> zu}&(Lg`E0Fs?QczD?w^TUL>j|9)#20O^M8y9jVqfKmIbECL*Q~RqEwKB&rvgJc2Z9 z=D)Cj+z;(!Dnz>N);lZ|gvALsY#SL8_!zD+;!Yux3tgd(Y@O6ZM3(^%P;>eZpQrbOQ=~ zzaNt?Lu}m{xq92x64@3;l5~ohd$2R9w@7(mgd}O`5?z|$7UI`AZvB2)_Fg~1?+dN0 zI8F)DYAAQ@O~L@2lS!Yzgsv1eLZi`HQmk=+TTiGtr87KA*5}S(;ho{RNNo}Dz*@f3 z)^*-=(IX)M?eQTslQ7 z0N6Kyhs!n-|_*2DMMX$BDr*ZdCkUHpJKn zJ4QB&#)hjNogTO^9h-OOi&gTwNK1f>c_O zfMf1^3~0P=c2D^E2Ra71&TXzp>P!-;&c1S+z3=c%EI`>*wVF6UuD^$Mro_YRv|y75lk2wQF^?ty@< zINR6w?(j}o20QE)nFDL*b=o!S@2;%MNAu)h#9K+f8uRW};!d=qW=nWx4imOqI_pp4 z&^`P!Kr@b0x8H*5<=~aH1SE@Bqz>A$>r}t=AzsHv_AgA|gmU!4amzd;zGvRW8xP7B zmx{>+pxmJDh7wX%B=A>nl14pmEqKz<9S1$Lg5ele>4yD1%`E8w$Q`hiCo1V+$Ex$! z=s6TATuvCOE%c|mz{dl>*IE4TD*`=7YwxX2EmRyiG}0~Qc16n7zvNWnThQFCWt#Jh z8okS#&P0_F1;O{Y?Th*;a{A%!GW2m@;hz=xDCJBdR~+E&LM=cr6+vNZJQ(qD_tb9!!G!~3#Ki#p`It3ca}ltxXporz`IXp_iG zS}x>%&1mzos>nu|5=y4Nuj+ROPmS;JDQXP?!i8?l4du-FXBB2LUB`-)j{LB}^CkeC zEj6|H&N8B9fXIPJNAtUc8hpQgwLNi?sE_S4HoO_q zx`T>Yu62Lr?Y-owmMscs@RWV71#=jq1K%W5MA>(kG9zVIhRQm;;}-B<62(FW2CAnH zdoPT}z@&EXK#Lfe$V1PWy$0#Mfi%fRZP=AvdiZELz1s0V!8FY24Yb*PJ;4Q46s6$ocbAn*u2T642<^C}FH92@xnnV&8NX#}M>} zqy@c2p!PsTTLNVJarcP>^xxv$>N-IOBQ;=T!RMc7IlqCx9au} z|NCIh|CX}j{oO@yk*l*$A5Y+15rl)zVlkkgFR61Ib#0%C>hDVf9C9sR`US;niS`$MW#Yf>lM>z= z6m*FCchasxO~~@bbUBN};K7o`J4I|KpqA>CD}(~(4XCB`u%hcVmsIc*Z7Li9JcpQN?lez!;Z}lSz_IR>v6JV;?kdz0YLa} zWePfYqSB{XasLV8Y#)NEDNrzwRqR+k-ECnm=77&qkz6D5(@JE}B%+^*9`%J2e1zqp zIW3#+3YkUQxTZ}F@XwU_1^--XCeKtK!35omOkZlgBYv)biqkxM<}GmHEdx1(7$o{c zmezxY2b$mWnSKlZ3~;aC*RFWoZsZl(KhuIqzhc*MpOQ{2bc#Ba-G@$ARe(fPxk;2W z9nO;b5GHpy=#i}+@MWeG#za#INU^r-V;{R1#k5jRE-Nv4A&Cc$i#&Ir(& zI}Tp9^yivlX8Y^OH@LW=x^BU_*Ev4!2sf!Cj-i-pk&2$kUMl@y8kl3aKuw8HQ0rFK zhk2QAl25O*AbPVw13}Bl_Pm6H^FIp*y73@Q-}XMCqr*>Od{1~0j-Q0F0;HoWZqT9IW4?&m}ADBVD?^NWbaj+fb%kaxRzsK!eeAp{Nhz@sqOo4N<@5*g zn~?4(GNXHU(1op^yfdtN7+<88##WW^{L{=kNFh|^l>y1C+tibR;hgahdIj3MJsX=H zwv%?GgkjvHc&cRu zs%syL@B&T80#+3yvNvWTXl#ZnYVm-q>bj}SNd)I=lTV=0Y@Z=oXd4TG&_{vspd9IW z!~M8ezi~u0LHo`>qhO|EHtd$(w=4I5dXt|vTD;`To0lGEq5gs)jsj71NGokYld2|H zxh(x{sb$Trl#l`>Y@*Uk!b+s+?)kRgewqvUWq!Le{=0nuJ8pj-I53rUeK?TFfyqPj z-EvjN?CVE0M6%oR8aYRDZTar46Ny~nn|6Nw7c?O|v5ifdQeXqYfWE_D%~*H)$?$v1 zT|v3bz>5wlswg}$CS6+XJ-5&l30Mb10RxM|3JaZZq_F=qO}f6KJE6XD_m=$lJp2f6 zE|CTqC4rE9(o@*Tt?xbpa8|=E)|W0pLMZ@VzmH0vIL-7}aRcOXVi=+5BGn{i898_Q z^LXZ9_5V!eYzjmcPT5O~s5a{5DMfK_iBuxbtLOO~;VYikp$+d6@ov2u+&&C$N)_rN z3BPb3&;4*Nlg2oNlM=sS9@nkAE4M0*r|*9^=}Y{JlS`hOA4uO2h|v$3?;z-*_r!%a zGs3!clRdD3PFwz$PK$XRq87Di*($(%p3hOAMpM}B?#wtG$rTcXUWj{4e1PV+%eeaq4^!(O=U2gyxjsM}?9v4nf zUJ48?`dj6P$naEQ66mHvlP}Kt(28!f^2;5k)bLyergwq(0YDaXQ~g@~E8m$EprRjP zLAO*5U;Ncmko#p;PYP{Bko=+!nn=!8>FBFpy})drrdu(d!Vf9p^)U@ z>`faafBs&_%_QJO?D%8$St8B&J;lZOL;EkB&(2x|^<8yCF=c+W60CInYhN>1Se$-p z0Ci~~8HWRhL7SJs)478oT09@LFy>V}NQ9N6uQ$TBoWQ99UI}x`t@LVDeDyQO3*75@ z+~(IZL)$KXYMubOY8by;mydUe`ceYX2gi15EdUm#b0sq@k=KV~1; z2&288?aCz~(D`kvJo!g?wj!Ime0sK;a1yK`=Gb<2Eo`5 z`^?-G_w_m*^ zB)Bp8v#|Iv9DGm+@)$lRIOp7SwSOA5H|f^G2_k)NJbT02U=7Uzwxs7~ey@73vDU_2 zHVNG)ksCK718vB0#Whu{U}$oSc3H<}O@YgHP1p_Y#j05;2;W$-<$A#t-E%gDwx`#o z^k6UObS+m|=52PSq8QQsx~=la{qri===;!3vdvoUn=M`6yp9@^S)sQXEI;w)3&pOx z&O*tkzu3lXw&7!s0T@OeAK14{T-c%$#+>v7fEwbwYjN z_RNu^(HZv2$#VVSj!DZv&S}Mcgm$NrlVa-UR{E#8&Y8>%e$nKvg;{J#t4WjJqni*H zu7HX>g2j<4p36!nXpAPHEm#_Ft>*1M8KPE4#XlN>5`xi+OR-gJF}{2U@-&Itt^w^j z@8bGha+|B0PZy5)pMGJL7Jg1#IpjLJ18+RAnndl24fk{@cH;Cqdo@Zm&lRd9F>DK6 zZ|N%JP%*6rnT)0gY{gk&=YtEEgCQICNcy`Q8!`N`yR0{58>aUJVqcHTdPY2kLESxj zLvRKgE8#>3Hb?Sfc&=z;h6u|in!>?5_HZ~s`_*K^0HSF+eQeEqj@3r3dchKAw4@Bn z!Vq_`7S>{kghtMCpjbb)jwwbIQrVv8padFpv#tnn*I2ZF`Yg-N;y7})MY?4u zEQ;PyVO!7bwm4q<;8r8X6SE_JI~M6cTBy#XpwTEt9ns540bY_GO%_DoySaxIV(c&( z*Me&FzAmPHl)D(Pf5c21b*hoS{cnkUOg`CCTy6#^+gh z(N2!n4C3%6xH8LJ3o%1=Ff*C+FL1}#l_}Y}cq>U~^c}chxpx^jiP6QLZFdQ^Ghm*I z9c!$g>BPjw-XyD^B(3LFXYdQ9(ls90T-(_GHoY1gd4x53tZNKqaU08Nr5p& zEHvUNre7COGn%4V5!SN%o8g}90x5>sC^I7;_%zOPJNm89lp6OEotgL+4k`-X3L=I$ zEIn<>B$!QwB5&f*Lsx|I{ll2A9#tKUBREEz10WVDa#5Isdz|x4Un@N zh`xKiqnBT@Gs}1uN`HWjgkp6P^>fU)(xAP-5rqYvjA22|_z7pyBP^hzyp`rMtX92r z7{?`V961%?5yE4`F9IcPeZ4MM#rv7*FGZJ$xN3ZN`)qHuuGARdDG$|fjp1+FGmA!N zz9VF27S3NYj&haz1)Zm}0pYr_#3;^bZQ1tY2VR8pGV_Mn?s!$YhORyfowFhe2i%Ni z+84(?6;(u73^ub7fAj=uu%Zc^7(?s@RX4>QcX<7*>Hc`Pbj-oZh@}#TxcI{*eRI|4 zj9QY#!fnS9DNg=j0Wc$JDfv@{>i?mp==eiT2{L@d}BDHLpWQ^AYnK?Y` z^DnFFpo0tYE&Z|D*r%cX2hh*U<5C{eXj>Z+pdNfiJ5`!dm%HbhN(oZd&G24KUU*qwAlLNYiC;QYXpjZ z3aDr;Ejd^*{w6t4XGABV{Va@7P)K$UXZhu#?pKby^sh>FwK?z=o#N9IlW|R$`DHbq za%;U%N{uHiJC62sXb@;IhkXs9{(=VCu#;_6`auv`wbu0*S1cK?r}($}FH5Z|__ZFo zs5zJY8#D=)Hu`wy(j|3{i(}Jm$N| zc-(E4I(A!|Z$`4QZ!L|ure?uhH&zI0z>J2VkDzyAPJc2{`K%kYpzlR%lf9s$huXG&6XB?7Z2rBRaZp3wu{V)_#PQen$O2IJ z*Z05;L&hJdyB+RU_#}}ECX&dCf7%znQR(uqx_M8+PmWU44Yr&&Qk&>LQNzjmLry?o zU8>{g!Gq#rx4BlLl4V8SKiAvk3;Nd7c(&!@xZm+U9k@i62O+jao_iD6vx00{~t4?+gNkzEsYesqV+OK3CJw8b}l~}al*&Dpm96%x7 zw^I*0Muu7Gef9@6Bcyk!vdnRvbJCwh%a(WWTay<9U!vDG|MG!afm9FW^epQ_)723~dK)+;#}EdIen~c%>rdWEM1DH| z=bj#nmCTHp6a2mP;HwU2=*sGDL&9)zmtj49zx=H|r(FUB#les8NRpdLuLO6p!O^=X zAMfO7PQc13igH&D8tPm}_;W~p4_+@J(Blu>l30}Y=x$ms#`BU^4aL*nxGNEjhq#Zh zUX*r&_pl~}!ACRZi8EM4H>=!2Sf3p*+w6uO@3VYD9-{aQQ7R3$lW`k2bbk51 zOZ*sb>rY3AUiH3inV0vEu2riT9D;BntS8qd?CP8}sZZpez)Sx;-$s}*YF^@*?I8?^ znfdRQSubNArZj76hIZU?Fry^QNY6FfG4!i5;!pg-dSay(lqut#eW$q#3841Jk7O4P zu%?qsG|_|MO=N;3qzAd&0*(yu{Z@cK+Y_`i1ndoBMjQ<6jxAxJl8)%0dpv55n;06$ zgGZVZBxpYs1Y4atCYk;zv2{$|2QGKvzM=LwTS%FwauVD8qSEA9o#42*y^~*t$G$G>Iza=$zu;2IYnZuI8BNkEX2O3)qQ4{)9PNg+H;o;#s zWpO{!dgGp?_%-@BancF8`V;GPa;T}TG;M)@*!|6Q0tSIemsOKltsR3**&{1c zT{%VBcU%t{;5nCzdd_B_ak3{;O6cf6#=lF_WIy@UoI_)bMD27oN{#3GyTMAtOa|I& z+$`$$U6$0SOpG$w_rr2Wc4iZ%Rq8YIyaP4;Ee#ZIVedWgl zN+lz(<^H-Uvux6IU5z#j z>SUIsBAHU=HR+h_R7EE1&Van=LF%G|BkPX|$34#v$8X~eu$ms2l159xkE3bB3vBLM zE{V-&mlt$t?!@LjRnvI8c5<}jR+hAOG}451q~kRr>t4Nc@YfIpCQx=5lQhDJIUm-s zxqKV)bW3L8*yh#@m9zQJI_)-W#ygK<73f4pN<*3@AIP*;=G(a9vaV)PL2(fBH*q0K zJd{xXne;F*uXd8N#DABKzVIY0fLVBSxKy zw09}Xr@tw{ewt`*W&Q}uu;o96B^20uOWtAke_hnKv_N)hX>yc)BfX0n${-+-l4i5i zv|TZp5|JruoYPm6>0lP2m$bfILHLIU>BflIvXdr$I`4~*V-rdnIN{fK(%B{M->N_< z_SA5n1!oXulq2RMs?8W(KD)DZtoq1~`&NIlw{J8TbtY?A`rA2gTq1M133PBZRA$h$9&Rs_on$@o9b$WsmmMY`MffvLE__nJxpy#c z+Tx>#X26Wd7zx%LVM%*~EUNfYJW0L>R$HGf4%Ld&7WQ`>qEz8jRGp%$tkjbhR}d_M ze_naQhtbV`O;W)qjC)E?DBj=w#X)97}Ro?m&5j2xAj z$pE^PV%S6L3@Ny+$X7NnvXUgEGS;z2((+UKXElT-2eDcE6RKa8Lgll6ny6s^I z6{lo>jE+KAk?xKw;lrbd!GKW9PzDKo28=#k?jW+@P zc+1aymx@dt{}bi=*b|W7gc;w;?$}CIz(|e1;jsH=ixgig?;m*D#yM;ZgZbhm%DNc^c(i zo(T)|zXN43yp%NOW3OJUMm&~a?IO?^KuS%dk^H!({AvCcy|BuDR$$m|Uylr)$%mT>rZR!2YAsV-}YPIhNROzS*odY3&CSx^5yDPvZ&7f{xauV4`3>& zPA396y*o_uJC-iIy?+2NpJBic%CYj6Hy_DXj9(Yf@;thg!93BLDJ_g6{CYBDbS6B3 zh2ofv@|lX{f{eO@#&F@QXS!X>OfFPGxKv5;F*lSn>|S<5I-=z@T&ScZZivUHZCQ+z z(nggqd8&N_{n9Snt_ed0nq)HXceIX6(~x)fN>65`+n3Z}JnE4)`*lo{!Cb$Sl-_iE znHiB63;C5Z{)P6dqlHHO8M$sbcP>1qD9Gi9XN$;kA)gTikLY6^5l>?Xx_loG(c>I=gbDDjTYrzdVMvn0^-y&Q{ zoy=cjf7k#M^wNj3;nmm$&p|~5t;`95^=GaQ%qd*g*>GHbA3Bwk>=y=mD}GlkpK*uU zI=bjk93Rj_<*>%RWEFv=>x(}zWFJiN#?)2n$)`c>qaUAV{h}LfsniyRlx9SpP=g7x z8H`24KQ$4q_sFg3(v^3O3H!Dx#yw!MG=IGV_?H%W?QauRY_q>m&Aqf=%cwSk_m!+_ z+PgK5CZ!a&L?eC>^6_It`EAncf{|@lqj?Ij<=xdHs67$5ha;;vi8Y`1j#3ADyPOe z6oWaW@nnLeErFfSN3+`(a2?b>_ME&ZBMQpU)yUnXA84_V)3|#D4Hx1TVDXGvazUWd zK(>1en^GoO^%kP95ye?c438t*^W)*M--ZqR?YoJ9zo4;^0qJ2*xqTV7gh{YGZxx+9X+K zg)Kj?nsnv;Yj|`}wzBrj(o1t5A z9__qFt`Vmf^EFg1j8(|{G?rgm4t=ZWIW6xWmR2}Wfbs=O_;AEa6WdMF@0$21<_N`O zt#FhnXk$btL5l~yE^*9}7i_|bA?$$wZCqMuj*L;L7B71?LOmKBk7Ph!Yjq`$n9(#I zu=`pgSlA<E-##Xa!0Oc=n=K3YYD+TqtqpHT@gb{CBvLuQr zFzm~27l9Pq>M&fi^_tVejciFR z4M~J``(RaJowVAfYb9}WfUsXd&NJ$ww6yB9_IqY%r-?!3B^|qa>k0u(1+V_1b-iF! z(^_0kKt+&Fq6vPBO(P>5L^__JiFIk}%U~Cl`6Lueek|iQhE%=Fj2{mcqj|M%lXs!P zZ0nn;FQ01kEfdm5MntLJUh8QMBx)ikxmVZ|OeZ}P@xfR%sB^0t{ERFn!5+to_2a#; z8-*r*hS8bPnl{fCING*wA$RYW_gHm)4atka{h7QlQVNdw`PlxU{R7I$x7UUk-*XHk zNttY?eJg)DV0`_TXr;&P8{IJ$?48;c1{)#497IYXitV7L+cI!0TRiN^i>KcHfg>aL zRJ8D8B8RyOwVw{d9c_fw(`MdI;rS4c!St*pa+=YBBnP^lq(^$f%e;f54=2$SY zr0R+{RwebRyQekVpgeEK4eGixpxvMG%+A?%r#>NrP&V=AlU@3GsNEVgB%<<`2iZHbdyNu;MY)5?lGMWIo5+G3Bk|h#YjwqNm}m>^ zmIqsO?N4H2hKh7OCmc$<=z zMc)IStI;lrQP7Ke;ou1|A%2*R71!;Fo9}bbCZF!M@pXzdHg`q~y99r9$jO;h*juJM zc<@~(17|G8=I}sX(udQAsnQ!;^#RA=~pwF!8>ND{}m~6~}Ip(k;Qug7I`m zu9oVO%ZRP?m2@f_$*lY^cI0e~gF~D2UMvJRjw8*a!hC$#SBExvGp%~L-WZK6nU(3T z%1|tegOz=6;)+|q5>J`c?DK;Z7kOT?ky4{v2dNi>-+GNrjz||tUO-c~>qonH`~N&~ zv@HP{_eg;1MhU$%)t7Pu(+|V%RtMHI#22j*Ol$VnLys_wC$ldj;@GuM4tHXy5C_JW z7%y*$4Qb#HnMnkU(^;J489cKX!^^MR>`b95YHp8YH<{T96Q~xHxOBp(;5P7dr$~;A z6rzSM zequ4nIA-y&Em8?O;`_Y#A!J8n2_g~q4G-WNU&k!Ll`SRK-}9{01L%xLwGG-LW{6AC za`o^IU{YJfJdJOnh%^`$y0qXby^XG2`Tv$G$SERwc|llk=dPA+^|kAjI46D*{S|#a zOFCovh2q>esTHehenqeN6MN{Wa^<2wP~TnHxo+31I@`@Hw@${buK8oyxz5bvZ_?}4 z{{n8mlsxo&`lmNq)tq-9fA(CWG3nQ z3Yw0-$#U5O8vW;h4x>F)1Wh9|?1J~*fE)*T_#o?W<9UKBlLSlnARh3D{|37T*sMMk bc_04oTlaC!%=urGK;i4@>gTe~DWM4f*Tc%h literal 0 HcmV?d00001 From 243aa671f12eb34a943c0ee93cb5435d4c5f43ff Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sun, 6 Jul 2025 18:26:05 +0200 Subject: [PATCH 05/60] Update Folder overview.md --- docs/docs/Folder overview.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/docs/Folder overview.md b/docs/docs/Folder overview.md index 0977b28..3b6bf1c 100644 --- a/docs/docs/Folder overview.md +++ b/docs/docs/Folder overview.md @@ -31,7 +31,7 @@ Just create the folder overview as normal in a template and then change the sett ### What's the id in the code block for? They're there to differentiate the overviews from each other so that you can use "Edit folder overview" command to edit the overviews from one file (there can be multiple overviews in one file). It's especially useful on mobile because the button to edit the code block on mobile is quite small. -## How can I make the links appear in the graph view? +### How can I make the links appear in the graph view? [Edit the folder overview](#edit-overview) and then enable "Use actual links" to let the links appear in the graph view. The file will be then edited under the code block and there will be another list added which is hidden by default. You can choose to either hide the code block or the list under it in the folder overview settings. The list of links under the code block will also be visible in other apps that support markdown. ![Screenshot](../assets/screenshots/Obsidian_nAqAIrlZFW.png) @@ -44,14 +44,15 @@ When this is enabled and you rename/create/delete a file/folder which is a child ### Title The title is above the overview when you enable "Show the title". You can use variables like this: {{variableName}} which will be replaced with something and the list of variables you can use is below this text. -| Variable | Explanation | Example | -| ------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -| folderName | The name of the folder depending on what folder you choose to display | Folder1 | -| folderPath | The path of the choosen folder | Folder1/Folder2 | -| fileName | The name of the file where the overview is located in | File1 | -| filePath | The path of the file where the overview is located in | Folder1/File1 | -| fmtpFileName | The changed file name using the [front matter title plugin](https://github.com/snezhig/obsidian-front-matter-title) | Real file name: 1234, changed name: File1 | -| properties.\ | Choose any property from a file | properties.name => File1
    properties.date => e.g. 01.01.2001 | +| Variable | Explanation | Example | +| ------------------ | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| folderName | The name of the folder depending on what folder you choose to display | Folder1 | +| folderPath | The path of the choosen folder | Folder1/Folder2 | +| fileName | The name of the file where the overview is located in | File1 | +| filePath | The path of the file where the overview is located in | Folder1/File1 | +| fmtpFileName | The changed file name using the [front matter title plugin](https://github.com/snezhig/obsidian-front-matter-title) | Real file name: 1234, changed name: File1 | +| properties. | Choose any property from a file | properties.name => File1
    properties.date => e.g. 01.01.2001 | + ### Folder path The overview will show the children of the selected folder. From ae1f67c84b08e4b149899dd47b78c81a90468857 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sun, 6 Jul 2025 18:35:58 +0200 Subject: [PATCH 06/60] Support frontmatter title plugin for folder overview --- src/events/FrontMatterTitle.ts | 57 ++++++++++++++++++-------- src/obsidian-folder-overview | 2 +- src/settings/FolderOverviewSettings.ts | 9 +++- src/settings/GeneralSettings.ts | 2 +- src/settings/SettingsTab.ts | 1 + 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/events/FrontMatterTitle.ts b/src/events/FrontMatterTitle.ts index 966837f..224faf6 100644 --- a/src/events/FrontMatterTitle.ts +++ b/src/events/FrontMatterTitle.ts @@ -26,25 +26,27 @@ export class FrontMatterTitlePluginHandler { await this.deffer.awaitFeatures(); } } - const dispatcher = this.api?.getEventDispatcher(); - if (dispatcher) { - this.dispatcher = dispatcher; - } - const event: Listener = { - name: 'manager:update', - cb: (data) => { - this.fmptUpdateFileName(data as any, true); - }, - }; - // Keep ref to remove listener - const ref = dispatcher?.addListener(event); - if (ref) { - this.eventRef = ref; + if (plugin.settings.frontMatterTitle.enabled) { + const dispatcher = this.api?.getEventDispatcher(); + if (dispatcher) { + this.dispatcher = dispatcher; + } + const event: Listener = { + name: 'manager:update', + cb: (data) => { + this.fmptUpdateFileName(data as any, true); + }, + }; + // Keep ref to remove listener + const ref = dispatcher?.addListener(event); + if (ref) { + this.eventRef = ref; + } + // this.plugin.app.vault.getFiles().forEach((file) => { + // this.handleRename({ id: '', result: false, path: file.path }, false); + // }); + this.plugin.updateAllBreadcrumbs(); } - // this.plugin.app.vault.getFiles().forEach((file) => { - // this.handleRename({ id: '', result: false, path: file.path }, false); - // }); - this.plugin.updateAllBreadcrumbs(); })(); } deleteEvent() { @@ -123,4 +125,23 @@ export class FrontMatterTitlePluginHandler { folder.newName = newName; this.modifiedFolders.set(folder.path, folder); } + + async getNewFolderName(folder: TFolder): Promise { + if (this.modifiedFolders.has(folder.path)) { + const modifiedFolder = this.modifiedFolders.get(folder.path); + if (modifiedFolder) { + return modifiedFolder.newName; + } + } + const folderNote = getFolderNote(this.plugin, folder.path); + if (!folderNote) return null; + const resolver = this.api?.getResolverFactory()?.createResolver('#feature-id#'); + return resolver?.resolve(folderNote?.path ?? '') ?? null; + } + + async getNewFileName(file: TFile): Promise { + const resolver = this.api?.getResolverFactory()?.createResolver('#feature-id#'); + const changedName = resolver?.resolve(file?.path ?? ''); + return changedName ?? null; + } } diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 4eb8381..49aa6fc 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 4eb8381ae1df5a95b94159263a6be31284d40ade +Subproject commit 49aa6fcb8d065a5b22e8792f92f6faa968080c03 diff --git a/src/settings/FolderOverviewSettings.ts b/src/settings/FolderOverviewSettings.ts index 937e716..69f1985 100644 --- a/src/settings/FolderOverviewSettings.ts +++ b/src/settings/FolderOverviewSettings.ts @@ -5,7 +5,14 @@ export async function renderFolderOverview(settingsTab: SettingsTab) { const { plugin } = settingsTab; const overviewSettings = plugin.settings.defaultOverview; const containerEl = settingsTab.settingsPage; - containerEl.createEl('p', { text: 'Edit the default settings for folder overviews', cls: 'setting-item-description' }); + const pEl = containerEl.createEl('p', { + text: 'Edit the default settings for new folder overviews, ', + cls: 'setting-item-description', + }); + const span = createSpan({ text: "this won't apply to already existing overviews.", cls: '' }); + const accentColor = (settingsTab.app.vault.getConfig('accentColor') as string) || '#7d5bed'; + span.setAttr('style', `color: ${accentColor};`); + pEl.appendChild(span); createOverviewSettings(containerEl, overviewSettings, plugin, plugin.settings.defaultOverview, settingsTab.display, undefined, undefined, undefined, settingsTab); } diff --git a/src/settings/GeneralSettings.ts b/src/settings/GeneralSettings.ts index ecea455..438860e 100644 --- a/src/settings/GeneralSettings.ts +++ b/src/settings/GeneralSettings.ts @@ -470,7 +470,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.fmtpHandler?.fmptUpdateFileName({ id: '', result: false, path: file.path, pathOnly: false }, false); }); settingsTab.plugin.fmtpHandler?.deleteEvent(); - settingsTab.plugin.fmtpHandler = null; + settingsTab.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(settingsTab.plugin); } settingsTab.display(); }) diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index ec93c2a..b1539af 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -128,6 +128,7 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = { hideLinkList: true, hideFolderOverview: false, useActualLinks: false, + fmtpIntegration: false, }, useSubmenus: true, syncMove: true, From f3f835844da71cd9a643805149c92abf71383c09 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 7 Jul 2025 11:08:27 +0200 Subject: [PATCH 07/60] Unnecessary click listener --- src/main.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main.ts b/src/main.ts index 9203c20..7d06bd5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -137,19 +137,6 @@ export default class FolderNotesPlugin extends Plugin { this.registerMarkdownCodeBlockProcessor('folder-overview', (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => { this.handleOverviewBlock(source, el, ctx); }); - - this.registerEvent( - this.app.workspace.on('layout-change', () => { - const fileExplorerLeaf = this.app.workspace.getLeavesOfType('file-explorer')[0]; - if (!fileExplorerLeaf) return; - const container = fileExplorerLeaf.view.containerEl; - if (container) { - this.registerDomEvent(container, 'click', (evt: MouseEvent) => { - this.handleFileExplorerClick(evt); - }, true); - } - }) - ); } onLayoutReady() { From c039b942e014121aef842f80a3a89443b1ca65b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 7 Jul 2025 09:12:10 +0000 Subject: [PATCH 08/60] Update manifest.json to version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index c4f709d..bebfa29 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes", - "version": "1.8.13", + "version": "1.8.14", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From 26ea44a0b35feaa4cd2ac40380ddc29040011853 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 7 Jul 2025 11:17:54 +0200 Subject: [PATCH 09/60] Update obsidian-folder-overview --- src/obsidian-folder-overview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 49aa6fc..2b6539f 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 49aa6fcb8d065a5b22e8792f92f6faa968080c03 +Subproject commit 2b6539f1e7c4a71b5b766fd18ea7061eef5d12ba From 0b6ecc730d434ec3dfd9a15047f9db6d61752eaf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 7 Jul 2025 09:18:47 +0000 Subject: [PATCH 10/60] Update manifest.json to version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index bebfa29..b84cbdd 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes", - "version": "1.8.14", + "version": "1.8.15", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From ce9fcb72c11dcedbf72c7af5b21a401e8308dfec Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 7 Jul 2025 11:40:16 +0200 Subject: [PATCH 11/60] Fix #253 --- src/main.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.ts b/src/main.ts index 7d06bd5..d1cf6e0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -154,6 +154,11 @@ export default class FolderNotesPlugin extends Plugin { this.handleFileExplorerClick(evt); }, true); + // Handle middle mouse button clicks + this.registerDomEvent(document, 'auxclick', (evt: MouseEvent) => { + this.handleFileExplorerClick(evt); + }, true); + const fileExplorerPlugin = this.app.internalPlugins.getEnabledPluginById('file-explorer'); if (fileExplorerPlugin) { const originalRevealInFolder = fileExplorerPlugin.revealInFolder.bind(fileExplorerPlugin); From ecc8dc266a74dbd733472e6351e5a4ce64f72536 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Tue, 15 Jul 2025 13:14:56 +0200 Subject: [PATCH 12/60] Fix #259 --- src/template.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/template.ts b/src/template.ts index a13a51a..7d6914e 100644 --- a/src/template.ts +++ b/src/template.ts @@ -7,6 +7,11 @@ export async function applyTemplate( leaf?: WorkspaceLeaf | null, templatePath?: string ) { + const fileContent = await plugin.app.vault.read(file).catch((err) => { + console.error(`Error reading file ${file.path}:`, err); + }); + if (fileContent !== '') return; + const templateFile = templatePath ? plugin.app.vault.getAbstractFileByPath(templatePath) : null; From f024b44f21755635fbda7a008a8b91116ef8eeeb Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Tue, 15 Jul 2025 13:36:17 +0200 Subject: [PATCH 13/60] Add notice about templater plugin --- src/suggesters/TemplateSuggester.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/suggesters/TemplateSuggester.ts b/src/suggesters/TemplateSuggester.ts index 1988641..7290f62 100644 --- a/src/suggesters/TemplateSuggester.ts +++ b/src/suggesters/TemplateSuggester.ts @@ -2,14 +2,14 @@ import { TAbstractFile, TFile, TFolder, Vault, AbstractInputSuggest } from 'obsi import FolderNotesPlugin from '../main'; import { getTemplatePlugins } from 'src/template'; export enum FileSuggestMode { - TemplateFiles, - ScriptFiles, + TemplateFiles, + ScriptFiles, } export class TemplateSuggest extends AbstractInputSuggest { constructor( - public inputEl: HTMLInputElement, - public plugin: FolderNotesPlugin + public inputEl: HTMLInputElement, + public plugin: FolderNotesPlugin ) { super(plugin.app, inputEl); } @@ -35,15 +35,22 @@ export class TemplateSuggest extends AbstractInputSuggest { file.path.toLowerCase().includes(lower_input_str) ); } else { - let folder: TFolder; + let folder: TFolder | TAbstractFile | null = null; if (templaterPlugin) { folder = this.plugin.app.vault.getAbstractFileByPath( templaterPlugin.plugin?.settings?.templates_folder as string - ) as TFolder; + ); + if (!(folder instanceof TFolder)) { + return [{ path: '', name: 'You need to set the Templates folder in the Templater settings first.' } as TFile]; + } } else { folder = this.plugin.app.vault.getAbstractFileByPath(templateFolder) as TFolder; } + if (!(folder instanceof TFolder)) { + return []; + } + Vault.recurseChildren(folder, (file: TAbstractFile) => { if (file instanceof TFile && file.path.toLowerCase().includes(lower_input_str)) { files.push(file); From ba442d20ce108b2f9cd0de28a6f7b7e1336f8105 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 15 Jul 2025 11:57:22 +0000 Subject: [PATCH 14/60] Update manifest.json to version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index b84cbdd..83a74ca 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes", - "version": "1.8.15", + "version": "1.8.16", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From 9a1e5773a04bdd7e202367c7c42a2f9c41818399 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Fri, 18 Jul 2025 18:01:26 +0200 Subject: [PATCH 15/60] Update obsidian-folder-overview --- src/obsidian-folder-overview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 2b6539f..e54c4a2 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 2b6539f1e7c4a71b5b766fd18ea7061eef5d12ba +Subproject commit e54c4a2a27a606416cffbe3aef1a75cd779b4f43 From e46191d13773f645506484d5df8d8c67b46e4dbf Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Fri, 18 Jul 2025 18:10:48 +0200 Subject: [PATCH 16/60] Update link list in the background --- src/main.ts | 14 +++++++++++++- src/settings/FolderOverviewSettings.ts | 4 ++-- src/settings/SettingsTab.ts | 12 +++++++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/main.ts b/src/main.ts index d1cf6e0..db44354 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,5 @@ -import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml, Notice, Keymap, WorkspaceLeaf, requireApiVersion, Platform } from 'obsidian'; +import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml, Notice, Keymap, WorkspaceLeaf, requireApiVersion, Platform, debounce } from 'obsidian'; import { DEFAULT_SETTINGS, FolderNotesSettings, SettingsTab } from './settings/SettingsTab'; import { Commands } from './Commands'; import { FileExplorerWorkspaceLeaf } from './globals'; @@ -20,6 +20,8 @@ import { FileExplorerView, InternalPlugin } from 'obsidian-typings'; import { FOLDER_OVERVIEW_VIEW, FolderOverviewView } from './obsidian-folder-overview/src/view'; import { registerOverviewCommands } from './obsidian-folder-overview/src/Commands'; import { updateOverviewView, updateViewDropdown } from './obsidian-folder-overview/src/main'; +import { FvIndexDB } from './obsidian-folder-overview/src/utils/IndexDB'; +import { updateAllOverviews } from './obsidian-folder-overview/src/utils/functions'; export default class FolderNotesPlugin extends Plugin { observer: MutationObserver; @@ -34,6 +36,7 @@ export default class FolderNotesPlugin extends Plugin { tabManager: TabManager; settingsOpened = false; askModalCurrentlyOpen = false; + fvIndexDB: FvIndexDB; private fileExplorerPlugin!: InternalPlugin; private fileExplorerView!: FileExplorerView; @@ -44,6 +47,7 @@ export default class FolderNotesPlugin extends Plugin { this.settingsTab = new SettingsTab(this.app, this); this.addSettingTab(this.settingsTab); this.saveSettings(); + this.fvIndexDB = new FvIndexDB(this); // Add CSS Classes document.body.classList.add('folder-notes-plugin'); @@ -128,6 +132,7 @@ export default class FolderNotesPlugin extends Plugin { this.registerEvent(this.app.vault.on('rename', (file: TAbstractFile, oldPath: string) => { handleRename(file, oldPath, this); + })); this.registerEvent(this.app.vault.on('delete', (file: TAbstractFile) => { @@ -230,6 +235,13 @@ export default class FolderNotesPlugin extends Plugin { }; } + handleVaultChange() { + if (!this.settings.fvGlobalSettings.autoUpdateLinks) return; + debounce(() => { + updateAllOverviews(this); + }, 2000, true)(); + } + handleFileExplorerClick(evt: MouseEvent) { const target = evt.target as HTMLElement; if (evt.shiftKey) return; diff --git a/src/settings/FolderOverviewSettings.ts b/src/settings/FolderOverviewSettings.ts index 69f1985..52e63d5 100644 --- a/src/settings/FolderOverviewSettings.ts +++ b/src/settings/FolderOverviewSettings.ts @@ -3,7 +3,7 @@ import { createOverviewSettings } from 'src/obsidian-folder-overview/src/setting export async function renderFolderOverview(settingsTab: SettingsTab) { const { plugin } = settingsTab; - const overviewSettings = plugin.settings.defaultOverview; + const defaultOverviewSettings = plugin.settings.defaultOverview; const containerEl = settingsTab.settingsPage; const pEl = containerEl.createEl('p', { text: 'Edit the default settings for new folder overviews, ', @@ -14,5 +14,5 @@ export async function renderFolderOverview(settingsTab: SettingsTab) { span.setAttr('style', `color: ${accentColor};`); pEl.appendChild(span); - createOverviewSettings(containerEl, overviewSettings, plugin, plugin.settings.defaultOverview, settingsTab.display, undefined, undefined, undefined, settingsTab); + createOverviewSettings(containerEl, defaultOverviewSettings, plugin, plugin.settings.defaultOverview, settingsTab.display, undefined, undefined, undefined, settingsTab); } diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index b1539af..881b537 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -3,7 +3,7 @@ import FolderNotesPlugin from '../main'; import { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; import { extractFolderName, getFolderNote } from '../functions/folderNoteFunctions'; -import { overviewSettings } from '../obsidian-folder-overview/src/FolderOverview'; +import { defaultOverviewSettings } from '../obsidian-folder-overview/src/FolderOverview'; import { renderGeneral } from './GeneralSettings'; import { renderFileExplorer } from './FileExplorerSettings'; import { renderPath } from './PathSettings'; @@ -41,7 +41,7 @@ export interface FolderNotesSettings { disableFolderHighlighting: boolean; storageLocation: 'insideFolder' | 'parentFolder' | 'vaultFolder'; syncDelete: boolean; - defaultOverview: overviewSettings; + defaultOverview: defaultOverviewSettings; useSubmenus: boolean; syncMove: boolean; frontMatterTitle: { @@ -76,6 +76,9 @@ export interface FolderNotesSettings { afterChangingTab: boolean; }, firstTimeInsertOverview: boolean; + fvGlobalSettings: { + autoUpdateLinks: boolean; + } } export const DEFAULT_SETTINGS: FolderNotesSettings = { @@ -194,6 +197,9 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = { afterChangingTab: true, }, firstTimeInsertOverview: true, + fvGlobalSettings: { + autoUpdateLinks: false, + }, }; export class SettingsTab extends PluginSettingTab { @@ -248,7 +254,7 @@ export class SettingsTab extends PluginSettingTab { } } - display(contentEl?: HTMLElement, yaml?: overviewSettings, plugin?: FolderNotesPlugin, defaultSettings?: boolean, display?: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: this) { + display(contentEl?: HTMLElement, yaml?: defaultOverviewSettings, plugin?: FolderNotesPlugin, defaultSettings?: boolean, display?: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: this) { plugin = this?.plugin ?? plugin; if (plugin) { plugin.settingsOpened = true; From cc2b15d8dbb75cdd61bb70bf0a936de629c4d6e1 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 19 Jul 2025 09:18:14 +0200 Subject: [PATCH 17/60] Fix folder highlight --- src/events/handleClick.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/handleClick.ts b/src/events/handleClick.ts index d5106e7..4bb35bd 100644 --- a/src/events/handleClick.ts +++ b/src/events/handleClick.ts @@ -5,11 +5,11 @@ import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions' import { addCSSClassToFileExplorerEl, removeCSSClassFromFileExplorerEL } from 'src/functions/styleFunctions'; export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNotesPlugin) { + if (!plugin.settings.openFolderNoteOnClickInPath) return; event.stopImmediatePropagation(); event.preventDefault(); event.stopPropagation(); if (!(event.target instanceof HTMLElement)) return; - if (!plugin.settings.openFolderNoteOnClickInPath) return; const folderPath = event.target.getAttribute('data-path'); if (!folderPath) { return; } From ffaa74faaa11be4a3facd1fcb43b5b512a40b102 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 19 Jul 2025 09:33:21 +0200 Subject: [PATCH 18/60] Update PathSettings.ts --- src/settings/PathSettings.ts | 46 +++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/settings/PathSettings.ts b/src/settings/PathSettings.ts index 7bf3e14..eb8499f 100644 --- a/src/settings/PathSettings.ts +++ b/src/settings/PathSettings.ts @@ -15,29 +15,31 @@ export async function renderPath(settingsTab: SettingsTab) { }) ); - new Setting(containerEl) - .setName('Open sidebar when opening a folder note through path (Mobile only)') - .setDesc('Open the sidebar when opening a folder note through the path on mobile') - .addToggle((toggle) => - toggle - .setValue(settingsTab.plugin.settings.openSidebar.mobile) - .onChange(async (value) => { - settingsTab.plugin.settings.openSidebar.mobile = value; - await settingsTab.plugin.saveSettings(); - }) - ); + if (settingsTab.plugin.settings.openFolderNoteOnClickInPath) { + new Setting(containerEl) + .setName('Open sidebar when opening a folder note through path (Mobile only)') + .setDesc('Open the sidebar when opening a folder note through the path on mobile') + .addToggle((toggle) => + toggle + .setValue(settingsTab.plugin.settings.openSidebar.mobile) + .onChange(async (value) => { + settingsTab.plugin.settings.openSidebar.mobile = value; + await settingsTab.plugin.saveSettings(); + }) + ); - new Setting(containerEl) - .setName('Open sidebar when opening a folder note through path (Desktop only)') - .setDesc('Open the sidebar when opening a folder note through the path on desktop') - .addToggle((toggle) => - toggle - .setValue(settingsTab.plugin.settings.openSidebar.desktop) - .onChange(async (value) => { - settingsTab.plugin.settings.openSidebar.desktop = value; - await settingsTab.plugin.saveSettings(); - }) - ); + new Setting(containerEl) + .setName('Open sidebar when opening a folder note through path (Desktop only)') + .setDesc('Open the sidebar when opening a folder note through the path on desktop') + .addToggle((toggle) => + toggle + .setValue(settingsTab.plugin.settings.openSidebar.desktop) + .onChange(async (value) => { + settingsTab.plugin.settings.openSidebar.desktop = value; + await settingsTab.plugin.saveSettings(); + }) + ); + } if (settingsTab.plugin.settings.frontMatterTitle.enabled) { new Setting(containerEl) From ccc3b24da767c3b27644820f2d7f570b9b354a81 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 19 Jul 2025 09:39:59 +0200 Subject: [PATCH 19/60] Update link list in the background --- src/main.ts | 16 +++++++++++----- src/settings/FolderOverviewSettings.ts | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/main.ts b/src/main.ts index db44354..24cd8c5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -111,10 +111,6 @@ export default class FolderNotesPlugin extends Plugin { this.hoverLinkTriggered = true; }); - this.registerEvent(this.app.vault.on('create', (file: TAbstractFile) => { - handleCreate(file, this); - })); - this.registerEvent(this.app.workspace.on('file-open', async (openFile: TFile | null) => { removeActiveFolder(this); @@ -130,13 +126,19 @@ export default class FolderNotesPlugin extends Plugin { setActiveFolder(folder.path, this); })); + this.registerEvent(this.app.vault.on('create', (file: TAbstractFile) => { + handleCreate(file, this); + this.handleVaultChange(); + })); + this.registerEvent(this.app.vault.on('rename', (file: TAbstractFile, oldPath: string) => { handleRename(file, oldPath, this); - + this.handleVaultChange(); })); this.registerEvent(this.app.vault.on('delete', (file: TAbstractFile) => { handleDelete(file, this); + this.handleVaultChange(); })); this.registerMarkdownCodeBlockProcessor('folder-overview', (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => { @@ -233,6 +235,10 @@ export default class FolderNotesPlugin extends Plugin { return originalHandleDrop.call(this, evt, ...args); }; + + if (this.settings.fvGlobalSettings.autoUpdateLinks) { + this.fvIndexDB.init(true); + } } handleVaultChange() { diff --git a/src/settings/FolderOverviewSettings.ts b/src/settings/FolderOverviewSettings.ts index 52e63d5..b06c6d4 100644 --- a/src/settings/FolderOverviewSettings.ts +++ b/src/settings/FolderOverviewSettings.ts @@ -1,3 +1,4 @@ +import { Setting } from 'obsidian'; import { SettingsTab } from './SettingsTab'; import { createOverviewSettings } from 'src/obsidian-folder-overview/src/settings'; @@ -5,6 +6,26 @@ export async function renderFolderOverview(settingsTab: SettingsTab) { const { plugin } = settingsTab; const defaultOverviewSettings = plugin.settings.defaultOverview; const containerEl = settingsTab.settingsPage; + + containerEl.createEl('h3', { text: 'Global settings' }); + new Setting(containerEl) + .setName('Auto-update links without opening the overview') + .setDesc('If enabled, the links that appear in the graph view will be updated even when you don\'t have the overview open somewhere.') + .addToggle((toggle) => + toggle + .setValue(plugin.settings.fvGlobalSettings.autoUpdateLinks) + .onChange(async (value) => { + plugin.settings.fvGlobalSettings.autoUpdateLinks = value; + await plugin.saveSettings(); + if (value) { + plugin.fvIndexDB.init(true); + } else { + plugin.fvIndexDB.active = false; + } + }) + ); + + containerEl.createEl('h3', { text: 'Overviews default settings' }); const pEl = containerEl.createEl('p', { text: 'Edit the default settings for new folder overviews, ', cls: 'setting-item-description', From b165b46aa7947285db5ff696197ede2228240cfa Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 19 Jul 2025 09:59:05 +0200 Subject: [PATCH 20/60] Supported file types add .base --- src/settings/GeneralSettings.ts | 3 ++- src/settings/SettingsTab.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/settings/GeneralSettings.ts b/src/settings/GeneralSettings.ts index 438860e..a588e84 100644 --- a/src/settings/GeneralSettings.ts +++ b/src/settings/GeneralSettings.ts @@ -130,7 +130,8 @@ export async function renderGeneral(settingsTab: SettingsTab) { const options = [ { value: 'md', label: 'Markdown' }, { value: 'canvas', label: 'Canvas' }, - { value: 'excalidraw', label: 'excalidraw' }, + { value: 'base', label: 'Bases' }, + { value: 'excalidraw', label: 'Excalidraw' }, { value: 'custom', label: 'Custom extension' }, ]; diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index 881b537..8c4ae84 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -141,7 +141,7 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = { path: true, }, settingsTab: 'general', - supportedFileTypes: ['md', 'canvas'], + supportedFileTypes: ['md', 'canvas', 'base'], boldName: false, boldNameInPath: false, cursiveName: false, From 4f018a915821f1d5f813095295e89865daf6f0fa Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 19 Jul 2025 11:15:39 +0200 Subject: [PATCH 21/60] Customize folder overview title size --- src/obsidian-folder-overview | 2 +- src/settings/SettingsTab.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index e54c4a2..77a54f4 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit e54c4a2a27a606416cffbe3aef1a75cd779b4f43 +Subproject commit 77a54f4146f6672c2818761b98203cc24881b567 diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index 8c4ae84..cd1077e 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -132,6 +132,7 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = { hideFolderOverview: false, useActualLinks: false, fmtpIntegration: false, + titleSize: 1, }, useSubmenus: true, syncMove: true, From f73515245cb22261077c691b7bcdcc646e8366fb Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 19 Jul 2025 11:19:18 +0200 Subject: [PATCH 22/60] Update Commands.ts --- src/Commands.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Commands.ts b/src/Commands.ts index 2155075..8e304e7 100644 --- a/src/Commands.ts +++ b/src/Commands.ts @@ -320,8 +320,6 @@ export class Commands { // item.setTitle('Manage excluded folder') // .setIcon('settings-2') // .onClick(() => { - // console.log('excludedFolder', excludedFolder) - // console.log('2', getExcludedFolder(this.plugin, file.path, false)) // if (excludedFolder instanceof ExcludedFolder) { // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open(); // } else if (excludedFolder instanceof ExcludePattern) { From 2fdda5c1999df37588856d6e12a41ba728f6f595 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 19 Jul 2025 16:01:38 +0200 Subject: [PATCH 23/60] Update obsidian-folder-overview --- src/obsidian-folder-overview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 77a54f4..6189a6a 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 77a54f4146f6672c2818761b98203cc24881b567 +Subproject commit 6189a6abf7fe63b4f3aae32f673c1f644a8493ad From 2dad83ee7f175fe23807a2d35e67f821e857d06f Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 21 Jul 2025 09:15:06 +0200 Subject: [PATCH 24/60] Remove notices --- src/main.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 24cd8c5..8255ddc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -151,6 +151,7 @@ export default class FolderNotesPlugin extends Plugin { this.registerView(FOLDER_OVERVIEW_VIEW, (leaf: WorkspaceLeaf) => { return new FolderOverviewView(leaf, this); }); + if (this.app.plugins.getPlugin('obsidian-front-matter-title-plugin')) { this.fmtpHandler = new FrontMatterTitlePluginHandler(this); } @@ -237,7 +238,7 @@ export default class FolderNotesPlugin extends Plugin { }; if (this.settings.fvGlobalSettings.autoUpdateLinks) { - this.fvIndexDB.init(true); + this.fvIndexDB.init(false); } } From c87a8b67d9b988b8ef51e23eaede6d2871727806 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 21 Jul 2025 09:15:22 +0200 Subject: [PATCH 25/60] Update obsidian-folder-overview --- src/obsidian-folder-overview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 6189a6a..cb1e825 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 6189a6abf7fe63b4f3aae32f673c1f644a8493ad +Subproject commit cb1e825c96513b18011f424fc4c60188d0d08ba5 From 7f59b13788084cac36f1390a1ce003f710e1f293 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:34:20 +0200 Subject: [PATCH 26/60] Fix #267 --- src/main.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.ts b/src/main.ts index 8255ddc..440dea0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -147,6 +147,10 @@ export default class FolderNotesPlugin extends Plugin { } onLayoutReady() { + if (!this._loaded) { + return; + } + registerFileExplorerObserver(this); this.registerView(FOLDER_OVERVIEW_VIEW, (leaf: WorkspaceLeaf) => { return new FolderOverviewView(leaf, this); From a66847a38ed48a83aaaa289928073392b41e36b6 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:10:02 +0200 Subject: [PATCH 27/60] Update obsidian-folder-overview --- src/obsidian-folder-overview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index cb1e825..503ed5d 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit cb1e825c96513b18011f424fc4c60188d0d08ba5 +Subproject commit 503ed5db9d9ab0456fcae330e30a62cd923285d3 From 726476a42c18b86630d22f2a760499737dc05acd Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:27:36 +0200 Subject: [PATCH 28/60] Fix #268 --- src/events/handleClick.ts | 3 ++- src/main.ts | 8 +++++--- src/settings/GeneralSettings.ts | 6 ++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/events/handleClick.ts b/src/events/handleClick.ts index 4bb35bd..4b3035a 100644 --- a/src/events/handleClick.ts +++ b/src/events/handleClick.ts @@ -36,7 +36,8 @@ export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNot }); return; } else if (event.altKey || Keymap.isModEvent(event) === 'tab') { - if ((plugin.settings.altKey && event.altKey) || (plugin.settings.ctrlKey && Keymap.isModEvent(event) === 'tab')) { + const usedCtrl = Platform.isMacOS ? event.metaKey : event.ctrlKey; + if ((plugin.settings.altKey && event.altKey) || (usedCtrl && Keymap.isModEvent(event) === 'tab')) { await createFolderNote(plugin, folderPath, true, undefined, true); addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, plugin); removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, plugin); diff --git a/src/main.ts b/src/main.ts index 8255ddc..e2935cd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -278,9 +278,11 @@ export default class FolderNotesPlugin extends Plugin { const excludedFolder = getExcludedFolder(this, folderPath, true); if (excludedFolder?.disableFolderNote) return; + const usedCtrl = Platform.isMacOS ? evt.metaKey : evt.ctrlKey; + const folderNote = getFolderNote(this, folderPath); if (!folderNote && (evt.altKey || Keymap.isModEvent(evt) === 'tab')) { - if ((this.settings.altKey && evt.altKey) || (this.settings.ctrlKey && Keymap.isModEvent(evt) === 'tab')) { + if ((this.settings.altKey && evt.altKey) || (usedCtrl && this.settings.ctrlKey)) { createFolderNote(this, folderPath, true, undefined, true); addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, this); removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, this); @@ -289,10 +291,10 @@ export default class FolderNotesPlugin extends Plugin { } if (!(folderNote instanceof TFile)) return; - if (this.settings.openWithCtrl && !evt.ctrlKey) return; + if (this.settings.openWithCtrl && !usedCtrl) return; if (this.settings.openWithAlt && !evt.altKey) return; - if (!this.settings.enableCollapsing || evt.ctrlKey) { + if (!this.settings.enableCollapsing || usedCtrl) { evt.preventDefault(); evt.stopImmediatePropagation(); } diff --git a/src/settings/GeneralSettings.ts b/src/settings/GeneralSettings.ts index a588e84..495361e 100644 --- a/src/settings/GeneralSettings.ts +++ b/src/settings/GeneralSettings.ts @@ -254,10 +254,11 @@ export async function renderGeneral(settingsTab: SettingsTab) { .addDropdown((dropdown) => { if (!Platform.isMacOS) { dropdown.addOption('ctrl', 'Ctrl + Click'); + dropdown.addOption('alt', 'Alt + Click'); } else { dropdown.addOption('ctrl', 'Cmd + Click'); + dropdown.addOption('alt', 'Option + Click'); } - dropdown.addOption('alt', 'Alt + Click'); dropdown.setValue(settingsTab.plugin.settings.ctrlKey ? 'ctrl' : 'alt'); dropdown.onChange(async (value) => { settingsTab.plugin.settings.ctrlKey = value === 'ctrl'; @@ -274,10 +275,11 @@ export async function renderGeneral(settingsTab: SettingsTab) { dropdown.addOption('click', 'Mouse Click'); if (!Platform.isMacOS) { dropdown.addOption('ctrl', 'Ctrl + Click'); + dropdown.addOption('alt', 'Alt + Click'); } else { dropdown.addOption('ctrl', 'Cmd + Click'); + dropdown.addOption('alt', 'Option + Click'); } - dropdown.addOption('alt', 'Alt + Click'); if (settingsTab.plugin.settings.openByClick) { dropdown.setValue('click'); } else if (settingsTab.plugin.settings.openWithCtrl) { From eedbd8fca9916d769847baec4036f05a191298cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 22 Jul 2025 08:40:37 +0000 Subject: [PATCH 29/60] Update manifest-beta.json to version 1.8.17-0-beta in main branch --- manifest-beta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest-beta.json b/manifest-beta.json index 95ff939..4cb7301 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes beta", - "version": "1.8.5-3-beta", + "version": "1.8.17-0-beta", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From 247c252ef88f747b07e7ab286b615e5cbffed5f9 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 26 Jul 2025 10:11:55 +0200 Subject: [PATCH 30/60] Support editing folder overview in callout --- src/obsidian-folder-overview | 2 +- styles.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 503ed5d..36548fd 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 503ed5db9d9ab0456fcae330e30a62cd923285d3 +Subproject commit 36548fdaf0b63e66a7c4c891347b788d38cf7f72 diff --git a/styles.css b/styles.css index 83abedf..1bfa9ea 100644 --- a/styles.css +++ b/styles.css @@ -120,6 +120,7 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { } .cm-line:has(.fv-link-list-item), +li:has(.fv-link-list-item), .el-ul:has(.fv-link-list-item), .cm-line:has(.fv-link-list-start), .cm-line:has(.fv-link-list-end), From e87aec28c14d1565c6cf594b03a798858ec3578a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 26 Jul 2025 08:12:20 +0000 Subject: [PATCH 31/60] Update manifest-beta.json to version 1.8.17-1-beta in main branch --- manifest-beta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest-beta.json b/manifest-beta.json index 4cb7301..60be061 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes beta", - "version": "1.8.17-0-beta", + "version": "1.8.17-1-beta", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From 86288080c893c4c8728bb9688ec0dba15674673f Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 26 Jul 2025 10:16:19 +0200 Subject: [PATCH 32/60] Update SettingsTab.ts --- src/settings/SettingsTab.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index cd1077e..15d62ab 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -133,6 +133,7 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = { useActualLinks: false, fmtpIntegration: false, titleSize: 1, + isInCallout: false, }, useSubmenus: true, syncMove: true, From 2504d69c3188bbae3b8491aa5971a61211994d65 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 26 Jul 2025 10:16:21 +0200 Subject: [PATCH 33/60] Update obsidian-folder-overview --- src/obsidian-folder-overview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 36548fd..f1d2495 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 36548fdaf0b63e66a7c4c891347b788d38cf7f72 +Subproject commit f1d24952999f23e2912c7e6c2da4bd53325d2402 From b0955915fbd99cc7c2d524ebb1cfb15a7da5d03d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 26 Jul 2025 08:18:48 +0000 Subject: [PATCH 34/60] Update manifest-beta.json to version 1.8.17-2-beta in main branch --- manifest-beta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest-beta.json b/manifest-beta.json index 60be061..c97ba08 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes beta", - "version": "1.8.17-1-beta", + "version": "1.8.17-2-beta", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From d98958c7bd8651ade286975846e4d57a32934c31 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sun, 27 Jul 2025 11:33:21 +0200 Subject: [PATCH 35/60] Eslint code style fixes no. 1 --- .eslintignore | 7 - .eslintrc | 150 -- eslint.config.mts | 158 ++ package-lock.json | 2173 ++++++++++------- package.json | 7 +- src/Commands.ts | 23 +- src/ExcludeFolders/ExcludeFolder.ts | 2 +- src/ExcludeFolders/ExcludePattern.ts | 2 +- src/ExcludeFolders/WhitelistFolder.ts | 2 +- src/ExcludeFolders/WhitelistPattern.ts | 2 +- .../functions/folderFunctions.ts | 22 +- .../functions/patternFunctions.ts | 8 +- .../functions/whitelistFolderFunctions.ts | 10 +- .../functions/whitelistPatternFunctions.ts | 8 +- .../modals/ExcludeFolderSettings.ts | 21 +- src/ExcludeFolders/modals/PatternSettings.ts | 19 +- .../modals/WhitelistFolderSettings.ts | 21 +- .../modals/WhitelistPatternSettings.ts | 17 +- .../modals/WhitelistedFoldersSettings.ts | 7 +- src/events/FrontMatterTitle.ts | 12 +- src/events/MutationObserver.ts | 4 +- src/events/TabManager.ts | 5 +- src/events/handleClick.ts | 2 +- src/events/handleCreate.ts | 5 +- src/events/handleDelete.ts | 5 +- src/events/handleRename.ts | 15 +- src/functions/excalidraw.ts | 4 +- src/functions/folderNoteFunctions.ts | 37 +- src/functions/styleFunctions.ts | 8 +- src/functions/utils.ts | 18 +- src/globals.d.ts | 2 +- src/main.ts | 20 +- src/modals/AddSupportedFileType.ts | 9 +- src/modals/AskForExtension.ts | 5 +- src/modals/DeleteConfirmation.ts | 5 +- src/modals/ExistingNote.ts | 5 +- src/modals/FolderName.ts | 7 +- src/modals/NewFolderName.ts | 5 +- src/obsidian-folder-overview | 2 +- src/settings/ExcludedFoldersSettings.ts | 2 +- src/settings/FileExplorerSettings.ts | 28 +- src/settings/FolderOverviewSettings.ts | 4 +- src/settings/GeneralSettings.ts | 46 +- src/settings/PathSettings.ts | 16 +- src/settings/SettingsTab.ts | 21 +- src/settings/modals/BackupWarning.ts | 2 +- src/settings/modals/CreateFnForEveryFolder.ts | 7 +- src/settings/modals/RenameFns.ts | 6 +- src/suggesters/FileSuggester.ts | 7 +- src/suggesters/FolderSuggester.ts | 5 +- src/suggesters/Suggest.ts | 20 +- src/suggesters/TemplateSuggester.ts | 11 +- src/template.ts | 11 +- 53 files changed, 1746 insertions(+), 1274 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc create mode 100644 eslint.config.mts diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 9e519d6..0000000 --- a/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -npm node_modules -build -*.js -*.json -*.md -*.css -LICENSE \ No newline at end of file diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 6d26f2d..0000000 --- a/.eslintrc +++ /dev/null @@ -1,150 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "env": { - "node": true - }, - "plugins": [ - "@typescript-eslint" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - "parserOptions": { - "sourceType": "module" - }, - "rules": { - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "error", - { - "args": "none" - } - ], - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/ban-ts-comment": "off", - "no-prototype-builtins": "off", - "@typescript-eslint/no-empty-function": "off", - "brace-style": [ - "error", - "1tbs", - { - "allowSingleLine": true - } - ], - "consistent-return": "off", - "quotes": [ - "error", - "single", - { - "avoidEscape": true - } - ], - "@typescript-eslint/ban-types": [ - "error", - { - "types": { - "Function": false - }, - "extendDefaults": true - } - ], - "no-mixed-spaces-and-tabs": "error", - "indent": [ - "error", - "tab", - { - "SwitchCase": 1 - } - ], - "arrow-parens": [ - "error", - "always" - ], - "eol-last": [ - "error", - "always" - ], - "func-call-spacing": [ - "error", - "never" - ], - "comma-dangle": [ - "error", - "always-multiline" - ], - "no-multi-spaces": "error", - "no-trailing-spaces": "error", - "no-whitespace-before-property": "off", - "semi": [ - "error", - "always" - ], - "semi-style": [ - "error", - "last" - ], - "space-in-parens": [ - "error", - "never" - ], - "block-spacing": [ - "error", - "always" - ], - "object-curly-spacing": [ - "error", - "always" - ], - "eqeqeq": [ - "error", - "always", - { - "null": "ignore" - } - ], - "spaced-comment": [ - "error", - "always", - { - "markers": [ - "!" - ] - } - ], - "yoda": "error", - "prefer-destructuring": [ - "error", - { - "object": false, - "array": false - } - ], - "operator-assignment": [ - "error", - "always" - ], - "no-useless-computed-key": "error", - "no-unneeded-ternary": [ - "error", - { - "defaultAssignment": false - } - ], - "no-invalid-regexp": "error", - "no-constant-condition": [ - "error", - { - "checkLoops": false - } - ], - "no-duplicate-imports": "error", - "no-extra-semi": "error", - "dot-notation": "error", - "no-useless-escape": [ - "error" - ] - } -} \ No newline at end of file diff --git a/eslint.config.mts b/eslint.config.mts new file mode 100644 index 0000000..bb047f9 --- /dev/null +++ b/eslint.config.mts @@ -0,0 +1,158 @@ +import tseslint from "typescript-eslint"; + +export default [ + { + files: ["**/*.ts"], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + sourceType: "module" + } + }, + ignores: [ + "**/node_modules/**", + "**/dist/**", + ], + plugins: { + "@typescript-eslint": tseslint.plugin + }, + rules: { + "no-unused-vars": "off", + "quotes": [ + "error", + "single", + { + "avoidEscape": true + } + ], + "no-mixed-spaces-and-tabs": "error", + "indent": [ + "error", + "tab", + { + "SwitchCase": 1 + } + ], + "arrow-parens": [ + "error", + "always" + ], + "eol-last": [ + "error", + "always" + ], + "func-call-spacing": [ + "error", + "never" + ], + "comma-dangle": [ + "error", + "always-multiline" + ], + "no-multi-spaces": "error", + "no-trailing-spaces": "error", + "no-whitespace-before-property": "off", + "semi": [ + "error", + "always" + ], + "semi-style": [ + "error", + "last" + ], + "space-in-parens": [ + "error", + "never" + ], + "block-spacing": [ + "error", + "always" + ], + "object-curly-spacing": [ + "error", + "always" + ], + "eqeqeq": [ + "error", + "always", + { + "null": "ignore" + } + ], + "spaced-comment": [ + "error", + "always", + { + "markers": [ + "!" + ] + } + ], + "yoda": "error", + "prefer-destructuring": [ + "error", + { + "object": true, + "array": false + } + ], + "operator-assignment": [ + "error", + "always" + ], + "no-useless-computed-key": "error", + "no-unneeded-ternary": [ + "error", + { + "defaultAssignment": false + } + ], + "no-invalid-regexp": "error", + "no-constant-condition": [ + "error", + { + "checkLoops": false + } + ], + "no-duplicate-imports": "error", + "no-extra-semi": "error", + "dot-notation": "error", + "no-useless-escape": "error", + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/consistent-type-definitions': [ + 'error', + 'interface'], + '@typescript-eslint/explicit-function-return-type': 'warn', + '@typescript-eslint/ban-ts-comment': 'warn', + 'array-bracket-spacing': [ + 'error', 'never'], + 'linebreak-style': [ + 'error', + 'unix' + ], + 'no-nested-ternary': 'error', + 'no-shadow': 'error', + 'no-return-await': 'error', + 'no-else-return': 'error', + 'no-empty-function': 'warn', + 'complexity': [ + 'warn', + 10 + ], + 'max-len': [ + 'warn', { + code: 100 + } + ], + 'no-inline-comments': 'warn', + 'no-magic-numbers': [ + 'warn', { + ignore: [0, 1], + enforceConst: true + } + ], + } + } +]; diff --git a/package-lock.json b/package-lock.json index c89fed7..7886651 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,16 +12,21 @@ "@popperjs/core": "^2.11.6" }, "devDependencies": { + "@eslint/js": "^9.32.0", "@types/node": "^16.11.6", "@typescript-eslint/eslint-plugin": "5.29.0", "@typescript-eslint/parser": "5.29.0", "builtin-modules": "3.3.0", "esbuild": "0.14.47", + "eslint": "^9.32.0", "front-matter-plugin-api-provider": "^0.1.4-alpha", + "globals": "^16.3.0", + "jiti": "^2.5.1", "obsidian": "latest", "obsidian-typings": "^2.2.0", "tslib": "2.4.0", - "typescript": "4.7.4" + "typescript": "^4.8.4", + "typescript-eslint": "^8.38.0" } }, "node_modules/@codemirror/state": { @@ -76,42 +81,96 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "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.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "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.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "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": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -119,35 +178,98 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/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/@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, - "peer": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, - "peer": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" }, "engines": { - "node": ">=10.10.0" + "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.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/module-importer": { @@ -155,7 +277,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "peer": true, "engines": { "node": ">=12.22" }, @@ -164,12 +285,19 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "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, - "peer": true + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -264,10 +392,11 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", @@ -277,10 +406,11 @@ "peer": true }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true + "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/keyv": { "version": "3.1.4", @@ -361,6 +491,55 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", + "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/@typescript-eslint/parser": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.29.0.tgz", @@ -388,6 +567,42 @@ } } }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", + "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/project-service/node_modules/@typescript-eslint/types": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "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/scope-manager": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", @@ -405,6 +620,23 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "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": "5.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz", @@ -431,6 +663,55 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", + "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/@typescript-eslint/types": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", @@ -471,30 +752,6 @@ } } }, - "node_modules/@typescript-eslint/utils": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", - "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/typescript-estree": "5.29.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", @@ -513,11 +770,11 @@ } }, "node_modules/acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, - "peer": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -530,7 +787,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "peer": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -540,7 +797,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -552,22 +809,11 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, "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, - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -583,7 +829,7 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "peer": true + "license": "Python-2.0" }, "node_modules/array-union": { "version": "2.1.0", @@ -599,7 +845,7 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "peer": true + "license": "MIT" }, "node_modules/boolean": { "version": "3.2.0", @@ -610,11 +856,11 @@ "peer": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "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, - "peer": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -625,6 +871,7 @@ "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" }, @@ -688,7 +935,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -698,7 +945,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -728,7 +974,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -740,22 +985,21 @@ "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, - "peer": true + "dev": true }, "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, - "peer": true + "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -816,7 +1060,7 @@ "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, - "peer": true + "license": "MIT" }, "node_modules/defer-to-connect": { "version": "2.0.1", @@ -886,19 +1130,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "peer": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/electron": { "version": "31.4.0", "resolved": "https://registry.npmjs.org/electron/-/electron-31.4.0.tgz", @@ -1362,7 +1593,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "peer": true, "engines": { "node": ">=10" }, @@ -1371,73 +1601,81 @@ } }, "node_modules/eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", - "esquery": "^1.4.2", + "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": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "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": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-utils": { @@ -1445,6 +1683,7 @@ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^2.0.0" }, @@ -1463,6 +1702,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=10" } @@ -1479,46 +1719,45 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "node_modules/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, - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/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, - "peer": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "peer": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/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" @@ -1529,7 +1768,6 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, - "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -1537,16 +1775,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/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, - "peer": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -1559,20 +1787,12 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "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, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -1582,7 +1802,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "peer": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -1613,19 +1833,20 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, - "peer": true + "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "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.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -1648,14 +1869,14 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "peer": 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, - "peer": true + "license": "MIT" }, "node_modules/fastq": { "version": "1.15.0", @@ -1677,16 +1898,16 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "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, - "peer": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -1694,6 +1915,7 @@ "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" }, @@ -1706,7 +1928,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -1719,25 +1940,25 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "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, - "peer": true + "license": "ISC" }, "node_modules/front-matter-plugin-api-provider": { "version": "0.1.4-alpha", @@ -1763,13 +1984,6 @@ "node": ">=6 <7 || >=8" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "peer": true - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1808,41 +2022,20 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "peer": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "peer": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "pump": "^3.0.0" }, "engines": { - "node": "*" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/glob-parent": { @@ -1850,7 +2043,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -1878,16 +2070,13 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1983,14 +2172,13 @@ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, - "peer": true + "license": "MIT" }, "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, - "peer": true, "engines": { "node": ">=8" } @@ -2082,11 +2270,11 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2103,29 +2291,10 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "peer": true, "engines": { "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "peer": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "peer": true - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2152,33 +2321,34 @@ "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-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, - "peer": true + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2190,22 +2360,20 @@ "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, - "peer": true + "dev": true }, "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, - "peer": 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, - "peer": true + "dev": true }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -2230,7 +2398,6 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -2240,7 +2407,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -2254,7 +2421,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -2269,8 +2435,7 @@ "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, - "peer": true + "dev": true }, "node_modules/lowercase-keys": { "version": "2.0.0", @@ -2282,18 +2447,6 @@ "node": ">=8" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -2318,12 +2471,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "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.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -2345,7 +2499,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "peer": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2372,8 +2526,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/normalize-url": { "version": "6.1.0", @@ -2435,18 +2588,18 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "peer": 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.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -2467,7 +2620,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -2483,7 +2635,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -2499,7 +2650,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -2512,27 +2663,16 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "peer": true, "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "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, - "peer": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2558,6 +2698,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -2570,7 +2711,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -2597,11 +2738,11 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -2663,7 +2804,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -2691,22 +2832,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "peer": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -2750,13 +2875,11 @@ } }, "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2808,7 +2931,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2821,7 +2944,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2843,25 +2966,12 @@ "optional": true, "peer": true }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "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, - "peer": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -2881,110 +2991,360 @@ "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, - "peer": true, + "peer": true, + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/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/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "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/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typescript-eslint": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.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/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "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.38.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "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/node_modules/@typescript-eslint/scope-manager": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "dev": true, + "license": "MIT", "dependencies": { - "debug": "^4.1.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { - "node": ">= 8.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "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==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": ">=8" + "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/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, - "peer": 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/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==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "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": ">=8.0" + "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/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^1.8.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { - "node": ">= 6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "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==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "@typescript-eslint/types": "8.38.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">= 0.8.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/typescript-eslint/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, - "peer": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-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": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "node_modules/typescript-eslint/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/typescript-eslint/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": ">=4.2.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/undici-types": { @@ -3009,7 +3369,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "peer": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -3026,7 +3386,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "peer": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -3042,7 +3402,7 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3054,12 +3414,6 @@ "dev": true, "peer": true }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -3076,7 +3430,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "peer": true, "engines": { "node": ">=10" }, @@ -3132,72 +3485,136 @@ } }, "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, - "peer": true, "requires": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" + }, + "dependencies": { + "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 + } } }, "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true + }, + "@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, - "peer": true + "requires": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + } + }, + "@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true + }, + "@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.15" + } }, "@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, - "peer": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", - "globals": "^13.19.0", + "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" + }, + "dependencies": { + "globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true + } } }, "@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "dev": true + }, + "@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true + }, + "@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, - "peer": true + "requires": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + } + }, + "@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 }, - "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, - "peer": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "dependencies": { + "@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true + } } }, "@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, - "peer": true + "dev": true }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true, - "peer": true + "@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -3270,9 +3687,9 @@ } }, "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, "@types/http-cache-semantics": { @@ -3283,9 +3700,9 @@ "peer": true }, "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "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 }, "@types/keyv": { @@ -3349,6 +3766,38 @@ "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", + "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } } }, "@typescript-eslint/parser": { @@ -3363,6 +3812,25 @@ "debug": "^4.3.4" } }, + "@typescript-eslint/project-service": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "dev": true, + "requires": { + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", + "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "dev": true + } + } + }, "@typescript-eslint/scope-manager": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", @@ -3373,6 +3841,13 @@ "@typescript-eslint/visitor-keys": "5.29.0" } }, + "@typescript-eslint/tsconfig-utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "dev": true, + "requires": {} + }, "@typescript-eslint/type-utils": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz", @@ -3382,6 +3857,38 @@ "@typescript-eslint/utils": "5.29.0", "debug": "^4.3.4", "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", + "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } } }, "@typescript-eslint/types": { @@ -3405,20 +3912,6 @@ "tsutils": "^3.21.0" } }, - "@typescript-eslint/utils": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", - "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/typescript-estree": "5.29.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, "@typescript-eslint/visitor-keys": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", @@ -3430,18 +3923,16 @@ } }, "acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", - "dev": true, - "peer": true + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true }, "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, - "peer": true, "requires": {} }, "ajv": { @@ -3449,7 +3940,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3457,19 +3947,11 @@ "uri-js": "^4.2.2" } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "peer": true - }, "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, - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -3478,8 +3960,7 @@ "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, - "peer": true + "dev": true }, "array-union": { "version": "2.1.0", @@ -3491,8 +3972,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "peer": true + "dev": true }, "boolean": { "version": "3.2.0", @@ -3503,11 +3983,10 @@ "peer": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "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, - "peer": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3562,15 +4041,13 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "peer": true + "dev": true }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3591,7 +4068,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -3600,22 +4076,19 @@ "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, - "peer": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "peer": true + "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "peer": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3654,8 +4127,7 @@ "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, - "peer": true + "dev": true }, "defer-to-connect": { "version": "2.0.1", @@ -3701,20 +4173,10 @@ "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "peer": true, "requires": { - "esutils": "^2.0.2" + "path-type": "^4.0.0" } }, "electron": { @@ -3957,85 +4419,67 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "peer": true + "dev": true }, "eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, - "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", - "esquery": "^1.4.2", + "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": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "dependencies": { - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "peer": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "peer": true + "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 } } }, "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" } }, "eslint-utils": { @@ -4062,15 +4506,22 @@ "dev": true }, "espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "peer": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.1" + }, + "dependencies": { + "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 + } } }, "esquery": { @@ -4078,18 +4529,8 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, - "peer": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "peer": true - } } }, "esrecurse": { @@ -4099,28 +4540,19 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "peer": true + "dev": true }, "extract-zip": { "version": "2.0.1", @@ -4139,20 +4571,19 @@ "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, - "peer": true + "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "dependencies": { "glob-parent": { @@ -4170,15 +4601,13 @@ "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, - "peer": true + "dev": true }, "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, - "peer": true + "dev": true }, "fastq": { "version": "1.15.0", @@ -4200,13 +4629,12 @@ } }, "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "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, - "peer": true, "requires": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" } }, "fill-range": { @@ -4223,29 +4651,26 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "peer": true, "requires": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "peer": true, "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" } }, "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true, - "peer": true + "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 }, "front-matter-plugin-api-provider": { "version": "0.1.4-alpha", @@ -4268,13 +4693,6 @@ "universalify": "^0.1.0" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "peer": true - }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -4314,27 +4732,11 @@ "pump": "^3.0.0" } }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "peer": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "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, - "peer": true, "requires": { "is-glob": "^4.0.3" } @@ -4356,14 +4758,10 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "peer": true, - "requires": { - "type-fest": "^0.20.2" - } + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "dev": true }, "globalthis": { "version": "1.0.4", @@ -4433,15 +4831,13 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "peer": true + "dev": true }, "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, - "peer": true + "dev": true }, "has-property-descriptors": { "version": "1.0.2", @@ -4506,11 +4902,10 @@ "dev": true }, "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "peer": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -4520,26 +4915,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "peer": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "peer": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "peer": true + "dev": true }, "is-extglob": { "version": "2.1.1", @@ -4562,26 +4938,23 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "peer": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "peer": true + "dev": true + }, + "jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "dev": true }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "peer": true, "requires": { "argparse": "^2.0.1" } @@ -4590,22 +4963,19 @@ "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, - "peer": true + "dev": true }, "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, - "peer": true + "dev": true }, "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, - "peer": true + "dev": true }, "json-stringify-safe": { "version": "5.0.1", @@ -4630,7 +5000,6 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "peer": true, "requires": { "json-buffer": "3.0.1" } @@ -4640,7 +5009,6 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "peer": true, "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -4651,7 +5019,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "peer": true, "requires": { "p-locate": "^5.0.0" } @@ -4660,8 +5027,7 @@ "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, - "peer": true + "dev": true }, "lowercase-keys": { "version": "2.0.0", @@ -4670,15 +5036,6 @@ "dev": true, "peer": true }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, "matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -4697,12 +5054,12 @@ "dev": true }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, @@ -4718,7 +5075,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "peer": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4739,8 +5095,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "peer": true + "dev": true }, "normalize-url": { "version": "6.1.0", @@ -4785,18 +5140,17 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "peer": true, "requires": { "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.3" + "word-wrap": "^1.2.5" } }, "p-cancelable": { @@ -4811,7 +5165,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "peer": true, "requires": { "yocto-queue": "^0.1.0" } @@ -4821,7 +5174,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "peer": true, "requires": { "p-limit": "^3.0.2" } @@ -4831,7 +5183,6 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "peer": true, "requires": { "callsites": "^3.0.0" } @@ -4840,22 +5191,13 @@ "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, - "peer": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "peer": true + "dev": true }, "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, - "peer": true + "dev": true }, "path-type": { "version": "4.0.0", @@ -4880,8 +5222,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "peer": true + "dev": true }, "progress": { "version": "2.0.3", @@ -4902,11 +5243,10 @@ } }, "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "peer": true + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true }, "queue-microtask": { "version": "1.2.3", @@ -4938,8 +5278,7 @@ "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, - "peer": true + "dev": true }, "responselike": { "version": "2.0.1", @@ -4957,16 +5296,6 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "peer": true, - "requires": { - "glob": "^7.1.3" - } - }, "roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -4993,13 +5322,10 @@ } }, "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true }, "semver-compare": { "version": "1.0.0", @@ -5035,7 +5361,6 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "peer": true, "requires": { "shebang-regex": "^3.0.0" } @@ -5044,8 +5369,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "peer": true + "dev": true }, "slash": { "version": "3.0.0", @@ -5061,22 +5385,11 @@ "optional": true, "peer": true }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "peer": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "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, - "peer": true + "dev": true }, "style-mod": { "version": "4.0.3", @@ -5100,18 +5413,10 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "peer": true, "requires": { "has-flag": "^4.0.0" } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "peer": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5121,6 +5426,13 @@ "is-number": "^7.0.0" } }, + "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, + "requires": {} + }, "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -5149,24 +5461,159 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "peer": true, "requires": { "prelude-ls": "^1.2.1" } }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "peer": true - }, "typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true }, + "typescript-eslint": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/parser": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/types": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "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" + } + }, + "@typescript-eslint/utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.38.0", + "eslint-visitor-keys": "^4.2.1" + } + }, + "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, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "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 + }, + "ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -5186,7 +5633,6 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "peer": true, "requires": { "punycode": "^2.1.0" } @@ -5203,7 +5649,6 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "peer": true, "requires": { "isexe": "^2.0.0" } @@ -5212,8 +5657,7 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "peer": true + "dev": true }, "wrappy": { "version": "1.0.2", @@ -5222,12 +5666,6 @@ "dev": true, "peer": true }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -5243,8 +5681,7 @@ "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, - "peer": true + "dev": true } } } diff --git a/package.json b/package.json index 8cd6454..f459e2a 100644 --- a/package.json +++ b/package.json @@ -16,16 +16,21 @@ "author": "Lost Paul", "license": "GPL-3.0-or-later", "devDependencies": { + "@eslint/js": "^9.32.0", "@types/node": "^16.11.6", "@typescript-eslint/eslint-plugin": "5.29.0", "@typescript-eslint/parser": "5.29.0", "builtin-modules": "3.3.0", "esbuild": "0.14.47", + "eslint": "^9.32.0", "front-matter-plugin-api-provider": "^0.1.4-alpha", + "globals": "^16.3.0", + "jiti": "^2.5.1", "obsidian": "latest", "obsidian-typings": "^2.2.0", "tslib": "2.4.0", - "typescript": "4.7.4" + "typescript": "^4.8.4", + "typescript-eslint": "^8.38.0" }, "dependencies": { "@popperjs/core": "^2.11.6" diff --git a/src/Commands.ts b/src/Commands.ts index 8e304e7..56f7dd1 100644 --- a/src/Commands.ts +++ b/src/Commands.ts @@ -1,5 +1,6 @@ -import { App, TFolder, Menu, TAbstractFile, Notice, TFile, Editor, MarkdownView, Platform } from 'obsidian'; -import FolderNotesPlugin from './main'; +import type { App, Menu, TAbstractFile, Editor, MarkdownView } from 'obsidian'; +import { TFolder, Notice, TFile, Platform } from 'obsidian'; +import type FolderNotesPlugin from './main'; import { getFolderNote, createFolderNote, deleteFolderNote, turnIntoFolderNote, openFolderNote, extractFolderName, detachFolderNote } from './functions/folderNoteFunctions'; import { ExcludedFolder } from './ExcludeFolders/ExcludeFolder'; import { getFolderPathFromString, getFileExplorerActiveFolder } from './functions/utils'; @@ -108,7 +109,7 @@ export class Commands { // Everything is fine and not checking, let's create the folder note. const ext = '.' + fileType; - const path = folder.path; + const { path } = folder; createFolderNote(this.plugin, path, true, ext, false); }, }); @@ -179,7 +180,7 @@ export class Commands { name: 'Create folder note from selection', editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView) => { const text = editor.getSelection().trim(); - const file = view.file; + const { file } = view; if (!(file instanceof TFile)) return false; if (text && text.trim() !== '') { if (checking) { return true; } @@ -201,10 +202,10 @@ export class Commands { if (folder instanceof TFolder) { new Notice('Folder note already exists'); return false; - } else { - this.plugin.app.vault.createFolder(text); - createFolderNote(this.plugin, text, false); } + this.plugin.app.vault.createFolder(text); + createFolderNote(this.plugin, text, false); + } else { folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text); if (folder instanceof TFolder) { @@ -447,7 +448,7 @@ export class Commands { item.setTitle('Create folder note') .setIcon('edit') .onClick(() => { - const file = view.file; + const { file } = view; if (!(file instanceof TFile)) return; const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':']; for (const char of blacklist) { @@ -467,10 +468,10 @@ export class Commands { folder = this.plugin.app.vault.getAbstractFileByPath(text); if (folder instanceof TFolder) { return new Notice('Folder note already exists'); - } else { - this.plugin.app.vault.createFolder(text); - createFolderNote(this.plugin, text, false); } + this.plugin.app.vault.createFolder(text); + createFolderNote(this.plugin, text, false); + } else { folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text); if (folder instanceof TFolder) { diff --git a/src/ExcludeFolders/ExcludeFolder.ts b/src/ExcludeFolders/ExcludeFolder.ts index 8eda370..24af3ab 100644 --- a/src/ExcludeFolders/ExcludeFolder.ts +++ b/src/ExcludeFolders/ExcludeFolder.ts @@ -1,4 +1,4 @@ -import FolderNotesPlugin from '../main'; +import type FolderNotesPlugin from '../main'; export class ExcludedFolder { type: string; id: string; diff --git a/src/ExcludeFolders/ExcludePattern.ts b/src/ExcludeFolders/ExcludePattern.ts index 6839069..c78aab3 100644 --- a/src/ExcludeFolders/ExcludePattern.ts +++ b/src/ExcludeFolders/ExcludePattern.ts @@ -1,4 +1,4 @@ -import FolderNotesPlugin from '../main'; +import type FolderNotesPlugin from '../main'; export class ExcludePattern { type: string; id: string; diff --git a/src/ExcludeFolders/WhitelistFolder.ts b/src/ExcludeFolders/WhitelistFolder.ts index f0b40db..e290926 100644 --- a/src/ExcludeFolders/WhitelistFolder.ts +++ b/src/ExcludeFolders/WhitelistFolder.ts @@ -1,4 +1,4 @@ -import FolderNotesPlugin from '../main'; +import type FolderNotesPlugin from '../main'; export class WhitelistedFolder { type: string; id: string; diff --git a/src/ExcludeFolders/WhitelistPattern.ts b/src/ExcludeFolders/WhitelistPattern.ts index 43ecd45..bf9fe91 100644 --- a/src/ExcludeFolders/WhitelistPattern.ts +++ b/src/ExcludeFolders/WhitelistPattern.ts @@ -1,4 +1,4 @@ -import FolderNotesPlugin from '../main'; +import type FolderNotesPlugin from '../main'; export class WhitelistedPattern { type: string; id: string; diff --git a/src/ExcludeFolders/functions/folderFunctions.ts b/src/ExcludeFolders/functions/folderFunctions.ts index 1c48af0..a2fb61c 100644 --- a/src/ExcludeFolders/functions/folderFunctions.ts +++ b/src/ExcludeFolders/functions/folderFunctions.ts @@ -1,15 +1,15 @@ -import FolderNotesPlugin from '../../main'; +import type FolderNotesPlugin from '../../main'; import { getFolderNameFromPathString, getFolderPathFromString } from '../../functions/utils'; -import { ExcludedFolder } from '../ExcludeFolder'; +import type { ExcludedFolder } from '../ExcludeFolder'; import { ExcludePattern } from '../ExcludePattern'; import { Platform, Setting } from 'obsidian'; import { FolderSuggest } from '../../suggesters/FolderSuggester'; -import { SettingsTab } from '../../settings/SettingsTab'; +import type { SettingsTab } from '../../settings/SettingsTab'; import ExcludedFolderSettings from '../modals/ExcludeFolderSettings'; import { updatePattern, getExcludedFoldersByPattern, addExcludePatternListItem } from './patternFunctions'; import { getWhitelistedFolder } from './whitelistFolderFunctions'; -import { WhitelistedFolder } from '../WhitelistFolder'; -import { WhitelistedPattern } from '../WhitelistPattern'; +import type { WhitelistedFolder } from '../WhitelistFolder'; +import type { WhitelistedPattern } from '../WhitelistPattern'; export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, includeDetached: boolean, pathOnly?: boolean, ignoreWhitelist?: boolean) { let excludedFolder = {} as ExcludedFolder | ExcludePattern | undefined; @@ -97,9 +97,9 @@ export function getExcludedFolderByPath(plugin: FolderNotesPlugin, path: string) if (folderPath.includes('/') || folderPath.includes('\\')) { return folderPath.startsWith(excludedFolderPath) || folderPath === excludedFolderPath; - } else { - return folderPath === excludedFolderPath; } + return folderPath === excludedFolderPath; + }); } @@ -114,9 +114,9 @@ export function getExcludedFoldersByPath(plugin: FolderNotesPlugin, path: string if (folderPath.includes('/') || folderPath.includes('\\')) { return folderPath.startsWith(excludedFolderPath) || folderPath === excludedFolderPath; - } else { - return folderPath === excludedFolderPath; } + return folderPath === excludedFolderPath; + }); } @@ -146,14 +146,14 @@ export function resyncArray(plugin: FolderNotesPlugin) { export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTMLElement, excludedFolder: ExcludedFolder) { - const plugin: FolderNotesPlugin = settings.plugin; + const { plugin } = settings; const setting = new Setting(containerEl); setting.setClass('fn-exclude-folder-list'); setting.addSearch((cb) => { new FolderSuggest( cb.inputEl, plugin, - false + false, ); // @ts-ignore cb.containerEl.addClass('fn-exclude-folder-path'); diff --git a/src/ExcludeFolders/functions/patternFunctions.ts b/src/ExcludeFolders/functions/patternFunctions.ts index c3f8c8b..8e4e4ae 100644 --- a/src/ExcludeFolders/functions/patternFunctions.ts +++ b/src/ExcludeFolders/functions/patternFunctions.ts @@ -1,7 +1,7 @@ -import FolderNotesPlugin from '../../main'; -import { ExcludePattern } from '../ExcludePattern'; +import type FolderNotesPlugin from '../../main'; +import type { ExcludePattern } from '../ExcludePattern'; import { Setting, Platform } from 'obsidian'; -import { SettingsTab } from '../../settings/SettingsTab'; +import type { SettingsTab } from '../../settings/SettingsTab'; import { addExcludedFolder, resyncArray, updateExcludedFolder } from './folderFunctions'; import PatternSettings from '../modals/PatternSettings'; @@ -73,7 +73,7 @@ export function getExcludedFolderByPattern(plugin: FolderNotesPlugin, folderName } export function addExcludePatternListItem(settings: SettingsTab, containerEl: HTMLElement, pattern: ExcludePattern) { - const plugin: FolderNotesPlugin = settings.plugin; + const { plugin } = settings; const setting = new Setting(containerEl); setting.setClass('fn-exclude-folder-list'); setting.addSearch((cb) => { diff --git a/src/ExcludeFolders/functions/whitelistFolderFunctions.ts b/src/ExcludeFolders/functions/whitelistFolderFunctions.ts index 78cdba1..156a716 100644 --- a/src/ExcludeFolders/functions/whitelistFolderFunctions.ts +++ b/src/ExcludeFolders/functions/whitelistFolderFunctions.ts @@ -1,10 +1,10 @@ -import FolderNotesPlugin from '../../main'; +import type FolderNotesPlugin from '../../main'; import { getFolderNameFromPathString, getFolderPathFromString } from '../../functions/utils'; -import { WhitelistedFolder } from '../WhitelistFolder'; +import type { WhitelistedFolder } from '../WhitelistFolder'; import { WhitelistedPattern } from '../WhitelistPattern'; import { Setting, Platform, ButtonComponent } from 'obsidian'; import { FolderSuggest } from '../../suggesters/FolderSuggester'; -import { SettingsTab } from '../../settings/SettingsTab'; +import type { SettingsTab } from '../../settings/SettingsTab'; import WhitelistFolderSettings from '../modals/WhitelistFolderSettings'; import { updateWhitelistedPattern, getWhitelistedFoldersByPattern, addWhitelistedPatternListItem } from './whitelistPatternFunctions'; Platform.isMobileApp; @@ -81,7 +81,7 @@ export function resyncArray(plugin: FolderNotesPlugin) { export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: HTMLElement, whitelistedFolder: WhitelistedFolder) { - const plugin: FolderNotesPlugin = settings.plugin; + const { plugin } = settings; const setting = new Setting(containerEl); setting.setClass('fn-exclude-folder-list'); @@ -91,7 +91,7 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H new FolderSuggest( cb.inputEl, plugin, - true + true, ); // @ts-ignore cb.containerEl.addClass('fn-exclude-folder-path'); diff --git a/src/ExcludeFolders/functions/whitelistPatternFunctions.ts b/src/ExcludeFolders/functions/whitelistPatternFunctions.ts index 6253e8c..30f5363 100644 --- a/src/ExcludeFolders/functions/whitelistPatternFunctions.ts +++ b/src/ExcludeFolders/functions/whitelistPatternFunctions.ts @@ -1,9 +1,9 @@ -import FolderNotesPlugin from '../../main'; +import type FolderNotesPlugin from '../../main'; import { Setting } from 'obsidian'; -import { SettingsTab } from '../../settings/SettingsTab'; +import type { SettingsTab } from '../../settings/SettingsTab'; import { resyncArray } from './folderFunctions'; import WhitelistPatternSettings from '../modals/WhitelistPatternSettings'; -import { WhitelistedPattern } from '../WhitelistPattern'; +import type { WhitelistedPattern } from '../WhitelistPattern'; import { addWhitelistedFolder, updateWhitelistedFolder } from './whitelistFolderFunctions'; export function updateWhitelistedPattern(plugin: FolderNotesPlugin, pattern: WhitelistedPattern, newPattern: WhitelistedPattern) { @@ -74,7 +74,7 @@ export function getWhitelistedFoldersByPattern(plugin: FolderNotesPlugin, folder } export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl: HTMLElement, pattern: WhitelistedPattern) { - const plugin: FolderNotesPlugin = settings.plugin; + const { plugin } = settings; const setting = new Setting(containerEl); setting.setClass('fn-exclude-folder-list'); setting.addSearch((cb) => { diff --git a/src/ExcludeFolders/modals/ExcludeFolderSettings.ts b/src/ExcludeFolders/modals/ExcludeFolderSettings.ts index 7326bc6..7f634b2 100644 --- a/src/ExcludeFolders/modals/ExcludeFolderSettings.ts +++ b/src/ExcludeFolders/modals/ExcludeFolderSettings.ts @@ -1,6 +1,7 @@ -import { App, Modal, Setting } from 'obsidian'; -import FolderNotesPlugin from '../../main'; -import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; +import type { App } from 'obsidian'; +import { Modal, Setting } from 'obsidian'; +import type FolderNotesPlugin from '../../main'; +import type { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; import { updateCSSClassesForFolder } from 'src/functions/styleFunctions'; export default class ExcludedFolderSettings extends Modal { plugin: FolderNotesPlugin; @@ -28,7 +29,7 @@ export default class ExcludedFolderSettings extends Modal { .onChange(async (value) => { this.excludedFolder.subFolders = value; await this.plugin.saveSettings(true); - }) + }), ); new Setting(contentEl) @@ -40,7 +41,7 @@ export default class ExcludedFolderSettings extends Modal { .onChange(async (value) => { this.excludedFolder.disableSync = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -52,7 +53,7 @@ export default class ExcludedFolderSettings extends Modal { .onChange(async (value) => { this.excludedFolder.excludeFromFolderOverview = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -66,7 +67,7 @@ export default class ExcludedFolderSettings extends Modal { updateCSSClassesForFolder(this.excludedFolder.path, this.plugin); await this.plugin.saveSettings(); this.display(); - }) + }), ); new Setting(contentEl) @@ -78,7 +79,7 @@ export default class ExcludedFolderSettings extends Modal { .onChange(async (value) => { this.excludedFolder.disableAutoCreate = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -91,7 +92,7 @@ export default class ExcludedFolderSettings extends Modal { this.excludedFolder.disableFolderNote = value; await this.plugin.saveSettings(true); this.display(); - }) + }), ); if (!this.excludedFolder.disableFolderNote) { @@ -104,7 +105,7 @@ export default class ExcludedFolderSettings extends Modal { .onChange(async (value) => { this.excludedFolder.enableCollapsing = value; await this.plugin.saveSettings(); - }) + }), ); } diff --git a/src/ExcludeFolders/modals/PatternSettings.ts b/src/ExcludeFolders/modals/PatternSettings.ts index a353c26..426f228 100644 --- a/src/ExcludeFolders/modals/PatternSettings.ts +++ b/src/ExcludeFolders/modals/PatternSettings.ts @@ -1,6 +1,7 @@ -import { App, Modal, Setting } from 'obsidian'; -import FolderNotesPlugin from '../../main'; -import { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern'; +import type { App } from 'obsidian'; +import { Modal, Setting } from 'obsidian'; +import type FolderNotesPlugin from '../../main'; +import type { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern'; import { refreshAllFolderStyles } from 'src/functions/styleFunctions'; export default class PatternSettings extends Modal { @@ -30,7 +31,7 @@ export default class PatternSettings extends Modal { .onChange(async (value) => { this.pattern.disableSync = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -42,7 +43,7 @@ export default class PatternSettings extends Modal { .onChange(async (value) => { this.pattern.disableAutoCreate = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -54,7 +55,7 @@ export default class PatternSettings extends Modal { .onChange(async (value) => { this.pattern.excludeFromFolderOverview = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -68,7 +69,7 @@ export default class PatternSettings extends Modal { await this.plugin.saveSettings(); refreshAllFolderStyles(true, this.plugin); this.display(); - }) + }), ); new Setting(contentEl) @@ -81,7 +82,7 @@ export default class PatternSettings extends Modal { this.pattern.disableFolderNote = value; await this.plugin.saveSettings(true); this.display(); - }) + }), ); if (!this.pattern.disableFolderNote) { @@ -94,7 +95,7 @@ export default class PatternSettings extends Modal { .onChange(async (value) => { this.pattern.enableCollapsing = value; await this.plugin.saveSettings(); - }) + }), ); } diff --git a/src/ExcludeFolders/modals/WhitelistFolderSettings.ts b/src/ExcludeFolders/modals/WhitelistFolderSettings.ts index 9673758..6f65cc7 100644 --- a/src/ExcludeFolders/modals/WhitelistFolderSettings.ts +++ b/src/ExcludeFolders/modals/WhitelistFolderSettings.ts @@ -1,6 +1,7 @@ -import { App, Modal, Setting } from 'obsidian'; -import FolderNotesPlugin from '../../main'; -import { WhitelistedFolder } from '../WhitelistFolder'; +import type { App } from 'obsidian'; +import { Modal, Setting } from 'obsidian'; +import type FolderNotesPlugin from '../../main'; +import type { WhitelistedFolder } from '../WhitelistFolder'; export default class WhitelistFolderSettings extends Modal { plugin: FolderNotesPlugin; app: App; @@ -27,7 +28,7 @@ export default class WhitelistFolderSettings extends Modal { .onChange(async (value) => { this.whitelistedFolder.subFolders = value; await this.plugin.saveSettings(true); - }) + }), ); new Setting(contentEl) @@ -39,7 +40,7 @@ export default class WhitelistFolderSettings extends Modal { .onChange(async (value) => { this.whitelistedFolder.enableSync = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -51,7 +52,7 @@ export default class WhitelistFolderSettings extends Modal { .onChange(async (value) => { this.whitelistedFolder.showInFolderOverview = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -63,7 +64,7 @@ export default class WhitelistFolderSettings extends Modal { .onChange(async (value) => { this.whitelistedFolder.hideInFileExplorer = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -74,7 +75,7 @@ export default class WhitelistFolderSettings extends Modal { .onChange(async (value) => { this.whitelistedFolder.enableAutoCreate = value; await this.plugin.saveSettings(); - }) + }), ); @@ -88,7 +89,7 @@ export default class WhitelistFolderSettings extends Modal { this.whitelistedFolder.enableFolderNote = value; await this.plugin.saveSettings(true); this.display(); - }) + }), ); if (this.whitelistedFolder.enableFolderNote) { @@ -101,7 +102,7 @@ export default class WhitelistFolderSettings extends Modal { .onChange(async (value) => { this.whitelistedFolder.disableCollapsing = value; await this.plugin.saveSettings(); - }) + }), ); } diff --git a/src/ExcludeFolders/modals/WhitelistPatternSettings.ts b/src/ExcludeFolders/modals/WhitelistPatternSettings.ts index b328f34..45a3688 100644 --- a/src/ExcludeFolders/modals/WhitelistPatternSettings.ts +++ b/src/ExcludeFolders/modals/WhitelistPatternSettings.ts @@ -1,6 +1,7 @@ -import { App, Modal, Setting } from 'obsidian'; -import FolderNotesPlugin from '../../main'; -import { WhitelistedPattern } from '../WhitelistPattern'; +import type { App } from 'obsidian'; +import { Modal, Setting } from 'obsidian'; +import type FolderNotesPlugin from '../../main'; +import type { WhitelistedPattern } from '../WhitelistPattern'; export default class WhitelistPatternSettings extends Modal { plugin: FolderNotesPlugin; @@ -28,7 +29,7 @@ export default class WhitelistPatternSettings extends Modal { .onChange(async (value) => { this.pattern.enableSync = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -39,7 +40,7 @@ export default class WhitelistPatternSettings extends Modal { .onChange(async (value) => { this.pattern.enableAutoCreate = value; await this.plugin.saveSettings(); - }) + }), ); new Setting(contentEl) @@ -51,7 +52,7 @@ export default class WhitelistPatternSettings extends Modal { .onChange(async (value) => { this.pattern.showInFolderOverview = value; await this.plugin.saveSettings(); - }) + }), ); @@ -65,7 +66,7 @@ export default class WhitelistPatternSettings extends Modal { this.pattern.enableFolderNote = value; await this.plugin.saveSettings(true); this.display(); - }) + }), ); if (this.pattern.enableFolderNote) { @@ -78,7 +79,7 @@ export default class WhitelistPatternSettings extends Modal { .onChange(async (value) => { this.pattern.disableCollapsing = value; await this.plugin.saveSettings(); - }) + }), ); } diff --git a/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts b/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts index 3208d57..2321f34 100644 --- a/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts +++ b/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts @@ -1,6 +1,7 @@ -import { App, Modal, Setting } from 'obsidian'; -import { SettingsTab } from 'src/settings/SettingsTab'; -import FolderNotesPlugin from '../../main'; +import type { App } from 'obsidian'; +import { Modal, Setting } from 'obsidian'; +import type { SettingsTab } from 'src/settings/SettingsTab'; +import type FolderNotesPlugin from '../../main'; import { WhitelistedFolder } from '../WhitelistFolder'; import { addWhitelistFolderListItem, addWhitelistedFolder } from '../functions/whitelistFolderFunctions'; import { addWhitelistedPatternListItem } from '../functions/whitelistPatternFunctions'; diff --git a/src/events/FrontMatterTitle.ts b/src/events/FrontMatterTitle.ts index 224faf6..3586346 100644 --- a/src/events/FrontMatterTitle.ts +++ b/src/events/FrontMatterTitle.ts @@ -1,6 +1,8 @@ -import FolderNotesPlugin from 'src/main'; -import { getDefer, Listener, Events, ApiInterface, DeferInterface, ListenerRef, EventDispatcherInterface } from 'front-matter-plugin-api-provider'; -import { App, TFile, TFolder } from 'obsidian'; +import type FolderNotesPlugin from 'src/main'; +import type { Listener, Events, ApiInterface, DeferInterface, ListenerRef, EventDispatcherInterface } from 'front-matter-plugin-api-provider'; +import { getDefer } from 'front-matter-plugin-api-provider'; +import type { App } from 'obsidian'; +import { TFile, TFolder } from 'obsidian'; import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions'; export class FrontMatterTitlePluginHandler { plugin: FolderNotesPlugin; @@ -77,7 +79,7 @@ export class FrontMatterTitlePluginHandler { this.plugin.changeFolderNameInExplorer(folder, newName); } - const breadcrumb = data.breadcrumb; + const { breadcrumb } = data; if (breadcrumb) { this.plugin.changeFolderNameInPath(folder, newName, breadcrumb); } @@ -117,7 +119,7 @@ export class FrontMatterTitlePluginHandler { this.plugin.changeFolderNameInExplorer(folder, newName); } - const breadcrumb = data.breadcrumb; + const { breadcrumb } = data; if (breadcrumb) { this.plugin.changeFolderNameInPath(folder, newName, breadcrumb); } diff --git a/src/events/MutationObserver.ts b/src/events/MutationObserver.ts index f4b5295..a03a5e0 100644 --- a/src/events/MutationObserver.ts +++ b/src/events/MutationObserver.ts @@ -1,5 +1,5 @@ import { Keymap, Platform } from 'obsidian'; -import FolderNotesPlugin from 'src/main'; +import type FolderNotesPlugin from 'src/main'; import { getFolderNote } from 'src/functions/folderNoteFunctions'; import { handleViewHeaderClick } from './handleClick'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; @@ -26,7 +26,7 @@ export function registerFileExplorerObserver(plugin: FolderNotesPlugin) { if (!(titleContainer instanceof HTMLElement)) return; updateFolderNamesInPath(plugin, titleContainer); - }) + }), ); } diff --git a/src/events/TabManager.ts b/src/events/TabManager.ts index 131f7f8..11903ff 100644 --- a/src/events/TabManager.ts +++ b/src/events/TabManager.ts @@ -1,5 +1,6 @@ -import FolderNotesPlugin from 'src/main'; -import { App, EditableFileView, TFolder } from 'obsidian'; +import type FolderNotesPlugin from 'src/main'; +import type { App } from 'obsidian'; +import { EditableFileView, TFolder } from 'obsidian'; import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions'; export class TabManager { plugin: FolderNotesPlugin; diff --git a/src/events/handleClick.ts b/src/events/handleClick.ts index 4b3035a..fb8dc6f 100644 --- a/src/events/handleClick.ts +++ b/src/events/handleClick.ts @@ -1,5 +1,5 @@ import { Keymap, Platform } from 'obsidian'; -import FolderNotesPlugin from 'src/main'; +import type FolderNotesPlugin from 'src/main'; import { openFolderNote, createFolderNote, getFolderNote } from 'src/functions/folderNoteFunctions'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; import { addCSSClassToFileExplorerEl, removeCSSClassFromFileExplorerEL } from 'src/functions/styleFunctions'; diff --git a/src/events/handleCreate.ts b/src/events/handleCreate.ts index 0cbcaee..f65d3ed 100644 --- a/src/events/handleCreate.ts +++ b/src/events/handleCreate.ts @@ -1,5 +1,6 @@ -import { TAbstractFile, TFolder, TFile } from 'obsidian'; -import FolderNotesPlugin from 'src/main'; +import type { TAbstractFile } from 'obsidian'; +import { TFolder, TFile } from 'obsidian'; +import type FolderNotesPlugin from 'src/main'; import { createFolderNote, getFolder, getFolderNote, turnIntoFolderNote } from 'src/functions/folderNoteFunctions'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl } from 'src/functions/styleFunctions'; diff --git a/src/events/handleDelete.ts b/src/events/handleDelete.ts index b2b644c..089c1ff 100644 --- a/src/events/handleDelete.ts +++ b/src/events/handleDelete.ts @@ -1,5 +1,6 @@ -import { TAbstractFile, TFolder, TFile } from 'obsidian'; -import FolderNotesPlugin from 'src/main'; +import type { TAbstractFile } from 'obsidian'; +import { TFolder, TFile } from 'obsidian'; +import type FolderNotesPlugin from 'src/main'; import { getFolderNote, getFolder, deleteFolderNote } from 'src/functions/folderNoteFunctions'; import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl, hideFolderNoteInFileExplorer } from 'src/functions/styleFunctions'; import { getFolderPathFromString } from 'src/functions/utils'; diff --git a/src/events/handleRename.ts b/src/events/handleRename.ts index 59c79b9..7ca75b2 100644 --- a/src/events/handleRename.ts +++ b/src/events/handleRename.ts @@ -1,5 +1,6 @@ -import { TFile, TFolder, TAbstractFile, Notice } from 'obsidian'; -import FolderNotesPlugin from 'src/main'; +import type { TAbstractFile } from 'obsidian'; +import { TFile, TFolder, Notice } from 'obsidian'; +import type FolderNotesPlugin from 'src/main'; import { extractFolderName, getFolderNote, getFolderNoteFolder } from '../functions/folderNoteFunctions'; import { getExcludedFolder, addExcludedFolder, updateExcludedFolder, deleteExcludedFolder, getDetachedFolder } from '../ExcludeFolders/functions/folderFunctions'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; @@ -32,15 +33,15 @@ export function handleRename(file: TAbstractFile, oldPath: string, plugin: Folde updateExcludedFolderPath(folder, oldPath, plugin); if (isFolderRename(folder, oldPath)) { return handleFolderRename(folder, oldPath, plugin); - } else { - return handleFolderMove(folder, oldPath, plugin); } + return handleFolderMove(folder, oldPath, plugin); + } else if (file instanceof TFile) { if (isFileRename(file, oldPath)) { return fmptUpdateFileName(file, oldPath, plugin); - } else { - return handleFileMove(file, oldPath, plugin); } + return handleFileMove(file, oldPath, plugin); + } } @@ -262,7 +263,7 @@ async function renameFolderOnFileRename(file: TFile, oldPath: string, oldFolder: function updateExcludedFolderPath(folder: TFolder, oldPath: string, plugin: FolderNotesPlugin) { const excludedFolders = plugin.settings.excludeFolders.filter( - (excludedFolder) => excludedFolder.path?.includes(oldPath) + (excludedFolder) => excludedFolder.path?.includes(oldPath), ); excludedFolders.forEach((excludedFolder) => { diff --git a/src/functions/excalidraw.ts b/src/functions/excalidraw.ts index c07baca..81eb8c8 100644 --- a/src/functions/excalidraw.ts +++ b/src/functions/excalidraw.ts @@ -1,4 +1,4 @@ -import { WorkspaceLeaf, App } from 'obsidian'; +import type { WorkspaceLeaf, App } from 'obsidian'; export async function openExcalidrawView(leaf: WorkspaceLeaf) { const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(this.app); @@ -10,7 +10,7 @@ export async function openExcalidrawView(leaf: WorkspaceLeaf) { export async function getExcalidrawPlugin(app: App) { const excalidraw = (app as any).plugins.plugins['obsidian-excalidraw-plugin']; const excalidrawEnabled = (app as any).plugins.enabledPlugins.has( - 'obsidian-excalidraw-plugin' + 'obsidian-excalidraw-plugin', ); return { excalidraw, diff --git a/src/functions/folderNoteFunctions.ts b/src/functions/folderNoteFunctions.ts index 7eb78d7..17f6944 100644 --- a/src/functions/folderNoteFunctions.ts +++ b/src/functions/folderNoteFunctions.ts @@ -1,7 +1,8 @@ -import FolderNotesPlugin from '../main'; +import type FolderNotesPlugin from '../main'; import ExistingFolderNoteModal from '../modals/ExistingNote'; import { applyTemplate } from '../template'; -import { TFolder, TFile, TAbstractFile, Keymap } from 'obsidian'; +import type { TAbstractFile } from 'obsidian'; +import { TFolder, TFile, Keymap } from 'obsidian'; import DeleteConfirmationModal from '../modals/DeleteConfirmation'; import { addExcludedFolder, deleteExcludedFolder, getDetachedFolder, getExcludedFolder, updateExcludedFolder } from '../ExcludeFolders/functions/folderFunctions'; import { ExcludedFolder } from '../ExcludeFolders/ExcludeFolder'; @@ -146,7 +147,7 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st } export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile, folder: TFolder, folderNote?: TFile | null | TAbstractFile, skipConfirmation?: boolean) { - const extension = file.extension; + const { extension } = file; const detachedExcludedFolder = getDetachedFolder(plugin, folder.path); if (folderNote) { @@ -214,7 +215,7 @@ export async function tempDisableSync(plugin: FolderNotesPlugin, folder: TFolder } export async function openFolderNote(plugin: FolderNotesPlugin, file: TAbstractFile, evt?: MouseEvent) { - const path = file.path; + const { path } = file; const focusExistingTab = plugin.settings.focusExistingTab && plugin.settings.openInNewTab; const activeFilePath = plugin.app.workspace.getActiveFile()?.path; @@ -310,7 +311,7 @@ export function getFolderNote(plugin: FolderNotesPlugin, folderPath: string, sto folder.path === '/' ? path = fileName : path = `${folder.path}/${fileName}`; - let folderNoteType = plugin.settings.folderNoteType; + let { folderNoteType } = plugin.settings; if (folderNoteType === '.excalidraw') { folderNoteType = '.md'; } @@ -318,21 +319,21 @@ export function getFolderNote(plugin: FolderNotesPlugin, folderPath: string, sto let folderNote = plugin.app.vault.getAbstractFileByPath(path + folderNoteType); if (folderNote instanceof TFile && plugin.settings.supportedFileTypes.includes(plugin.settings.folderNoteType.replace('.', ''))) { return folderNote; - } else { - const supportedFileTypes = plugin.settings.supportedFileTypes.filter((type) => type !== plugin.settings.folderNoteType.replace('.', '')); - for (let type of supportedFileTypes) { - if (type === 'excalidraw' || type === '.excalidraw') { - type = '.md'; - } - if (!type.startsWith('.')) { - type = '.' + type; - } - folderNote = plugin.app.vault.getAbstractFileByPath(path + type); - if (folderNote instanceof TFile) { - return folderNote; - } + } + const supportedFileTypes = plugin.settings.supportedFileTypes.filter((type) => type !== plugin.settings.folderNoteType.replace('.', '')); + for (let type of supportedFileTypes) { + if (type === 'excalidraw' || type === '.excalidraw') { + type = '.md'; + } + if (!type.startsWith('.')) { + type = '.' + type; + } + folderNote = plugin.app.vault.getAbstractFileByPath(path + type); + if (folderNote instanceof TFile) { + return folderNote; } } + } export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile) { diff --git a/src/functions/styleFunctions.ts b/src/functions/styleFunctions.ts index 9965d35..d65d886 100644 --- a/src/functions/styleFunctions.ts +++ b/src/functions/styleFunctions.ts @@ -1,10 +1,10 @@ import { TFile, TFolder } from 'obsidian'; -import FolderNotesPlugin from '../main'; +import type FolderNotesPlugin from '../main'; import { getDetachedFolder, getExcludedFolder, addExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions'; import { getFileExplorer } from './utils'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; -import FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main'; +import type FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main'; /** * @description Refreshes the CSS classes for all folder notes in the file explorer. @@ -156,9 +156,9 @@ export function removeCSSClassFromFileExplorerEL(path: string | undefined, cssCl parentElement.removeClass(cssClass); } return; - } else { - fileExplorerItem.removeClass(cssClass); } + fileExplorerItem.removeClass(cssClass); + } export function getFileExplorerElement(path: string, plugin: FolderNotesPlugin | FolderOverviewPlugin): HTMLElement | null { diff --git a/src/functions/utils.ts b/src/functions/utils.ts index c50a5e7..468c5de 100644 --- a/src/functions/utils.ts +++ b/src/functions/utils.ts @@ -1,9 +1,9 @@ import { TFolder, TFile, View } from 'obsidian'; -import { FileExplorerWorkspaceLeaf, FileExplorerView } from 'src/globals'; +import type { FileExplorerWorkspaceLeaf, FileExplorerView } from 'src/globals'; import { getFolderNote } from './folderNoteFunctions'; -import FolderNotesPlugin from 'src/main'; -import { FileExplorerLeaf } from 'obsidian-typings'; -import FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main'; +import type FolderNotesPlugin from 'src/main'; +import type { FileExplorerLeaf } from 'obsidian-typings'; +import type FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main'; export function getFileNameFromPathString(path: string): string { return path.substring(path.lastIndexOf('/') >= 0 ? path.lastIndexOf('/') + 1 : 0); @@ -12,9 +12,9 @@ export function getFileNameFromPathString(path: string): string { export function getFolderNameFromPathString(path: string): string { if (path.endsWith('.md') || path.endsWith('.canvas')) { return path.split('/').slice(-2)[0]; - } else { - return path.split('/').slice(-1)[0]; } + return path.split('/').slice(-1)[0]; + } export function removeExtension(name: string): string { @@ -30,9 +30,9 @@ export function getFolderPathFromString(path: string): string { const folderPath = path.substring(0, subString); if (folderPath === '') { return '/'; - } else { - return folderPath; } + return folderPath; + } export function getParentFolderPath(path: string): string { @@ -49,7 +49,7 @@ export function getFileExplorerView(plugin: FolderNotesPlugin) { export function getFocusedItem(plugin: FolderNotesPlugin) { const fileExplorer = getFileExplorer(plugin) as any as FileExplorerLeaf; - const focusedItem = fileExplorer.view.tree.focusedItem; + const { focusedItem } = fileExplorer.view.tree; return focusedItem; } diff --git a/src/globals.d.ts b/src/globals.d.ts index ffe085a..1229822 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -1,4 +1,4 @@ -import { TAbstractFile, TFile, TFolder, View, WorkspaceLeaf } from 'obsidian'; +import type { TAbstractFile, TFile, TFolder, View, WorkspaceLeaf } from 'obsidian'; declare module 'obsidian' { interface Setting { diff --git a/src/main.ts b/src/main.ts index a3920c3..b72609a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,10 @@ -import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml, Notice, Keymap, WorkspaceLeaf, requireApiVersion, Platform, debounce } from 'obsidian'; -import { DEFAULT_SETTINGS, FolderNotesSettings, SettingsTab } from './settings/SettingsTab'; +import type { TAbstractFile, MarkdownPostProcessorContext, WorkspaceLeaf } from 'obsidian'; +import { Plugin, TFile, TFolder, parseYaml, Notice, Keymap, requireApiVersion, Platform, debounce } from 'obsidian'; +import type { FolderNotesSettings } from './settings/SettingsTab'; +import { DEFAULT_SETTINGS, SettingsTab } from './settings/SettingsTab'; import { Commands } from './Commands'; -import { FileExplorerWorkspaceLeaf } from './globals'; +import type { FileExplorerWorkspaceLeaf } from './globals'; import { registerFileExplorerObserver, unregisterFileExplorerObserver } from './events/MutationObserver'; import { handleRename } from './events/handleRename'; import { getFolderNote, getFolder, openFolderNote, createFolderNote } from './functions/folderNoteFunctions'; @@ -15,7 +17,7 @@ import './functions/ListComponent'; import { handleDelete } from './events/handleDelete'; import { addCSSClassToFileExplorerEl, getFileExplorerElement, removeCSSClassFromFileExplorerEL, refreshAllFolderStyles, setActiveFolder, removeActiveFolder } from './functions/styleFunctions'; import { getExcludedFolder } from './ExcludeFolders/functions/folderFunctions'; -import { FileExplorerView, InternalPlugin } from 'obsidian-typings'; +import type { FileExplorerView, InternalPlugin } from 'obsidian-typings'; // import { getFocusedItem } from './functions/utils'; import { FOLDER_OVERVIEW_VIEW, FolderOverviewView } from './obsidian-folder-overview/src/view'; import { registerOverviewCommands } from './obsidian-folder-overview/src/Commands'; @@ -89,7 +91,7 @@ export default class FolderNotesPlugin extends Plugin { // openFolderNote(this, folderNote); // } - const hoveredElement = this.hoveredElement; + const { hoveredElement } = this; if (this.hoverLinkTriggered) return; if (!hoveredElement) return; if (!Keymap.isModEvent(event)) return; @@ -212,7 +214,7 @@ export default class FolderNotesPlugin extends Plugin { const originalHandleDrop = clipboardProto.handleDrop; clipboardProto.handleDragOver = function (evt: DragEvent, ...args: any[]) { - const dragManager = this.app.dragManager; + const { dragManager } = this.app; const draggable = dragManager?.draggable; if (draggable?.file instanceof TFolder) { @@ -227,7 +229,7 @@ export default class FolderNotesPlugin extends Plugin { }; clipboardProto.handleDrop = function (evt: DragEvent, ...args: any[]) { - const dragManager = this.app.dragManager; + const { dragManager } = this.app; const draggable = dragManager?.draggable; if (draggable?.file instanceof TFolder) { @@ -383,9 +385,9 @@ export default class FolderNotesPlugin extends Plugin { } } return folder.children.length <= threshold + 1; - } else { - return false; } + return false; + } return true; } diff --git a/src/modals/AddSupportedFileType.ts b/src/modals/AddSupportedFileType.ts index 7a54f94..c4e802d 100644 --- a/src/modals/AddSupportedFileType.ts +++ b/src/modals/AddSupportedFileType.ts @@ -1,6 +1,7 @@ -import { App, Modal, Setting, Notice, SettingTab } from 'obsidian'; -import FolderNotesPlugin from '../main'; -import { ListComponent } from 'src/functions/ListComponent'; +import type { App, SettingTab } from 'obsidian'; +import { Modal, Setting, Notice } from 'obsidian'; +import type FolderNotesPlugin from '../main'; +import type { ListComponent } from 'src/functions/ListComponent'; export default class AddSupportedFileModal extends Modal { plugin: FolderNotesPlugin; @@ -34,7 +35,7 @@ export default class AddSupportedFileModal extends Modal { if (value.trim() !== '') { this.name = value.trim(); } - }) + }), ); } async onClose() { diff --git a/src/modals/AskForExtension.ts b/src/modals/AskForExtension.ts index 27811c9..50b5ee8 100644 --- a/src/modals/AskForExtension.ts +++ b/src/modals/AskForExtension.ts @@ -1,5 +1,6 @@ -import { FuzzySuggestModal, TFile } from 'obsidian'; -import FolderNotesPlugin from 'src/main'; +import type { TFile } from 'obsidian'; +import { FuzzySuggestModal } from 'obsidian'; +import type FolderNotesPlugin from 'src/main'; import { createFolderNote } from 'src/functions/folderNoteFunctions'; export class AskForExtensionModal extends FuzzySuggestModal { plugin: FolderNotesPlugin; diff --git a/src/modals/DeleteConfirmation.ts b/src/modals/DeleteConfirmation.ts index 9307745..c179ec4 100644 --- a/src/modals/DeleteConfirmation.ts +++ b/src/modals/DeleteConfirmation.ts @@ -1,5 +1,6 @@ -import { App, Modal, TFile, Platform } from 'obsidian'; -import FolderNotesPlugin from '../main'; +import type { App, TFile } from 'obsidian'; +import { Modal, Platform } from 'obsidian'; +import type FolderNotesPlugin from '../main'; import { deleteFolderNote } from 'src/functions/folderNoteFunctions'; export default class DeleteConfirmationModal extends Modal { plugin: FolderNotesPlugin; diff --git a/src/modals/ExistingNote.ts b/src/modals/ExistingNote.ts index 0babd5e..d3c680c 100644 --- a/src/modals/ExistingNote.ts +++ b/src/modals/ExistingNote.ts @@ -1,5 +1,6 @@ -import { App, Modal, Setting, TFile, Platform, TFolder, TAbstractFile } from 'obsidian'; -import FolderNotesPlugin from '../main'; +import type { App, TFile, TFolder, TAbstractFile } from 'obsidian'; +import { Modal, Setting, Platform } from 'obsidian'; +import type FolderNotesPlugin from '../main'; import { turnIntoFolderNote } from 'src/functions/folderNoteFunctions'; export default class ExistingFolderNoteModal extends Modal { plugin: FolderNotesPlugin; diff --git a/src/modals/FolderName.ts b/src/modals/FolderName.ts index 77f6c1c..bdef456 100644 --- a/src/modals/FolderName.ts +++ b/src/modals/FolderName.ts @@ -1,5 +1,6 @@ -import { App, Modal, Setting, TFolder } from 'obsidian'; -import FolderNotesPlugin from '../main'; +import type { App, TFolder } from 'obsidian'; +import { Modal, Setting } from 'obsidian'; +import type FolderNotesPlugin from '../main'; export default class FolderNameModal extends Modal { plugin: FolderNotesPlugin; app: App; @@ -30,7 +31,7 @@ export default class FolderNameModal extends Modal { this.plugin.app.fileManager.renameFile(this.folder, this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + value.trim()); } } - }) + }), ); } onClose() { diff --git a/src/modals/NewFolderName.ts b/src/modals/NewFolderName.ts index 59a35bf..64fa454 100644 --- a/src/modals/NewFolderName.ts +++ b/src/modals/NewFolderName.ts @@ -1,5 +1,6 @@ -import { App, Modal, TFolder } from 'obsidian'; -import FolderNotesPlugin from '../main'; +import type { App, TFolder } from 'obsidian'; +import { Modal } from 'obsidian'; +import type FolderNotesPlugin from '../main'; export default class NewFolderNameModal extends Modal { plugin: FolderNotesPlugin; app: App; diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index f1d2495..07825d6 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit f1d24952999f23e2912c7e6c2da4bd53325d2402 +Subproject commit 07825d6f187a9ddf2c9b29066eb0c06aa8b8fee2 diff --git a/src/settings/ExcludedFoldersSettings.ts b/src/settings/ExcludedFoldersSettings.ts index 6c21b86..e4126fb 100644 --- a/src/settings/ExcludedFoldersSettings.ts +++ b/src/settings/ExcludedFoldersSettings.ts @@ -2,7 +2,7 @@ import { addExcludeFolderListItem, addExcludedFolder } from 'src/ExcludeFolders/ import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; import { addExcludePatternListItem } from 'src/ExcludeFolders/functions/patternFunctions'; import { Setting } from 'obsidian'; -import { SettingsTab } from './SettingsTab'; +import type { SettingsTab } from './SettingsTab'; import ExcludedFolderSettings from 'src/ExcludeFolders/modals/ExcludeFolderSettings'; import PatternSettings from 'src/ExcludeFolders/modals/PatternSettings'; import WhitelistedFoldersSettings from 'src/ExcludeFolders/modals/WhitelistedFoldersSettings'; diff --git a/src/settings/FileExplorerSettings.ts b/src/settings/FileExplorerSettings.ts index e408014..0d79841 100644 --- a/src/settings/FileExplorerSettings.ts +++ b/src/settings/FileExplorerSettings.ts @@ -1,5 +1,5 @@ import { Setting } from 'obsidian'; -import { SettingsTab } from './SettingsTab'; +import type { SettingsTab } from './SettingsTab'; export async function renderFileExplorer(settingsTab: SettingsTab) { const containerEl = settingsTab.settingsPage; @@ -18,7 +18,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { document.body.classList.remove('hide-folder-note'); } settingsTab.display(); - }) + }), ); const setting2 = new Setting(containerEl) @@ -30,7 +30,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { .onChange(async (value) => { settingsTab.plugin.settings.disableOpenFolderNoteOnClick = value; await settingsTab.plugin.saveSettings(); - }) + }), ); setting2.infoEl.appendText('Requires a restart to take effect'); @@ -50,7 +50,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { } settingsTab.plugin.settings.stopWhitespaceCollapsing = !value; await settingsTab.plugin.saveSettings(); - }) + }), ); const disableSetting = new Setting(containerEl); @@ -62,7 +62,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { .onChange(async (value) => { settingsTab.plugin.settings.enableCollapsing = !value; await settingsTab.plugin.saveSettings(); - }) + }), ); disableSetting.infoEl.appendText('Requires a restart to take effect'); disableSetting.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; @@ -77,7 +77,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { settingsTab.plugin.settings.useSubmenus = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); if (settingsTab.plugin.settings.frontMatterTitle.enabled) { @@ -93,7 +93,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { settingsTab.plugin.app.vault.getFiles().forEach((file) => { settingsTab.plugin.fmtpHandler?.fmptUpdateFileName({ id: '', result: false, path: file.path, pathOnly: false }, false); }); - }) + }), ); } @@ -113,7 +113,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { document.body.classList.remove('disable-folder-highlight'); } await settingsTab.plugin.saveSettings(); - }) + }), ); new Setting(containerEl) @@ -131,7 +131,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { document.body.classList.remove('fn-hide-collapse-icon'); } settingsTab.display(); - }) + }), ); new Setting(containerEl) @@ -149,7 +149,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { document.body.classList.remove('fn-hide-empty-collapse-icon'); } settingsTab.display(); - } + }, )); if (settingsTab.plugin.settings.hideCollapsingIcon) { @@ -161,7 +161,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { .onChange(async (value) => { settingsTab.plugin.settings.ignoreAttachmentFolder = value; await settingsTab.plugin.saveSettings(); - }) + }), ); } @@ -179,7 +179,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { document.body.classList.remove('folder-note-underline'); } await settingsTab.plugin.saveSettings(); - }) + }), ); new Setting(containerEl) @@ -196,7 +196,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { document.body.classList.remove('folder-note-bold'); } await settingsTab.plugin.saveSettings(); - }) + }), ); new Setting(containerEl) @@ -213,7 +213,7 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { document.body.classList.remove('folder-note-cursive'); } await settingsTab.plugin.saveSettings(); - }) + }), ); } diff --git a/src/settings/FolderOverviewSettings.ts b/src/settings/FolderOverviewSettings.ts index b06c6d4..310fb1e 100644 --- a/src/settings/FolderOverviewSettings.ts +++ b/src/settings/FolderOverviewSettings.ts @@ -1,5 +1,5 @@ import { Setting } from 'obsidian'; -import { SettingsTab } from './SettingsTab'; +import type { SettingsTab } from './SettingsTab'; import { createOverviewSettings } from 'src/obsidian-folder-overview/src/settings'; export async function renderFolderOverview(settingsTab: SettingsTab) { @@ -22,7 +22,7 @@ export async function renderFolderOverview(settingsTab: SettingsTab) { } else { plugin.fvIndexDB.active = false; } - }) + }), ); containerEl.createEl('h3', { text: 'Overviews default settings' }); diff --git a/src/settings/GeneralSettings.ts b/src/settings/GeneralSettings.ts index 495361e..3a15b8e 100644 --- a/src/settings/GeneralSettings.ts +++ b/src/settings/GeneralSettings.ts @@ -1,5 +1,5 @@ import { Setting, Platform } from 'obsidian'; -import { SettingsTab } from './SettingsTab'; +import type { SettingsTab } from './SettingsTab'; import { ListComponent } from '../functions/ListComponent'; import AddSupportedFileModal from '../modals/AddSupportedFileType'; import { FrontMatterTitlePluginHandler } from '../events/FrontMatterTitle'; @@ -38,7 +38,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { } } }, 2000); - }) + }), ) .addButton((button) => button @@ -52,7 +52,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.renameFolderNotes, []) .open(); - }) + }), ); nameSetting.infoEl.appendText('Requires a restart to take effect'); nameSetting.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; @@ -74,7 +74,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.tabManagerEnabled = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); } @@ -115,7 +115,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { setting0.setName('Supported file types'); const desc0 = document.createDocumentFragment(); desc0.append( - 'Specify which file types are allowed as folder notes. Applies to both new and existing folders. Adding many types may affect performance.' + 'Specify which file types are allowed as folder notes. Applies to both new and existing folders. Adding many types may affect performance.', ); setting0.setDesc(desc0); const list = new ListComponent(setting0.settingEl, settingsTab.plugin.settings.supportedFileTypes || [], ['md', 'canvas']); @@ -158,7 +158,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { .setCta() .onClick(async () => { new AddSupportedFileModal(settingsTab.app, settingsTab.plugin, settingsTab, list as ListComponent).open(); - }) + }), ); } @@ -195,7 +195,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { await settingsTab.plugin.saveSettings(); settingsTab.display(); refreshAllFolderStyles(undefined, settingsTab.plugin); - }) + }), ) .addButton((button) => button @@ -213,9 +213,9 @@ export async function renderGeneral(settingsTab: SettingsTab) { 'Switch storage location', 'When you click on "Confirm" all folder notes will be moved to the new storage location.', settingsTab.switchStorageLocation, - [oldStorageLocation] + [oldStorageLocation], ).open(); - }) + }), ); storageLocation.infoEl.appendText('Requires a restart to take effect'); storageLocation.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; @@ -230,8 +230,8 @@ export async function renderGeneral(settingsTab: SettingsTab) { .onChange(async (value) => { settingsTab.plugin.settings.syncDelete = value; await settingsTab.plugin.saveSettings(); - } - ) + }, + ), ); new Setting(containerEl) .setName('Move folder notes when moving the folder') @@ -242,7 +242,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { .onChange(async (value) => { settingsTab.plugin.settings.syncMove = value; await settingsTab.plugin.saveSettings(); - }) + }), ); } if (Platform.isDesktopApp) { @@ -309,7 +309,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.showDeleteConfirmation = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); new Setting(containerEl) @@ -338,7 +338,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.openInNewTab = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); setting3.infoEl.appendText('Requires a restart to take effect'); setting3.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; @@ -355,7 +355,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.focusExistingTab = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); } @@ -369,7 +369,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.syncFolderName = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); settingsTab.settingsPage.createEl('h4', { text: 'Automation settings' }); @@ -395,7 +395,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.autoCreate = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); if (settingsTab.plugin.settings.autoCreate) { @@ -409,7 +409,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.autoCreateFocusFiles = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); new Setting(containerEl) @@ -422,7 +422,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.autoCreateForAttachmentFolder = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); } @@ -436,7 +436,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.autoCreateForFiles = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); settingsTab.settingsPage.createEl('h3', { text: 'Integration & Compatibility' }); @@ -476,7 +476,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(settingsTab.plugin); } settingsTab.display(); - }) + }), ); fmtpSetting.infoEl.appendText('Requires a restart to take effect'); fmtpSetting.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; @@ -493,7 +493,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.persistentSettingsTab.afterRestart = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); new Setting(containerEl) @@ -506,6 +506,6 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.persistentSettingsTab.afterChangingTab = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); } diff --git a/src/settings/PathSettings.ts b/src/settings/PathSettings.ts index eb8499f..3ea8be2 100644 --- a/src/settings/PathSettings.ts +++ b/src/settings/PathSettings.ts @@ -1,5 +1,5 @@ import { Setting } from 'obsidian'; -import { SettingsTab } from './SettingsTab'; +import type { SettingsTab } from './SettingsTab'; export async function renderPath(settingsTab: SettingsTab) { const containerEl = settingsTab.settingsPage; new Setting(containerEl) @@ -12,7 +12,7 @@ export async function renderPath(settingsTab: SettingsTab) { settingsTab.plugin.settings.openFolderNoteOnClickInPath = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - }) + }), ); if (settingsTab.plugin.settings.openFolderNoteOnClickInPath) { @@ -25,7 +25,7 @@ export async function renderPath(settingsTab: SettingsTab) { .onChange(async (value) => { settingsTab.plugin.settings.openSidebar.mobile = value; await settingsTab.plugin.saveSettings(); - }) + }), ); new Setting(containerEl) @@ -37,7 +37,7 @@ export async function renderPath(settingsTab: SettingsTab) { .onChange(async (value) => { settingsTab.plugin.settings.openSidebar.desktop = value; await settingsTab.plugin.saveSettings(); - }) + }), ); } @@ -56,7 +56,7 @@ export async function renderPath(settingsTab: SettingsTab) { } else { settingsTab.plugin.updateAllBreadcrumbs(true); } - }) + }), ); } @@ -76,7 +76,7 @@ export async function renderPath(settingsTab: SettingsTab) { document.body.classList.remove('folder-note-underline-path'); } await settingsTab.plugin.saveSettings(); - }) + }), ); new Setting(containerEl) @@ -93,7 +93,7 @@ export async function renderPath(settingsTab: SettingsTab) { document.body.classList.remove('folder-note-bold-path'); } await settingsTab.plugin.saveSettings(); - }) + }), ); new Setting(containerEl) @@ -110,6 +110,6 @@ export async function renderPath(settingsTab: SettingsTab) { document.body.classList.remove('folder-note-cursive-path'); } await settingsTab.plugin.saveSettings(); - }) + }), ); } diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index 15d62ab..e6723c8 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -1,17 +1,18 @@ -import { App, Notice, PluginSettingTab, TFile, TFolder, MarkdownPostProcessorContext } from 'obsidian'; -import FolderNotesPlugin from '../main'; -import { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern'; -import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; +import type { App, MarkdownPostProcessorContext } from 'obsidian'; +import { Notice, PluginSettingTab, TFile, TFolder } from 'obsidian'; +import type FolderNotesPlugin from '../main'; +import type { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern'; +import type { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; import { extractFolderName, getFolderNote } from '../functions/folderNoteFunctions'; -import { defaultOverviewSettings } from '../obsidian-folder-overview/src/FolderOverview'; +import type { defaultOverviewSettings } from '../obsidian-folder-overview/src/FolderOverview'; import { renderGeneral } from './GeneralSettings'; import { renderFileExplorer } from './FileExplorerSettings'; import { renderPath } from './PathSettings'; import { renderFolderOverview } from './FolderOverviewSettings'; import { renderExcludeFolders } from './ExcludedFoldersSettings'; import { getFolderPathFromString } from '../functions/utils'; -import { WhitelistedFolder } from 'src/ExcludeFolders/WhitelistFolder'; -import { WhitelistedPattern } from 'src/ExcludeFolders/WhitelistPattern'; +import type { WhitelistedFolder } from 'src/ExcludeFolders/WhitelistFolder'; +import type { WhitelistedPattern } from 'src/ExcludeFolders/WhitelistPattern'; export interface FolderNotesSettings { syncFolderName: boolean; @@ -348,10 +349,10 @@ export class SettingsTab extends PluginSettingTab { } else if (this.plugin.settings.storageLocation === 'insideFolder') { if (getFolderPathFromString(folderNote.path) === file.path) { return; - } else { - const newPath = `${file.path}/${folderNote.name}`; - this.plugin.app.fileManager.renameFile(folderNote, newPath); } + const newPath = `${file.path}/${folderNote.name}`; + this.plugin.app.fileManager.renameFile(folderNote, newPath); + } } } diff --git a/src/settings/modals/BackupWarning.ts b/src/settings/modals/BackupWarning.ts index 98db472..a0deb27 100644 --- a/src/settings/modals/BackupWarning.ts +++ b/src/settings/modals/BackupWarning.ts @@ -1,5 +1,5 @@ import { Modal, ButtonComponent } from 'obsidian'; -import FolderNotesPlugin from 'src/main'; +import type FolderNotesPlugin from 'src/main'; export default class BackupWarningModal extends Modal { plugin: FolderNotesPlugin; diff --git a/src/settings/modals/CreateFnForEveryFolder.ts b/src/settings/modals/CreateFnForEveryFolder.ts index c3e719a..0380886 100644 --- a/src/settings/modals/CreateFnForEveryFolder.ts +++ b/src/settings/modals/CreateFnForEveryFolder.ts @@ -1,5 +1,6 @@ -import { App, ButtonComponent, Modal, Setting, TFolder, Notice } from 'obsidian'; -import FolderNotesPlugin from '../../main'; +import type { App, ButtonComponent } from 'obsidian'; +import { Modal, Setting, TFolder, Notice } from 'obsidian'; +import type FolderNotesPlugin from '../../main'; import { createFolderNote, getFolderNote } from 'src/functions/folderNoteFunctions'; import { getTemplatePlugins } from 'src/template'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; @@ -46,7 +47,7 @@ export default class ConfirmationModal extends Modal { cb.onChange(async (value) => { this.extension = value; }); - } + }, ); } new Setting(contentEl) diff --git a/src/settings/modals/RenameFns.ts b/src/settings/modals/RenameFns.ts index f4f6667..56cd925 100644 --- a/src/settings/modals/RenameFns.ts +++ b/src/settings/modals/RenameFns.ts @@ -1,5 +1,5 @@ import BackupWarningModal from './BackupWarning'; -import FolderNotesPlugin from 'src/main'; +import type FolderNotesPlugin from 'src/main'; import { Setting } from 'obsidian'; export default class RenameFolderNotesModal extends BackupWarningModal { @@ -17,7 +17,7 @@ export default class RenameFolderNotesModal extends BackupWarningModal { .setValue(this.plugin.settings.oldFolderNoteName || '') .onChange(async (value) => { this.plugin.settings.oldFolderNoteName = value; - }) + }), ); new Setting(contentEl) @@ -29,7 +29,7 @@ export default class RenameFolderNotesModal extends BackupWarningModal { .onChange(async (value) => { this.plugin.settings.folderNoteName = value; this.plugin.settingsTab.display(); - }) + }), ); } } diff --git a/src/suggesters/FileSuggester.ts b/src/suggesters/FileSuggester.ts index 09777da..941ba8d 100644 --- a/src/suggesters/FileSuggester.ts +++ b/src/suggesters/FileSuggester.ts @@ -1,8 +1,9 @@ // Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater -import { TAbstractFile, TFile } from 'obsidian'; +import type { TAbstractFile } from 'obsidian'; +import { TFile } from 'obsidian'; import { TextInputSuggest } from './Suggest'; -import FolderNotesPlugin from '../main'; +import type FolderNotesPlugin from '../main'; export enum FileSuggestMode { TemplateFiles, ScriptFiles, @@ -11,7 +12,7 @@ export enum FileSuggestMode { export class FileSuggest extends TextInputSuggest { constructor( public inputEl: HTMLInputElement, - plugin: FolderNotesPlugin + plugin: FolderNotesPlugin, ) { super(inputEl, plugin); } diff --git a/src/suggesters/FolderSuggester.ts b/src/suggesters/FolderSuggester.ts index 4b156d5..e3d07b6 100644 --- a/src/suggesters/FolderSuggester.ts +++ b/src/suggesters/FolderSuggester.ts @@ -1,8 +1,9 @@ // Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater -import { TAbstractFile, TFolder } from 'obsidian'; +import type { TAbstractFile } from 'obsidian'; +import { TFolder } from 'obsidian'; import { TextInputSuggest } from './Suggest'; -import FolderNotesPlugin from '../main'; +import type FolderNotesPlugin from '../main'; export enum FileSuggestMode { TemplateFiles, ScriptFiles, diff --git a/src/suggesters/Suggest.ts b/src/suggesters/Suggest.ts index fa92e75..4a3fb02 100644 --- a/src/suggesters/Suggest.ts +++ b/src/suggesters/Suggest.ts @@ -1,9 +1,11 @@ // Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater -import { ISuggestOwner, Scope } from 'obsidian'; -import { createPopper, Instance as PopperInstance } from '@popperjs/core'; -import FolderNotesPlugin from 'src/main'; +import type { ISuggestOwner } from 'obsidian'; +import { Scope } from 'obsidian'; +import type { Instance as PopperInstance } from '@popperjs/core'; +import { createPopper } from '@popperjs/core'; +import type FolderNotesPlugin from 'src/main'; const wrapAround = (value: number, size: number): number => { return ((value % size) + size) % size; @@ -20,7 +22,7 @@ class Suggest { constructor( owner: ISuggestOwner, containerEl: HTMLElement, - scope: Scope + scope: Scope, ) { this.owner = owner; this.containerEl = containerEl; @@ -28,12 +30,12 @@ class Suggest { containerEl.on( 'click', '.suggestion-item', - this.onSuggestionClick.bind(this) + this.onSuggestionClick.bind(this), ); containerEl.on( 'mousemove', '.suggestion-item', - this.onSuggestionMouseover.bind(this) + this.onSuggestionMouseover.bind(this), ); scope.register([], 'ArrowUp', (event) => { @@ -96,7 +98,7 @@ class Suggest { setSelectedItem(selectedIndex: number, scrollIntoView: boolean) { const normalizedIndex = wrapAround( selectedIndex, - this.suggestions.length + this.suggestions.length, ); const prevSelectedSuggestion = this.suggestions[this.selectedItem]; const selectedSuggestion = this.suggestions[normalizedIndex]; @@ -140,7 +142,7 @@ export abstract class TextInputSuggest implements ISuggestOwner { '.suggestion-container', (event: MouseEvent) => { event.preventDefault(); - } + }, ); } @@ -163,7 +165,7 @@ export abstract class TextInputSuggest implements ISuggestOwner { } open(container: HTMLElement, inputEl: HTMLElement): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.plugin.app.keymap.pushScope(this.scope); container.appendChild(this.suggestEl); diff --git a/src/suggesters/TemplateSuggester.ts b/src/suggesters/TemplateSuggester.ts index 7290f62..0e26932 100644 --- a/src/suggesters/TemplateSuggester.ts +++ b/src/suggesters/TemplateSuggester.ts @@ -1,5 +1,6 @@ -import { TAbstractFile, TFile, TFolder, Vault, AbstractInputSuggest } from 'obsidian'; -import FolderNotesPlugin from '../main'; +import type { TAbstractFile } from 'obsidian'; +import { TFile, TFolder, Vault, AbstractInputSuggest } from 'obsidian'; +import type FolderNotesPlugin from '../main'; import { getTemplatePlugins } from 'src/template'; export enum FileSuggestMode { TemplateFiles, @@ -9,7 +10,7 @@ export enum FileSuggestMode { export class TemplateSuggest extends AbstractInputSuggest { constructor( public inputEl: HTMLInputElement, - public plugin: FolderNotesPlugin + public plugin: FolderNotesPlugin, ) { super(plugin.app, inputEl); } @@ -32,13 +33,13 @@ export class TemplateSuggest extends AbstractInputSuggest { if ((!templateFolder || templateFolder.trim() === '') && !templaterPlugin) { files = this.plugin.app.vault.getFiles().filter((file) => - file.path.toLowerCase().includes(lower_input_str) + file.path.toLowerCase().includes(lower_input_str), ); } else { let folder: TFolder | TAbstractFile | null = null; if (templaterPlugin) { folder = this.plugin.app.vault.getAbstractFileByPath( - templaterPlugin.plugin?.settings?.templates_folder as string + templaterPlugin.plugin?.settings?.templates_folder as string, ); if (!(folder instanceof TFolder)) { return [{ path: '', name: 'You need to set the Templates folder in the Templater settings first.' } as TFile]; diff --git a/src/template.ts b/src/template.ts index 7d6914e..4855c52 100644 --- a/src/template.ts +++ b/src/template.ts @@ -1,11 +1,12 @@ -import { TFile, App, WorkspaceLeaf } from 'obsidian'; -import FolderNotesPlugin from './main'; +import type { App } from 'obsidian'; +import { TFile, WorkspaceLeaf } from 'obsidian'; +import type FolderNotesPlugin from './main'; export async function applyTemplate( plugin: FolderNotesPlugin, file: TFile, leaf?: WorkspaceLeaf | null, - templatePath?: string + templatePath?: string, ) { const fileContent = await plugin.app.vault.read(file).catch((err) => { console.error(`Error reading file ${file.path}:`, err); @@ -37,9 +38,9 @@ export async function applyTemplate( await leaf.openFile(file); } return await templatesPlugin.instance.insertTemplate(templateFile); - } else { - await plugin.app.vault.modify(file, templateContent); } + await plugin.app.vault.modify(file, templateContent); + } catch (e) { console.error(e); From 8adf6186ac159d4b3e8cdb5aff1a1a452d5000d8 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:37:35 +0200 Subject: [PATCH 36/60] Fix linter issues for the events folder --- eslint.config.mts | 11 +- src/events/EventEmitter.ts | 8 +- src/events/FrontMatterTitle.ts | 74 +++++--- src/events/MutationObserver.ts | 54 ++++-- src/events/TabManager.ts | 14 +- src/events/handleClick.ts | 92 ++++++--- src/events/handleCreate.ts | 21 +- src/events/handleDelete.ts | 25 ++- src/events/handleRename.ts | 327 +++++++++++++++++++++++--------- src/main.ts | 13 +- src/obsidian-folder-overview | 2 +- src/suggesters/FileSuggester.ts | 5 +- src/template.ts | 3 +- 13 files changed, 437 insertions(+), 212 deletions(-) diff --git a/eslint.config.mts b/eslint.config.mts index bb047f9..fcf59e4 100644 --- a/eslint.config.mts +++ b/eslint.config.mts @@ -139,18 +139,23 @@ export default [ 'no-empty-function': 'warn', 'complexity': [ 'warn', - 10 + 15 ], 'max-len': [ 'warn', { - code: 100 + code: 100, + ignoreComments: true, } ], 'no-inline-comments': 'warn', 'no-magic-numbers': [ 'warn', { ignore: [0, 1], - enforceConst: true + enforceConst: true, + ignoreTypeIndexes: true, + ignoreEnums: true, + ignoreNumericLiteralTypes: true, + ignoreClassFieldInitialValues: true, } ], } diff --git a/src/events/EventEmitter.ts b/src/events/EventEmitter.ts index a5ba33e..607fb07 100644 --- a/src/events/EventEmitter.ts +++ b/src/events/EventEmitter.ts @@ -1,20 +1,20 @@ export class CustomEventEmitter { - private events: { [key: string]: Array<(data?: any) => void> } = {}; + private events: { [key: string]: Array<(data?: unknown) => void> } = {}; - on(event: string, listener: (data?: any) => void) { + on(event: string, listener: (data?: unknown) => void): void { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(listener); } - off(event: string, listener: (data?: any) => void) { + off(event: string, listener: (data?: unknown) => void): void { if (!this.events[event]) return; this.events[event] = this.events[event].filter((l) => l !== listener); } - emit(event: string, data?: any) { + emit(event: string, data?: unknown): void { if (!this.events[event]) return; this.events[event].forEach((listener) => listener(data)); diff --git a/src/events/FrontMatterTitle.ts b/src/events/FrontMatterTitle.ts index 3586346..e694c87 100644 --- a/src/events/FrontMatterTitle.ts +++ b/src/events/FrontMatterTitle.ts @@ -1,9 +1,28 @@ import type FolderNotesPlugin from 'src/main'; -import type { Listener, Events, ApiInterface, DeferInterface, ListenerRef, EventDispatcherInterface } from 'front-matter-plugin-api-provider'; -import { getDefer } from 'front-matter-plugin-api-provider'; -import type { App } from 'obsidian'; -import { TFile, TFolder } from 'obsidian'; +import { + type Listener, + type Events, + type ApiInterface, + type DeferInterface, + type ListenerRef, + type EventDispatcherInterface, + getDefer, +} from 'front-matter-plugin-api-provider'; +import { type App, TFile, TFolder } from 'obsidian'; import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions'; + +interface UpdateData { + id: string; + result: boolean; + path: string; + pathOnly: boolean; + breadcrumb?: HTMLElement; +} + +interface WrappedUpdateData { + data: UpdateData; +} + export class FrontMatterTitlePluginHandler { plugin: FolderNotesPlugin; app: App; @@ -16,7 +35,7 @@ export class FrontMatterTitlePluginHandler { this.plugin = plugin; this.app = plugin.app; - (async () => { + (async (): Promise => { this.deffer = getDefer(this.app); if (this.deffer.isPluginReady()) { this.api = this.deffer.getApi(); @@ -35,8 +54,8 @@ export class FrontMatterTitlePluginHandler { } const event: Listener = { name: 'manager:update', - cb: (data) => { - this.fmptUpdateFileName(data as any, true); + cb: (data): void => { + this.fmptUpdateFileName(data as unknown as UpdateData, true); }, }; // Keep ref to remove listener @@ -51,20 +70,18 @@ export class FrontMatterTitlePluginHandler { } })(); } - deleteEvent() { + + deleteEvent(): void { if (this.eventRef) { this.dispatcher.removeListener(this.eventRef); } } - async fmptUpdateFileName(data: { - id: string; - result: boolean; - path: string; - pathOnly: boolean; - breadcrumb?: HTMLElement; - }, isEvent: boolean) { - if ((data as any).data) data = (data as any).data; - const file = this.app.vault.getAbstractFileByPath(data.path); + async fmptUpdateFileName(data: UpdateData, isEvent: boolean): Promise { + const hasNestedData = 'data' in (data as unknown as Record); + const actualData: UpdateData = hasNestedData + ? (data as unknown as WrappedUpdateData).data + : data; + const file = this.app.vault.getAbstractFileByPath(actualData.path); if (!(file instanceof TFile)) { return; } const resolver = this.api?.getResolverFactory()?.createResolver('#feature-id#'); @@ -75,11 +92,11 @@ export class FrontMatterTitlePluginHandler { const folderNote = getFolderNote(this.plugin, folder.path); if (!folderNote) { return; } if (folderNote !== file) { return; } - if (!data.pathOnly) { + if (!actualData.pathOnly) { this.plugin.changeFolderNameInExplorer(folder, newName); } - const { breadcrumb } = data; + const { breadcrumb } = actualData; if (breadcrumb) { this.plugin.changeFolderNameInPath(folder, newName, breadcrumb); } @@ -98,15 +115,12 @@ export class FrontMatterTitlePluginHandler { } - async fmptUpdateFolderName(data: { - id: string; - result: boolean; - path: string; - pathOnly: boolean; - breadcrumb?: HTMLElement; - }, replacePath: boolean) { - if ((data as any).data) data = (data as any).data; - const folder = this.app.vault.getAbstractFileByPath(data.path); + async fmptUpdateFolderName(data: UpdateData, _replacePath: boolean): Promise { + const hasNestedData = 'data' in (data as unknown as Record); + const actualData: UpdateData = hasNestedData + ? (data as unknown as WrappedUpdateData).data + : data; + const folder = this.app.vault.getAbstractFileByPath(actualData.path); if (!(folder instanceof TFolder)) { return; } const folderNote = getFolderNote(this.plugin, folder.path); if (!folderNote) { return; } @@ -115,11 +129,11 @@ export class FrontMatterTitlePluginHandler { const newName = resolver?.resolve(folderNote?.path ?? ''); if (!newName) return; - if (!data.pathOnly) { + if (!actualData.pathOnly) { this.plugin.changeFolderNameInExplorer(folder, newName); } - const { breadcrumb } = data; + const { breadcrumb } = actualData; if (breadcrumb) { this.plugin.changeFolderNameInPath(folder, newName, breadcrumb); } diff --git a/src/events/MutationObserver.ts b/src/events/MutationObserver.ts index a03a5e0..1a6a01d 100644 --- a/src/events/MutationObserver.ts +++ b/src/events/MutationObserver.ts @@ -7,7 +7,7 @@ import { updateCSSClassesForFolder } from 'src/functions/styleFunctions'; let fileExplorerMutationObserver: MutationObserver | null = null; -export function registerFileExplorerObserver(plugin: FolderNotesPlugin) { +export function registerFileExplorerObserver(plugin: FolderNotesPlugin): void { // Run once on initial layout plugin.app.workspace.onLayoutReady(() => { initializeFolderNoteFeatures(plugin); @@ -30,19 +30,19 @@ export function registerFileExplorerObserver(plugin: FolderNotesPlugin) { ); } -export function unregisterFileExplorerObserver() { +export function unregisterFileExplorerObserver(): void { if (fileExplorerMutationObserver) { fileExplorerMutationObserver.disconnect(); fileExplorerMutationObserver = null; } } -function initializeFolderNoteFeatures(plugin: FolderNotesPlugin) { +function initializeFolderNoteFeatures(plugin: FolderNotesPlugin): void { initializeAllFolderTitles(plugin); observeFolderTitleMutations(plugin); } -function initializeBreadcrumbs(plugin: FolderNotesPlugin) { +function initializeBreadcrumbs(plugin: FolderNotesPlugin): void { const titleContainers = document.querySelectorAll('.view-header-title-container'); if (!titleContainers.length) return; titleContainers.forEach((container) => { @@ -55,7 +55,7 @@ function initializeBreadcrumbs(plugin: FolderNotesPlugin) { * Observes the File Explorer for newly added folder elements and applies plugin logic (e.g., styles, event listeners) * automatically when folders are created, expanded, or when the File Explorer view is reopened. */ -function observeFolderTitleMutations(plugin: FolderNotesPlugin) { +function observeFolderTitleMutations(plugin: FolderNotesPlugin): void { if (fileExplorerMutationObserver) { fileExplorerMutationObserver.disconnect(); } @@ -71,7 +71,7 @@ function observeFolderTitleMutations(plugin: FolderNotesPlugin) { fileExplorerMutationObserver.observe(document, { childList: true, subtree: true }); } -function initializeAllFolderTitles(plugin: FolderNotesPlugin) { +function initializeAllFolderTitles(plugin: FolderNotesPlugin): void { const allTitles = document.querySelectorAll('.nav-folder-title-content'); for (const title of Array.from(allTitles)) { const folderTitle = title as HTMLElement; @@ -83,7 +83,7 @@ function initializeAllFolderTitles(plugin: FolderNotesPlugin) { } } -function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin) { +function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin): void { const titles: HTMLElement[] = []; if (node.matches('.nav-folder-title-content')) { titles.push(node); @@ -95,6 +95,7 @@ function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin) { titles.forEach((folderTitle) => { const folderEl = folderTitle.closest('.nav-folder-title'); const folderPath = folderEl?.getAttribute('data-path') || ''; + const RETRY_TIMEOUT = 50; if (!folderEl || !folderPath) { setTimeout(() => { const retryFolderEl = folderTitle.closest('.nav-folder-title'); @@ -102,14 +103,18 @@ function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin) { if (retryFolderEl && retryFolderPath) { setupFolderTitle(folderTitle, plugin, retryFolderPath); } - }, 50); + }, RETRY_TIMEOUT); return; } setupFolderTitle(folderTitle, plugin, folderPath); }); } -async function setupFolderTitle(folderTitle: HTMLElement, plugin: FolderNotesPlugin, folderPath: string) { +async function setupFolderTitle( + folderTitle: HTMLElement, + plugin: FolderNotesPlugin, + folderPath: string, +): Promise { if (folderTitle.dataset.initialized === 'true') return; if (!folderPath) return; @@ -117,7 +122,10 @@ async function setupFolderTitle(folderTitle: HTMLElement, plugin: FolderNotesPlu await updateCSSClassesForFolder(folderPath, plugin); if (plugin.settings.frontMatterTitle.enabled) { - plugin.fmtpHandler?.fmptUpdateFolderName({ id: '', result: false, path: folderPath, pathOnly: false }, false); + plugin.fmtpHandler?.fmptUpdateFolderName( + { id: '', result: false, path: folderPath, pathOnly: false }, + false, + ); } if (Platform.isMobile && plugin.settings.disableOpenFolderNoteOnClick) return; @@ -150,21 +158,24 @@ async function setupFolderTitle(folderTitle: HTMLElement, plugin: FolderNotesPlu }); } -async function updateFolderNamesInPath(plugin: FolderNotesPlugin, titleContainer: HTMLElement) { +async function updateFolderNamesInPath( + plugin: FolderNotesPlugin, + titleContainer: HTMLElement, +): Promise { const headers = titleContainer.querySelectorAll('span.view-header-breadcrumb'); let path = ''; + const TRAILING_SLASH_LENGTH = 1; headers.forEach(async (breadcrumb: HTMLElement) => { path += breadcrumb.getAttribute('old-name') ?? (breadcrumb as HTMLElement).innerText.trim(); path += '/'; - const folderPath = path.slice(0, -1); + const folderPath = path.slice(0, -TRAILING_SLASH_LENGTH); const excludedFolder = getExcludedFolder(plugin, folderPath, true); if (excludedFolder?.disableFolderNote) return; const folderNote = getFolderNote(plugin, folderPath); if (!folderNote) return; if (folderNote) breadcrumb.classList.add('has-folder-note'); - - breadcrumb?.setAttribute('data-path', path.slice(0, -1)); + breadcrumb?.setAttribute('data-path', path.slice(0, -TRAILING_SLASH_LENGTH)); if (!breadcrumb.onclick) { breadcrumb.addEventListener('click', (e) => { handleViewHeaderClick(e as MouseEvent, plugin); @@ -172,7 +183,10 @@ async function updateFolderNamesInPath(plugin: FolderNotesPlugin, titleContainer } if (plugin.settings.frontMatterTitle.enabled) { - plugin.fmtpHandler?.fmptUpdateFolderName({ id: '', result: false, path: folderPath, pathOnly: true, breadcrumb: breadcrumb }, true); + plugin.fmtpHandler?.fmptUpdateFolderName( + { id: '', result: false, path: folderPath, pathOnly: true, breadcrumb: breadcrumb }, + true, + ); } }); } @@ -180,10 +194,14 @@ async function updateFolderNamesInPath(plugin: FolderNotesPlugin, titleContainer // Schedules a callback to run when the browser is idle, or after a timeout as a fallback. // - callback: The function to execute when idle or after the timeout. // - options: Optional object with a 'timeout' property (in milliseconds). -function scheduleIdle(callback: () => void, options?: { timeout: number }) { +function scheduleIdle(callback: () => void, options?: { timeout: number }): void { + const DEFAULT_IDLE_TIMEOUT = 200; if ('requestIdleCallback' in window) { - (window as any).requestIdleCallback(callback, options); + const windowWithIdle = window as Window & { + requestIdleCallback: (callback: () => void, options?: { timeout: number }) => void + }; + windowWithIdle.requestIdleCallback(callback, options); } else { - setTimeout(callback, options?.timeout || 200); + setTimeout(callback, options?.timeout || DEFAULT_IDLE_TIMEOUT); } } diff --git a/src/events/TabManager.ts b/src/events/TabManager.ts index 11903ff..c9647bb 100644 --- a/src/events/TabManager.ts +++ b/src/events/TabManager.ts @@ -1,6 +1,5 @@ import type FolderNotesPlugin from 'src/main'; -import type { App } from 'obsidian'; -import { EditableFileView, TFolder } from 'obsidian'; +import { EditableFileView, TFolder, type App } from 'obsidian'; import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions'; export class TabManager { plugin: FolderNotesPlugin; @@ -10,19 +9,18 @@ export class TabManager { this.app = plugin.app; } - resetTabs() { + resetTabs(): void { if (!this.isEnabled()) return; this.app.workspace.iterateAllLeaves((leaf) => { if (!(leaf.view instanceof EditableFileView)) return; const file = leaf.view?.file; if (!file) return; - // @ts-ignore leaf.tabHeaderInnerTitleEl.setText(file.basename); }); } - updateTabs() { + updateTabs(): void { if (!this.isEnabled()) return; this.app.workspace.iterateAllLeaves((leaf) => { if (!(leaf.view instanceof EditableFileView)) return; @@ -30,12 +28,11 @@ export class TabManager { if (!file) return; const folder = getFolder(this.plugin, file); if (!folder) return; - // @ts-ignore leaf.tabHeaderInnerTitleEl.setText(folder.name); }); } - updateTab(folderPath: string) { + updateTab(folderPath: string): void { if (!this.isEnabled()) return; const folder = this.app.vault.getAbstractFileByPath(folderPath); @@ -49,13 +46,12 @@ export class TabManager { const file = leaf.view?.file; if (!file) return; if (file.path === folderNote.path) { - // @ts-ignore leaf.tabHeaderInnerTitleEl.setText(folder.name); } }); } - isEnabled() { + isEnabled(): boolean { if (this.plugin.settings.folderNoteName === '{{folder_name}}') return false; return this.plugin.settings.tabManagerEnabled; } diff --git a/src/events/handleClick.ts b/src/events/handleClick.ts index fb8dc6f..60ecabd 100644 --- a/src/events/handleClick.ts +++ b/src/events/handleClick.ts @@ -1,10 +1,18 @@ -import { Keymap, Platform } from 'obsidian'; +import { Keymap, Platform, type TFile } from 'obsidian'; import type FolderNotesPlugin from 'src/main'; import { openFolderNote, createFolderNote, getFolderNote } from 'src/functions/folderNoteFunctions'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; -import { addCSSClassToFileExplorerEl, removeCSSClassFromFileExplorerEL } from 'src/functions/styleFunctions'; +import { + addCSSClassToFileExplorerEl, + removeCSSClassFromFileExplorerEL, +} from 'src/functions/styleFunctions'; -export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNotesPlugin) { + + +export async function handleViewHeaderClick( + event: MouseEvent, + plugin: FolderNotesPlugin, +): Promise { if (!plugin.settings.openFolderNoteOnClickInPath) return; event.stopImmediatePropagation(); event.preventDefault(); @@ -13,37 +21,61 @@ export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNot const folderPath = event.target.getAttribute('data-path'); if (!folderPath) { return; } - const excludedFolder = getExcludedFolder(plugin, folderPath, true); - if (excludedFolder?.disableFolderNote) { - event.target.onclick = null; - event.target.click(); - return; - } else if (excludedFolder?.enableCollapsing || plugin.settings.enableCollapsing) { - event.target.onclick = null; - event.target.click(); - } + + if (await isExcludedFolder(event, plugin, folderPath)) return; const folderNote = getFolderNote(plugin, folderPath); if (folderNote) { - await openFolderNote(plugin, folderNote, event).then(async () => { - // @ts-ignore - const fileExplorerPlugin = plugin.app.internalPlugins.getEnabledPluginById('file-explorer'); - if (fileExplorerPlugin && Platform.isMobile && plugin.settings.openSidebar.mobile) { - setTimeout(() => { fileExplorerPlugin.revealInFolder(folderNote); }, 200); - } else if (fileExplorerPlugin && Platform.isDesktop && plugin.settings.openSidebar.desktop) { - fileExplorerPlugin.revealInFolder(folderNote); - } - }); + await openFolderNote(plugin, folderNote, event).then(() => + handleFolderNoteReveal(plugin, folderNote), + ); return; } else if (event.altKey || Keymap.isModEvent(event) === 'tab') { - const usedCtrl = Platform.isMacOS ? event.metaKey : event.ctrlKey; - if ((plugin.settings.altKey && event.altKey) || (usedCtrl && Keymap.isModEvent(event) === 'tab')) { - await createFolderNote(plugin, folderPath, true, undefined, true); - addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, plugin); - removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, plugin); - return; - } + if (await handleFolderNoteCreation(event, plugin, folderPath)) return; + } + (event.target as HTMLElement).onclick = null; + (event.target as HTMLElement).click(); +} + +async function isExcludedFolder( + event: MouseEvent, + plugin: FolderNotesPlugin, + folderPath: string, +): Promise { + const excludedFolder = getExcludedFolder(plugin, folderPath, true); + if (excludedFolder?.disableFolderNote) { + (event.target as HTMLElement).onclick = null; + (event.target as HTMLElement).click(); + return true; + } else if (excludedFolder?.enableCollapsing || plugin.settings.enableCollapsing) { + (event.target as HTMLElement).onclick = null; + (event.target as HTMLElement).click(); + } + return false; +} + +async function handleFolderNoteReveal(plugin: FolderNotesPlugin, folderNote: TFile): Promise { + const fileExplorerPlugin = plugin.app.internalPlugins.getEnabledPluginById('file-explorer'); + if (fileExplorerPlugin && Platform.isMobile && plugin.settings.openSidebar.mobile) { + const OPEN_SIDEBAR_DELAY = 200; + setTimeout(() => { fileExplorerPlugin.revealInFolder(folderNote); }, OPEN_SIDEBAR_DELAY); + } else if (fileExplorerPlugin && Platform.isDesktop && plugin.settings.openSidebar.desktop) { + fileExplorerPlugin.revealInFolder(folderNote); + } +} + +async function handleFolderNoteCreation( + event: MouseEvent, + plugin: FolderNotesPlugin, + folderPath: string, +): Promise { + const usedCtrl = Platform.isMacOS ? event.metaKey : event.ctrlKey; + if ((plugin.settings.altKey && event.altKey) || + (usedCtrl && Keymap.isModEvent(event) === 'tab')) { + await createFolderNote(plugin, folderPath, true, undefined, true); + addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, plugin); + return true; } - event.target.onclick = null; - event.target.click(); + return false; } diff --git a/src/events/handleCreate.ts b/src/events/handleCreate.ts index f65d3ed..c6239c3 100644 --- a/src/events/handleCreate.ts +++ b/src/events/handleCreate.ts @@ -1,11 +1,18 @@ -import type { TAbstractFile } from 'obsidian'; -import { TFolder, TFile } from 'obsidian'; +import { TFolder, TFile, type TAbstractFile } from 'obsidian'; import type FolderNotesPlugin from 'src/main'; -import { createFolderNote, getFolder, getFolderNote, turnIntoFolderNote } from 'src/functions/folderNoteFunctions'; +import { + createFolderNote, + getFolder, + getFolderNote, + turnIntoFolderNote, +} from 'src/functions/folderNoteFunctions'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; -import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl } from 'src/functions/styleFunctions'; +import { + removeCSSClassFromFileExplorerEL, + addCSSClassToFileExplorerEl, +} from 'src/functions/styleFunctions'; -export async function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin) { +export async function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin): Promise { if (!plugin.app.workspace.layoutReady) return; const folder = file.parent; @@ -24,7 +31,7 @@ export async function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugi } } -async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin) { +async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin): Promise { const folder = getFolder(plugin, file); if (!(folder instanceof TFolder) && plugin.settings.autoCreateForFiles) { @@ -51,7 +58,7 @@ async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin) { } } -async function handleFolderCreation(folder: TFolder, plugin: FolderNotesPlugin) { +async function handleFolderCreation(folder: TFolder, plugin: FolderNotesPlugin): Promise { let openFile = plugin.settings.autoCreateFocusFiles; const attachmentFolderPath = plugin.app.vault.getConfig('attachmentFolderPath') as string; diff --git a/src/events/handleDelete.ts b/src/events/handleDelete.ts index 089c1ff..e9d5c51 100644 --- a/src/events/handleDelete.ts +++ b/src/events/handleDelete.ts @@ -1,11 +1,14 @@ -import type { TAbstractFile } from 'obsidian'; -import { TFolder, TFile } from 'obsidian'; +import { TFolder, TFile, type TAbstractFile } from 'obsidian'; import type FolderNotesPlugin from 'src/main'; import { getFolderNote, getFolder, deleteFolderNote } from 'src/functions/folderNoteFunctions'; -import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl, hideFolderNoteInFileExplorer } from 'src/functions/styleFunctions'; +import { + removeCSSClassFromFileExplorerEL, + addCSSClassToFileExplorerEl, + hideFolderNoteInFileExplorer, +} from 'src/functions/styleFunctions'; import { getFolderPathFromString } from 'src/functions/utils'; -export function handleDelete(file: TAbstractFile, plugin: FolderNotesPlugin) { +export function handleDelete(file: TAbstractFile, plugin: FolderNotesPlugin): void { const folder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(file.path)); if (folder instanceof TFolder) { if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { @@ -16,13 +19,15 @@ export function handleDelete(file: TAbstractFile, plugin: FolderNotesPlugin) { } if (file instanceof TFile) { - const folder = getFolder(plugin, file); - if (!folder) { return; } - const folderNote = getFolderNote(plugin, folder.path); + const folderNoteFolder = getFolder(plugin, file); + if (!folderNoteFolder) { return; } + const folderNote = getFolderNote(plugin, folderNoteFolder.path); if (folderNote) { return; } - removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); - removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); - hideFolderNoteInFileExplorer(folder.path, plugin); + removeCSSClassFromFileExplorerEL(folderNoteFolder.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL( + folderNoteFolder.path, 'only-has-folder-note', true, plugin, + ); + hideFolderNoteInFileExplorer(folderNoteFolder.path, plugin); } if (!(file instanceof TFolder)) { return; } diff --git a/src/events/handleRename.ts b/src/events/handleRename.ts index 7ca75b2..c6fb0ea 100644 --- a/src/events/handleRename.ts +++ b/src/events/handleRename.ts @@ -1,14 +1,29 @@ -import type { TAbstractFile } from 'obsidian'; -import { TFile, TFolder, Notice } from 'obsidian'; +import { TFile, TFolder, Notice, type TAbstractFile } from 'obsidian'; import type FolderNotesPlugin from 'src/main'; -import { extractFolderName, getFolderNote, getFolderNoteFolder } from '../functions/folderNoteFunctions'; -import { getExcludedFolder, addExcludedFolder, updateExcludedFolder, deleteExcludedFolder, getDetachedFolder } from '../ExcludeFolders/functions/folderFunctions'; +import { + extractFolderName, getFolderNote, getFolderNoteFolder, +} from '../functions/folderNoteFunctions'; +import { + getExcludedFolder, addExcludedFolder, + updateExcludedFolder, deleteExcludedFolder, getDetachedFolder, +} from '../ExcludeFolders/functions/folderFunctions'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; -import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl, markFileAsFolderNote, unmarkFileAsFolderNote, unmarkFolderAsFolderNote, markFolderWithFolderNoteClasses, hideFolderNoteInFileExplorer, removeActiveFolder, setActiveFolder } from 'src/functions/styleFunctions'; -import { getFolderPathFromString, removeExtension, getFileNameFromPathString } from 'src/functions/utils'; - -export function handleRename(file: TAbstractFile, oldPath: string, plugin: FolderNotesPlugin) { - const folder = file.parent; +import { + removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl, + markFileAsFolderNote, unmarkFileAsFolderNote, + unmarkFolderAsFolderNote, markFolderWithFolderNoteClasses, + hideFolderNoteInFileExplorer, removeActiveFolder, setActiveFolder, +} from 'src/functions/styleFunctions'; +import { + getFolderPathFromString, removeExtension, getFileNameFromPathString, +} from 'src/functions/utils'; + +export function handleRename( + file: TAbstractFile, + oldPath: string, + plugin: FolderNotesPlugin, +): void { + let folder = file.parent; const oldFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(oldPath)); if (folder instanceof TFolder) { @@ -28,19 +43,22 @@ export function handleRename(file: TAbstractFile, oldPath: string, plugin: Folde } if (file instanceof TFolder) { - const folder = file; + folder = file; plugin.tabManager.updateTab(folder.path); updateExcludedFolderPath(folder, oldPath, plugin); if (isFolderRename(folder, oldPath)) { - return handleFolderRename(folder, oldPath, plugin); + handleFolderRename(folder, oldPath, plugin); + return; } return handleFolderMove(folder, oldPath, plugin); } else if (file instanceof TFile) { if (isFileRename(file, oldPath)) { - return fmptUpdateFileName(file, oldPath, plugin); + handleFileRename(file, oldPath, plugin); + return; } - return handleFileMove(file, oldPath, plugin); + handleFileMove(file, oldPath, plugin); + return; } } @@ -64,7 +82,7 @@ function isFolderRename(folder: TFolder, oldPath: string): boolean { return oldParent === newParent && oldName !== newName; } -export function handleFolderMove(file: TFolder, oldPath: string, plugin: FolderNotesPlugin) { +export function handleFolderMove(file: TFolder, oldPath: string, plugin: FolderNotesPlugin): void { if (plugin.settings.storageLocation === 'insideFolder') { return; } if (!plugin.settings.syncMove) { return; } const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation); @@ -82,60 +100,43 @@ export function handleFolderMove(file: TFolder, oldPath: string, plugin: FolderN plugin.app.fileManager.renameFile(folderNote, newPath); } -export async function handleFileMove(file: TFile, oldPath: string, plugin: FolderNotesPlugin) { - const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) || file.basename; - const oldFileName = removeExtension(getFileNameFromPathString(oldPath)); - const newFolder = getFolderNoteFolder(plugin, file, file.basename); - let excludedFolder = getExcludedFolder(plugin, newFolder?.path || '', true); - const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName); - const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file); - +// eslint-disable-next-line complexity +export async function handleFileMove( + file: TFile, + oldPath: string, + plugin: FolderNotesPlugin, +): Promise { + const { folderName, oldFileName, newFolder, excludedFolder, oldFolder, folderNote } = getArgs( + plugin, file, oldPath, + ); - const isFileNowFolderNoteInNewFolder = folderName === newFolder?.name; - const isFileMovedFromOldFolderNote = oldFolder && oldFolder.name === oldFileName && newFolder?.path !== oldFolder.path; + const isFolderNoteInNewFolder = folderName === newFolder?.name; + const fileMovedFromOldFolderNote = oldFolder && oldFolder.name === oldFileName + && newFolder?.path !== oldFolder.path; // this is for turning files into folder notes for folders that already have a folder note // e.g. Turn into folder note for "Folder name" - const isFileNowFolderNoteWithExistingNote = folderName === newFolder?.name && folderNote; - - if (isFileNowFolderNoteWithExistingNote) { - let excludedFolderExisted = true; - let disabledSync = false; - - if (!excludedFolder) { - excludedFolderExisted = false; - excludedFolder = new ExcludedFolder(oldFolder?.path || '', plugin.settings.excludeFolders.length, undefined, plugin); - addExcludedFolder(plugin, excludedFolder); - } else if (!excludedFolder.disableSync) { - disabledSync = false; - excludedFolder.disableSync = true; - updateExcludedFolder(plugin, excludedFolder, excludedFolder); - } - return plugin.app.fileManager.renameFile(file, oldPath).then(() => { - if (!excludedFolder) { return; } - if (!excludedFolderExisted) { - deleteExcludedFolder(plugin, excludedFolder); - } else if (!disabledSync) { - excludedFolder.disableSync = false; - updateExcludedFolder(plugin, excludedFolder, excludedFolder); - } - }); - } else if (isFileNowFolderNoteInNewFolder) { - if (!excludedFolder?.disableFolderNote) { - markFileAsFolderNote(file, plugin); - if (newFolder instanceof TFolder) { - markFolderWithFolderNoteClasses(newFolder, plugin); - if (plugin.app.workspace.getActiveFile()?.path === file.path) { - removeActiveFolder(plugin); - setActiveFolder(newFolder.path, plugin); - } - } - if (oldFolder instanceof TFolder) { - hideFolderNoteInFileExplorer(oldFolder.path, plugin); - unmarkFolderAsFolderNote(oldFolder, plugin); + const isFileWithExistingNote = folderName === newFolder?.name && folderNote; + + if (isFileWithExistingNote) { + renameExistingFolderNote( + file, oldPath, plugin, excludedFolder, oldFolder, + ); + } else if (isFolderNoteInNewFolder) { + if (excludedFolder?.disableFolderNote) { return; } + markFileAsFolderNote(file, plugin); + if (newFolder instanceof TFolder) { + markFolderWithFolderNoteClasses(newFolder, plugin); + if (plugin.app.workspace.getActiveFile()?.path === file.path) { + removeActiveFolder(plugin); + setActiveFolder(newFolder.path, plugin); } } - } else if (isFileMovedFromOldFolderNote) { + if (oldFolder instanceof TFolder) { + hideFolderNoteInFileExplorer(oldFolder.path, plugin); + unmarkFolderAsFolderNote(oldFolder, plugin); + } + } else if (fileMovedFromOldFolderNote) { unmarkFileAsFolderNote(file, plugin); if (oldFolder instanceof TFolder) { removeActiveFolder(plugin); @@ -145,9 +146,75 @@ export async function handleFileMove(file: TFile, oldPath: string, plugin: Folde } } -export async function handleFolderRename(file: TFolder, oldPath: string, plugin: FolderNotesPlugin) { + + +function getArgs(plugin: FolderNotesPlugin, file: TFile, oldPath: string): { + folderName: string; + oldFileName: string; + newFolder: TAbstractFile | null; + excludedFolder: ExcludedFolder | undefined; + oldFolder: TAbstractFile | null; + folderNote: TFile | null | undefined; +} { + const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) + || file.basename; + const oldFileName = removeExtension(getFileNameFromPathString(oldPath)); + const newFolder = getFolderNoteFolder(plugin, file, file.basename); + let excludedFolder = getExcludedFolder(plugin, newFolder?.path || '', true); + const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName); + const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file); + + return { + folderName, + oldFileName, + newFolder, + excludedFolder, + oldFolder, + folderNote, + }; +} + +function renameExistingFolderNote( + file: TFile, oldPath: string, plugin: FolderNotesPlugin, + excludedFolder: ExcludedFolder | undefined, + oldFolder: TAbstractFile | null, +): void { + let excludedFolderExisted = true; + let disabledSync = false; + + if (!excludedFolder) { + excludedFolderExisted = false; + excludedFolder = new ExcludedFolder( + oldFolder?.path || '', + plugin.settings.excludeFolders.length, + undefined, + plugin, + ); + addExcludedFolder(plugin, excludedFolder); + } else if (!excludedFolder.disableSync) { + disabledSync = false; + excludedFolder.disableSync = true; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + } + plugin.app.fileManager.renameFile(file, oldPath).then(() => { + if (!excludedFolder) { return; } + if (!excludedFolderExisted) { + deleteExcludedFolder(plugin, excludedFolder); + } else if (!disabledSync) { + excludedFolder.disableSync = false; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + } + }); +} + + + +export async function handleFolderRename( + file: TFolder, oldPath: string, plugin: FolderNotesPlugin, +): Promise { const fileName = plugin.settings.folderNoteName.replace('{{folder_name}}', file.name); - const oldFileName = plugin.settings.folderNoteName.replace('{{folder_name}}', getFileNameFromPathString(oldPath)); + const oldFileName = plugin.settings.folderNoteName + .replace('{{folder_name}}', getFileNameFromPathString(oldPath)); if (fileName === oldFileName) { return; } @@ -183,50 +250,62 @@ export async function handleFolderRename(file: TFolder, oldPath: string, plugin: plugin.app.fileManager.renameFile(folderNote, newPath); } -export async function fmptUpdateFileName(file: TFile, oldPath: string, plugin: FolderNotesPlugin) { +// eslint-disable-next-line complexity +export async function handleFileRename( + file: TFile, + oldPath: string, + plugin: FolderNotesPlugin, +): Promise { const oldFileName = removeExtension(getFileNameFromPathString(oldPath)); const newFileName = file.basename; if (oldFileName === newFileName) { return; } const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName); - const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) || file.basename; - const oldFolderName = extractFolderName(plugin.settings.folderNoteName, oldFileName) || oldFileName; + const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) + || file.basename; + const oldFolderName = extractFolderName(plugin.settings.folderNoteName, oldFileName) + || oldFileName; const newFolder = getFolderNoteFolder(plugin, file, file.basename); const excludedFolder = getExcludedFolder(plugin, newFolder?.path || '', true); const detachedExcludedFolder = getDetachedFolder(plugin, newFolder?.path || ''); const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file); - if (!excludedFolder?.disableFolderNote && folderName === newFolder?.name && !detachedExcludedFolder) { - addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); - addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin); + // Handle folder note creation + if (shouldCreateFolderNote(excludedFolder, folderName, newFolder, detachedExcludedFolder)) { + if (newFolder) { + handleFolderNoteCreation(file, newFolder, plugin); + } return; - } else if (excludedFolder?.disableFolderNote || (folderName !== newFolder?.name)) { - removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin); - removeCSSClassFromFileExplorerEL(newFolder?.path || '', 'has-folder-note', false, plugin); } - if (excludedFolder?.disableSync || !plugin.settings.syncFolderName) { return; } + // Handle folder note removal + if (shouldRemoveFolderNoteClasses(excludedFolder, folderName, newFolder)) { + handleFolderNoteRemoval(file, newFolder, plugin); + } + // Early return if sync is disabled + if (excludedFolder?.disableSync || !plugin.settings.syncFolderName) { + return; + } - if (folderName === newFolder?.name) { - addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); - removeCSSClassFromFileExplorerEL(oldFolder?.path, 'has-folder-note', false, plugin); - addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin); + // Handle same folder rename + if (folderName === newFolder?.name && newFolder) { + handleSameFolderRename(file, newFolder, oldFolder, plugin); return; } - // file matched folder name before rename - // file hasnt moved just renamed - // Need to rename the folder - if (!oldFolder) return; - if (oldFolderName === oldFolder.name && newFolder?.path === oldFolder.path) { - return renameFolderOnFileRename(file, oldPath, oldFolder, plugin); - } else if (folderNote && oldFolderName === oldFolder.name) { - return renameFolderOnFileRename(file, oldPath, oldFolder, plugin); + // Handle folder rename on file rename + if (shouldRenameFolderOnFileRename(oldFolderName, oldFolder, newFolder, folderNote)) { + return renameFolderOnFileRename(file, oldPath, oldFolder!, plugin); } } -async function renameFolderOnFileRename(file: TFile, oldPath: string, oldFolder: TAbstractFile, plugin: FolderNotesPlugin) { +async function renameFolderOnFileRename( + file: TFile, + oldPath: string, + oldFolder: TAbstractFile, + plugin: FolderNotesPlugin, +): Promise { const newFolderName = extractFolderName(plugin.settings.folderNoteName, file.basename); if (!newFolderName) { removeCSSClassFromFileExplorerEL(oldFolder.path, 'has-folder-note', false, plugin); @@ -256,12 +335,17 @@ async function renameFolderOnFileRename(file: TFile, oldPath: string, oldFolder: if (plugin.app.vault.getAbstractFileByPath(newFolderPath)) { await plugin.app.fileManager.renameFile(file, oldPath); - return new Notice('A folder with the same name already exists'); + new Notice('A folder with the same name already exists'); + return; } plugin.app.fileManager.renameFile(oldFolder, newFolderPath); } -function updateExcludedFolderPath(folder: TFolder, oldPath: string, plugin: FolderNotesPlugin) { +function updateExcludedFolderPath( + folder: TFolder, + oldPath: string, + plugin: FolderNotesPlugin, +): void { const excludedFolders = plugin.settings.excludeFolders.filter( (excludedFolder) => excludedFolder.path?.includes(oldPath), ); @@ -282,3 +366,68 @@ function updateExcludedFolderPath(folder: TFolder, oldPath: string, plugin: Fold }); plugin.saveSettings(); } + + +function shouldCreateFolderNote( + excludedFolder: ExcludedFolder | undefined, + folderName: string, + newFolder: TAbstractFile | null, + detachedExcludedFolder: ExcludedFolder | undefined, +): boolean { + return !excludedFolder?.disableFolderNote + && folderName === (newFolder as TFolder)?.name + && !detachedExcludedFolder; +} + +function shouldRemoveFolderNoteClasses( + excludedFolder: ExcludedFolder | undefined, + folderName: string, + newFolder: TAbstractFile | null, +): boolean { + return excludedFolder?.disableFolderNote || (folderName !== (newFolder as TFolder)?.name); +} + +function handleFolderNoteCreation( + file: TFile, + newFolder: TAbstractFile, + plugin: FolderNotesPlugin, +): void { + addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); + addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin); +} + +function handleFolderNoteRemoval( + file: TFile, + newFolder: TAbstractFile | null, + plugin: FolderNotesPlugin, +): void { + removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(newFolder?.path || '', 'has-folder-note', false, plugin); +} + +function handleSameFolderRename( + file: TFile, + newFolder: TAbstractFile, + oldFolder: TAbstractFile | null, + plugin: FolderNotesPlugin, +): void { + addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(oldFolder?.path, 'has-folder-note', false, plugin); + addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin); +} + +function shouldRenameFolderOnFileRename( + oldFolderName: string, + oldFolder: TAbstractFile | null, + newFolder: TAbstractFile | null, + folderNote: TFile | null | undefined, +): boolean { + if (!oldFolder) return false; + + const oldFolderAsFolder = oldFolder as TFolder; + const newFolderAsFolder = newFolder as TFolder; + + return (oldFolderName === oldFolderAsFolder.name + && newFolderAsFolder?.path === oldFolderAsFolder.path) + || (folderNote !== null && oldFolderName === oldFolderAsFolder.name); +} diff --git a/src/main.ts b/src/main.ts index b72609a..6f18134 100644 --- a/src/main.ts +++ b/src/main.ts @@ -203,7 +203,7 @@ export default class FolderNotesPlugin extends Plugin { // @ts-ignore const editMode = view.editMode ?? view.sourceMode ?? this.app.workspace.activeEditor?.editMode; - // eslint-disable-next-line + const plugin = this; if (!editMode) { return; } @@ -308,7 +308,7 @@ export default class FolderNotesPlugin extends Plugin { openFolderNote(this, folderNote, evt); } - handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) { + handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext): void { const observer = new MutationObserver(() => { const editButton = el.parentElement?.childNodes.item(1); if (editButton) { @@ -316,7 +316,10 @@ export default class FolderNotesPlugin extends Plugin { e.stopImmediatePropagation(); e.preventDefault(); e.stopPropagation(); - new FolderOverviewSettings(this.app, this, parseYaml(source), ctx, el, this.settings.defaultOverview).open(); + new FolderOverviewSettings( + this.app, this, parseYaml(source), + ctx, el, this.settings.defaultOverview, + ).open(); }, { capture: true }); } }); @@ -329,11 +332,11 @@ export default class FolderNotesPlugin extends Plugin { try { if (this.app.workspace.layoutReady) { const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview); - folderOverview.create(this, parseYaml(source), el, ctx); + folderOverview.create(this, el, ctx); } else { this.app.workspace.onLayoutReady(() => { const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview); - folderOverview.create(this, parseYaml(source), el, ctx); + folderOverview.create(this, el, ctx); }); } } catch (e) { diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 07825d6..5323f27 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 07825d6f187a9ddf2c9b29066eb0c06aa8b8fee2 +Subproject commit 5323f27922aff0165dca78770f24a7d8a99de7c0 diff --git a/src/suggesters/FileSuggester.ts b/src/suggesters/FileSuggester.ts index 941ba8d..3828036 100644 --- a/src/suggesters/FileSuggester.ts +++ b/src/suggesters/FileSuggester.ts @@ -1,7 +1,4 @@ -// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater - -import type { TAbstractFile } from 'obsidian'; -import { TFile } from 'obsidian'; +import { type TAbstractFile, TFile } from 'obsidian'; import { TextInputSuggest } from './Suggest'; import type FolderNotesPlugin from '../main'; export enum FileSuggestMode { diff --git a/src/template.ts b/src/template.ts index 4855c52..09753cb 100644 --- a/src/template.ts +++ b/src/template.ts @@ -1,5 +1,4 @@ -import type { App } from 'obsidian'; -import { TFile, WorkspaceLeaf } from 'obsidian'; +import { TFile, WorkspaceLeaf, type App } from 'obsidian'; import type FolderNotesPlugin from './main'; export async function applyTemplate( From 113cf5f40c9fe71a906207b6497e16a717e440a0 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sun, 10 Aug 2025 12:21:16 +0200 Subject: [PATCH 37/60] Excluded folders --- src/ExcludeFolders/ExcludeFolder.ts | 1 + src/ExcludeFolders/ExcludePattern.ts | 8 +- src/ExcludeFolders/WhitelistPattern.ts | 7 +- .../functions/folderFunctions.ts | 209 +++++++++++++----- .../functions/patternFunctions.ts | 160 ++++++++------ .../functions/whitelistFolderFunctions.ts | 114 +++++++--- .../functions/whitelistPatternFunctions.ts | 164 ++++++++------ .../modals/ExcludeFolderSettings.ts | 11 +- src/ExcludeFolders/modals/PatternSettings.ts | 15 +- .../modals/WhitelistFolderSettings.ts | 12 +- .../modals/WhitelistPatternSettings.ts | 14 +- .../modals/WhitelistedFoldersSettings.ts | 40 ++-- 12 files changed, 494 insertions(+), 261 deletions(-) diff --git a/src/ExcludeFolders/ExcludeFolder.ts b/src/ExcludeFolders/ExcludeFolder.ts index 24af3ab..b080ec2 100644 --- a/src/ExcludeFolders/ExcludeFolder.ts +++ b/src/ExcludeFolders/ExcludeFolder.ts @@ -25,6 +25,7 @@ export class ExcludedFolder { this.disableFolderNote = plugin.settings.excludeFolderDefaultSettings.disableFolderNote; this.enableCollapsing = plugin.settings.excludeFolderDefaultSettings.enableCollapsing; this.position = position; + // eslint-disable-next-line max-len this.excludeFromFolderOverview = plugin.settings.excludeFolderDefaultSettings.excludeFromFolderOverview; this.string = ''; this.hideInSettings = false; diff --git a/src/ExcludeFolders/ExcludePattern.ts b/src/ExcludeFolders/ExcludePattern.ts index c78aab3..971cbba 100644 --- a/src/ExcludeFolders/ExcludePattern.ts +++ b/src/ExcludeFolders/ExcludePattern.ts @@ -15,7 +15,12 @@ export class ExcludePattern { detached: boolean; detachedFilePath?: string; showFolderNote: boolean; - constructor(pattern: string, position: number, id: string | undefined, plugin: FolderNotesPlugin) { + constructor( + pattern: string, + position: number, + id: string | undefined, + plugin: FolderNotesPlugin, + ) { this.type = 'pattern'; this.id = id || crypto.randomUUID(); this.string = pattern; @@ -25,6 +30,7 @@ export class ExcludePattern { this.disableAutoCreate = plugin.settings.excludePatternDefaultSettings.disableAutoCreate; this.disableFolderNote = plugin.settings.excludePatternDefaultSettings.disableFolderNote; this.enableCollapsing = plugin.settings.excludePatternDefaultSettings.enableCollapsing; + // eslint-disable-next-line max-len this.excludeFromFolderOverview = plugin.settings.excludePatternDefaultSettings.excludeFromFolderOverview; this.path = ''; this.hideInSettings = false; diff --git a/src/ExcludeFolders/WhitelistPattern.ts b/src/ExcludeFolders/WhitelistPattern.ts index bf9fe91..506a597 100644 --- a/src/ExcludeFolders/WhitelistPattern.ts +++ b/src/ExcludeFolders/WhitelistPattern.ts @@ -13,7 +13,12 @@ export class WhitelistedPattern { showInFolderOverview: boolean; hideInFileExplorer: boolean; hideInSettings: boolean; - constructor(pattern: string, position: number, id: string | undefined, plugin: FolderNotesPlugin) { + constructor( + pattern: string, + position: number, + id: string | undefined, + plugin: FolderNotesPlugin, + ) { this.type = 'pattern'; this.id = id || crypto.randomUUID(); this.subFolders = plugin.settings.excludePatternDefaultSettings.subFolders; diff --git a/src/ExcludeFolders/functions/folderFunctions.ts b/src/ExcludeFolders/functions/folderFunctions.ts index a2fb61c..8ec4a45 100644 --- a/src/ExcludeFolders/functions/folderFunctions.ts +++ b/src/ExcludeFolders/functions/folderFunctions.ts @@ -6,24 +6,34 @@ import { Platform, Setting } from 'obsidian'; import { FolderSuggest } from '../../suggesters/FolderSuggester'; import type { SettingsTab } from '../../settings/SettingsTab'; import ExcludedFolderSettings from '../modals/ExcludeFolderSettings'; -import { updatePattern, getExcludedFoldersByPattern, addExcludePatternListItem } from './patternFunctions'; +import { + updatePattern, + getExcludedFoldersByPattern, + addExcludePatternListItem, +} from './patternFunctions'; import { getWhitelistedFolder } from './whitelistFolderFunctions'; import type { WhitelistedFolder } from '../WhitelistFolder'; import type { WhitelistedPattern } from '../WhitelistPattern'; -export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, includeDetached: boolean, pathOnly?: boolean, ignoreWhitelist?: boolean) { - let excludedFolder = {} as ExcludedFolder | ExcludePattern | undefined; - const whitelistedFolder = getWhitelistedFolder(plugin, path) as WhitelistedFolder | WhitelistedPattern | undefined; +function combineExcluded( + plugin: FolderNotesPlugin, + path: string, + includeDetached: boolean, + pathOnly?: boolean, +): Array { const folderName = getFolderNameFromPathString(path); - let matchedPatterns = getExcludedFoldersByPattern(plugin, folderName); - const excludedFolders = getExcludedFoldersByPath(plugin, path); - if (pathOnly) { matchedPatterns = []; } - let combinedExcludedFolders = [...matchedPatterns, ...excludedFolders]; - - if (!includeDetached) { - combinedExcludedFolders = combinedExcludedFolders.filter((f) => !f.detached); - } + const matchedPatterns = pathOnly ? [] : getExcludedFoldersByPattern(plugin, folderName); + const excludedByPath = getExcludedFoldersByPath(plugin, path); + let combined = [...matchedPatterns, ...excludedByPath]; + if (!includeDetached) combined = combined.filter((f) => !f.detached); + return combined; +} +function aggregateFlags( + combinedExcludedFolders: Array, +): Partial | undefined { + if (combinedExcludedFolders.length === 0) return undefined; + const result: Partial = {}; const propertiesToCopy: (keyof ExcludedFolder)[] = [ 'disableAutoCreate', 'disableFolderNote', @@ -35,32 +45,46 @@ export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, inclu 'id', 'showFolderNote', ]; - - if (combinedExcludedFolders.length > 0) { - for (const matchedFolder of combinedExcludedFolders) { - propertiesToCopy.forEach((property) => { - if (matchedFolder[property] === true) { - (excludedFolder as any)[property] = true; - } else if (!matchedFolder[property]) { - (excludedFolder as any)[property] = false; - } - }); + for (const matchedFolder of combinedExcludedFolders) { + for (const property of propertiesToCopy) { + const value = (matchedFolder as Partial)[property]; + if (value === true) { + (result as Partial)[property] = true as never; + } else if (!value) { + (result as Partial)[property] = false as never; + } } - } else { - excludedFolder = undefined; } + return result; +} + +function applyWhitelistOverrides( + excluded: Partial, + whitelisted: WhitelistedFolder | WhitelistedPattern, +): Partial { + const out: Partial = { ...excluded }; + if (out.disableAutoCreate !== undefined) { + out.disableAutoCreate = !whitelisted.enableAutoCreate; + } + if (out.disableFolderNote !== undefined) { + out.disableFolderNote = !whitelisted.enableFolderNote; + } + if (out.disableSync !== undefined) { + out.disableSync = !whitelisted.enableSync; + } + out.enableCollapsing = !whitelisted.disableCollapsing; + if (out.excludeFromFolderOverview !== undefined) { + out.excludeFromFolderOverview = !whitelisted.showInFolderOverview; + } + out.showFolderNote = !whitelisted.hideInFileExplorer; + return out; +} - if (excludedFolder?.detached) { ignoreWhitelist = true; } - - if (whitelistedFolder && excludedFolder && !ignoreWhitelist) { - excludedFolder.disableAutoCreate ? excludedFolder.disableAutoCreate = !whitelistedFolder.enableAutoCreate : ''; - excludedFolder.disableFolderNote ? excludedFolder.disableFolderNote = !whitelistedFolder.enableFolderNote : ''; - excludedFolder.disableSync ? excludedFolder.disableSync = !whitelistedFolder.enableSync : ''; - excludedFolder.enableCollapsing = !whitelistedFolder.disableCollapsing; - excludedFolder.excludeFromFolderOverview ? excludedFolder.excludeFromFolderOverview = !whitelistedFolder.showInFolderOverview : ''; - excludedFolder.showFolderNote = !whitelistedFolder.hideInFileExplorer; - } else if (excludedFolder && Object.keys(excludedFolder).length === 0) { - excludedFolder = { +function defaultExcludedIfEmpty( + value: Partial | undefined, +): ExcludedFolder | undefined { + if (value && Object.keys(value).length === 0) { + return { type: 'folder', id: '', path: '', @@ -77,23 +101,54 @@ export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, inclu showFolderNote: false, }; } + return value as ExcludedFolder | undefined; +} + +export function getExcludedFolder( + plugin: FolderNotesPlugin, + path: string, + includeDetached: boolean, + pathOnly?: boolean, + ignoreWhitelist?: boolean, +): ExcludedFolder | ExcludePattern | undefined { + const combined = combineExcluded(plugin, path, includeDetached, pathOnly); + let excluded = aggregateFlags(combined); + + const whitelist = getWhitelistedFolder( + plugin, + path, + ) as WhitelistedFolder | WhitelistedPattern | undefined; - return excludedFolder; + let skipWhitelist = ignoreWhitelist ?? false; + if (excluded?.detached) skipWhitelist = true; + + if (whitelist && excluded && !skipWhitelist) { + excluded = applyWhitelistOverrides(excluded, whitelist); + } + + return defaultExcludedIfEmpty(excluded) as ExcludedFolder | ExcludePattern | undefined; } -export function getDetachedFolder(plugin: FolderNotesPlugin, path: string) { +export function getDetachedFolder( + plugin: FolderNotesPlugin, + path: string, +): ExcludedFolder | undefined { return plugin.settings.excludeFolders.find((f) => f.path === path && f.detached); } - -export function getExcludedFolderByPath(plugin: FolderNotesPlugin, path: string) { +export function getExcludedFolderByPath( + plugin: FolderNotesPlugin, + path: string, +): ExcludedFolder | undefined { return plugin.settings.excludeFolders.find((excludedFolder) => { if (path.trim() === '' || !excludedFolder.path) { return false; } if (excludedFolder.path === path) { return true; } if (!excludedFolder.subFolders) { return false; } - const excludedFolderPath = excludedFolder.path.includes('/') ? excludedFolder.path : excludedFolder.path + '/'; + const excludedFolderPath = excludedFolder.path.includes('/') + ? excludedFolder.path + : `${excludedFolder.path}/`; let folderPath = getFolderPathFromString(path); - folderPath = folderPath.includes('/') ? folderPath : folderPath + '/'; + folderPath = folderPath.includes('/') ? folderPath : `${folderPath}/`; if (folderPath.includes('/') || folderPath.includes('\\')) { return folderPath.startsWith(excludedFolderPath) || folderPath === excludedFolderPath; @@ -103,14 +158,19 @@ export function getExcludedFolderByPath(plugin: FolderNotesPlugin, path: string) }); } -export function getExcludedFoldersByPath(plugin: FolderNotesPlugin, path: string) { +export function getExcludedFoldersByPath( + plugin: FolderNotesPlugin, + path: string, +): ExcludedFolder[] { return plugin.settings.excludeFolders.filter((excludedFolder) => { if (path.trim() === '' || !excludedFolder.path) { return false; } if (excludedFolder.path === path) { return true; } if (!excludedFolder.subFolders) { return false; } - const excludedFolderPath = excludedFolder.path.includes('/') ? excludedFolder.path : excludedFolder.path + '/'; + const excludedFolderPath = excludedFolder.path.includes('/') + ? excludedFolder.path + : `${excludedFolder.path}/`; let folderPath = getFolderPathFromString(path); - folderPath = folderPath.includes('/') ? folderPath : folderPath + '/'; + folderPath = folderPath.includes('/') ? folderPath : `${folderPath}/`; if (folderPath.includes('/') || folderPath.includes('\\')) { return folderPath.startsWith(excludedFolderPath) || folderPath === excludedFolderPath; @@ -120,32 +180,52 @@ export function getExcludedFoldersByPath(plugin: FolderNotesPlugin, path: string }); } -export function addExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder, reloadStyles = true) { +export function addExcludedFolder( + plugin: FolderNotesPlugin, + excludedFolder: ExcludedFolder, + reloadStyles = true, +): void { plugin.settings.excludeFolders.push(excludedFolder); - plugin.saveSettings(reloadStyles); + void plugin.saveSettings(reloadStyles); } -export async function deleteExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder) { - plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== excludedFolder.id || folder.type === 'pattern'); - plugin.saveSettings(true); +export async function deleteExcludedFolder( + plugin: FolderNotesPlugin, + excludedFolder: ExcludedFolder, +): Promise { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter( + (folder) => folder.id !== excludedFolder.id || folder.type === 'pattern', + ); + await plugin.saveSettings(true); resyncArray(plugin); } -export function updateExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludePattern, newExcludeFolder: ExcludePattern) { - plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== excludedFolder.id); +export function updateExcludedFolder( + plugin: FolderNotesPlugin, + excludedFolder: ExcludePattern, + newExcludeFolder: ExcludePattern, +): void { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter( + (folder) => folder.id !== excludedFolder.id, + ); addExcludedFolder(plugin, newExcludeFolder); } -export function resyncArray(plugin: FolderNotesPlugin) { - plugin.settings.excludeFolders = plugin.settings.excludeFolders.sort((a, b) => a.position - b.position); +export function resyncArray(plugin: FolderNotesPlugin): void { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.sort( + (a, b) => a.position - b.position, + ); plugin.settings.excludeFolders.forEach((folder, index) => { folder.position = index; }); - plugin.saveSettings(); + void plugin.saveSettings(); } - -export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTMLElement, excludedFolder: ExcludedFolder) { +export function addExcludeFolderListItem( + settings: SettingsTab, + containerEl: HTMLElement, + excludedFolder: ExcludedFolder, +): void { const { plugin } = settings; const setting = new Setting(containerEl); setting.setClass('fn-exclude-folder-list'); @@ -155,14 +235,19 @@ export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTM plugin, false, ); - // @ts-ignore + // @ts-expect-error Obsidian's public types don't include this property cb.containerEl.addClass('fn-exclude-folder-path'); cb.setPlaceholder('Folder path'); cb.setValue(excludedFolder.path || ''); cb.onChange((value) => { if (value.startsWith('{regex}') || value.includes('*')) { deleteExcludedFolder(plugin, excludedFolder); - const pattern = new ExcludePattern(value, plugin.settings.excludeFolders.length, undefined, plugin); + const pattern = new ExcludePattern( + value, + plugin.settings.excludeFolders.length, + undefined, + plugin, + ); addExcludedFolder(plugin, pattern); addExcludePatternListItem(settings, containerEl, pattern); setting.clear(); @@ -190,7 +275,9 @@ export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTM if (excludedFolder.position === 0) { return; } excludedFolder.position -= 1; updateExcludedFolder(plugin, excludedFolder, excludedFolder); - const oldExcludedFolder = plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position); + const oldExcludedFolder = plugin.settings.excludeFolders.find( + (folder) => folder.position === excludedFolder.position, + ); if (oldExcludedFolder) { oldExcludedFolder.position += 1; if (oldExcludedFolder.type === 'pattern') { @@ -213,7 +300,9 @@ export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTM excludedFolder.position += 1; updateExcludedFolder(plugin, excludedFolder, excludedFolder); - const oldExcludedFolder = plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position); + const oldExcludedFolder = plugin.settings.excludeFolders.find( + (folder) => folder.position === excludedFolder.position, + ); if (oldExcludedFolder) { oldExcludedFolder.position -= 1; if (oldExcludedFolder.type === 'pattern') { diff --git a/src/ExcludeFolders/functions/patternFunctions.ts b/src/ExcludeFolders/functions/patternFunctions.ts index 8e4e4ae..db69acf 100644 --- a/src/ExcludeFolders/functions/patternFunctions.ts +++ b/src/ExcludeFolders/functions/patternFunctions.ts @@ -5,79 +5,97 @@ import type { SettingsTab } from '../../settings/SettingsTab'; import { addExcludedFolder, resyncArray, updateExcludedFolder } from './folderFunctions'; import PatternSettings from '../modals/PatternSettings'; -export function updatePattern(plugin: FolderNotesPlugin, pattern: ExcludePattern, newPattern: ExcludePattern) { - plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== pattern.id); +const REGEX_PREFIX = '{regex}'; +const STAR = '*'; +const INDEX_START = 0; +const SLICE_START_ONE = 1; +const SLICE_EXCLUDE_LAST = -1; + +function matchesPatternSpec(raw: string | undefined, folderName: string): boolean { + if (!raw) return false; + const string = raw.trim(); + const isRegex = string.startsWith(REGEX_PREFIX); + const hasStartStar = string.startsWith(STAR); + const hasEndStar = string.endsWith(STAR); + if (!isRegex && !(hasStartStar || hasEndStar)) return false; + + if (isRegex) { + const body = string.replace(REGEX_PREFIX, '').trim(); + if (body === '') return false; + try { + return new RegExp(body).test(folderName); + } catch { + return false; + } + } + + if (hasStartStar && hasEndStar) { + const inner = string.slice(SLICE_START_ONE, SLICE_EXCLUDE_LAST); + return folderName.includes(inner); + } + if (hasStartStar) { + const suffix = string.slice(SLICE_START_ONE); + return folderName.endsWith(suffix); + } + if (hasEndStar) { + const prefix = string.slice(INDEX_START, SLICE_EXCLUDE_LAST); + return folderName.startsWith(prefix); + } + return false; +} + +export function updatePattern( + plugin: FolderNotesPlugin, + pattern: ExcludePattern, + newPattern: ExcludePattern, +): void { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter( + (folder) => folder.id !== pattern.id, + ); addExcludedFolder(plugin, newPattern); } -export function deletePattern(plugin: FolderNotesPlugin, pattern: ExcludePattern) { - plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== pattern.id || folder.type === 'folder'); - plugin.saveSettings(true); +export async function deletePattern( + plugin: FolderNotesPlugin, + pattern: ExcludePattern, +): Promise { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter( + (folder) => folder.id !== pattern.id || folder.type === 'folder', + ); + await plugin.saveSettings(true); resyncArray(plugin); } -export function getExcludedFoldersByPattern(plugin: FolderNotesPlugin, folderName: string): ExcludePattern[] { - return plugin.settings.excludeFolders.filter((s) => s.type === 'pattern').filter((pattern) => { - if (!pattern.string) { return false; } - const string = pattern.string.trim(); - if (!string.startsWith('{regex}') && !(string.startsWith('*') || string.endsWith('*'))) { return false; } - const regex = string.replace('{regex}', '').trim(); - if (string.startsWith('{regex}') && regex === '') { return false; } - if (regex !== undefined && string.startsWith('{regex}')) { - const match = new RegExp(regex).exec(folderName); - if (match) { - return true; - } - } else if (string.startsWith('*') && string.endsWith('*')) { - if (folderName.includes(string.slice(1, -1))) { - return true; - } - } else if (string.startsWith('*')) { - if (folderName.endsWith(string.slice(1))) { - return true; - } - } else if (string.endsWith('*')) { - if (folderName.startsWith(string.slice(0, -1))) { - return true; - } - } - }); +export function getExcludedFoldersByPattern( + plugin: FolderNotesPlugin, + folderName: string, +): ExcludePattern[] { + return plugin.settings.excludeFolders + .filter((s) => s.type === 'pattern') + .filter((pattern) => matchesPatternSpec(pattern.string, folderName)) as ExcludePattern[]; } -export function getExcludedFolderByPattern(plugin: FolderNotesPlugin, folderName: string): ExcludePattern | undefined{ - return plugin.settings.excludeFolders.filter((s) => s.type === 'pattern').find((pattern) => { - if (!pattern.string) { return false; } - const string = pattern.string.trim(); - if (!string.startsWith('{regex}') && !(string.startsWith('*') || string.endsWith('*'))) { return false; } - const regex = string.replace('{regex}', '').trim(); - if (string.startsWith('{regex}') && regex === '') { return false; } - if (regex !== undefined && string.startsWith('{regex}')) { - const match = new RegExp(regex).exec(folderName); - if (match) { - return true; - } - } else if (string.startsWith('*') && string.endsWith('*')) { - if (folderName.includes(string.slice(1, -1))) { - return true; - } - } else if (string.startsWith('*')) { - if (folderName.endsWith(string.slice(1))) { - return true; - } - } else if (string.endsWith('*')) { - if (folderName.startsWith(string.slice(0, -1))) { - return true; - } - } - }); +export function getExcludedFolderByPattern( + plugin: FolderNotesPlugin, + folderName: string, +): ExcludePattern | undefined { + return ( + plugin.settings.excludeFolders + .filter((s) => s.type === 'pattern') + .find((pattern) => matchesPatternSpec(pattern.string, folderName)) + ) as ExcludePattern | undefined; } -export function addExcludePatternListItem(settings: SettingsTab, containerEl: HTMLElement, pattern: ExcludePattern) { +export function addExcludePatternListItem( + settings: SettingsTab, + containerEl: HTMLElement, + pattern: ExcludePattern, +): void { const { plugin } = settings; const setting = new Setting(containerEl); setting.setClass('fn-exclude-folder-list'); setting.addSearch((cb) => { - // @ts-ignore + // @ts-expect-error Obsidian's public types don't include containerEl on this control cb.containerEl.addClass('fn-exclude-folder-path'); cb.setPlaceholder('Pattern'); cb.setValue(pattern.string); @@ -102,11 +120,18 @@ export function addExcludePatternListItem(settings: SettingsTab, containerEl: HT if (pattern.position === 0) { return; } pattern.position -= 1; updatePattern(plugin, pattern, pattern); - const oldPattern = plugin.settings.excludeFolders.find((folder) => folder.position === pattern.position); + const oldPattern = plugin.settings.excludeFolders.find( + (folder) => folder.position === pattern.position, + ); if (oldPattern) { oldPattern.position += 1; if (oldPattern.type === 'pattern') { - updatePattern(plugin, oldPattern, oldPattern); + const pat = oldPattern as ExcludePattern; + updatePattern( + plugin, + pat, + pat, + ); } else { updateExcludedFolder(plugin, oldPattern, oldPattern); } @@ -125,11 +150,18 @@ export function addExcludePatternListItem(settings: SettingsTab, containerEl: HT pattern.position += 1; updatePattern(plugin, pattern, pattern); - const oldPattern = plugin.settings.excludeFolders.find((folder) => folder.position === pattern.position); + const oldPattern = plugin.settings.excludeFolders.find( + (folder) => folder.position === pattern.position, + ); if (oldPattern) { oldPattern.position -= 1; if (oldPattern.type === 'pattern') { - updatePattern(plugin, oldPattern, oldPattern); + const pat = oldPattern as ExcludePattern; + updatePattern( + plugin, + pat, + pat, + ); } else { updateExcludedFolder(plugin, oldPattern, oldPattern); } @@ -143,7 +175,7 @@ export function addExcludePatternListItem(settings: SettingsTab, containerEl: HT cb.setIcon('trash-2'); cb.setTooltip('Delete pattern'); cb.onClick(() => { - deletePattern(plugin, pattern); + void deletePattern(plugin, pattern); setting.clear(); setting.settingEl.remove(); }); diff --git a/src/ExcludeFolders/functions/whitelistFolderFunctions.ts b/src/ExcludeFolders/functions/whitelistFolderFunctions.ts index 156a716..e700eac 100644 --- a/src/ExcludeFolders/functions/whitelistFolderFunctions.ts +++ b/src/ExcludeFolders/functions/whitelistFolderFunctions.ts @@ -2,15 +2,21 @@ import type FolderNotesPlugin from '../../main'; import { getFolderNameFromPathString, getFolderPathFromString } from '../../functions/utils'; import type { WhitelistedFolder } from '../WhitelistFolder'; import { WhitelistedPattern } from '../WhitelistPattern'; -import { Setting, Platform, ButtonComponent } from 'obsidian'; +import { Setting, ButtonComponent } from 'obsidian'; import { FolderSuggest } from '../../suggesters/FolderSuggester'; import type { SettingsTab } from '../../settings/SettingsTab'; import WhitelistFolderSettings from '../modals/WhitelistFolderSettings'; -import { updateWhitelistedPattern, getWhitelistedFoldersByPattern, addWhitelistedPatternListItem } from './whitelistPatternFunctions'; -Platform.isMobileApp; - -export function getWhitelistedFolder(plugin: FolderNotesPlugin, path: string) { - let whitelistedFolder = {} as WhitelistedFolder | WhitelistedPattern | undefined; +import { + updateWhitelistedPattern, + getWhitelistedFoldersByPattern, + addWhitelistedPatternListItem, +} from './whitelistPatternFunctions'; + +export function getWhitelistedFolder( + plugin: FolderNotesPlugin, + path: string, +): WhitelistedFolder | WhitelistedPattern | undefined { + let whitelistedFolder: Partial | undefined = {}; const folderName = getFolderNameFromPathString(path); const matchedPatterns = getWhitelistedFoldersByPattern(plugin, folderName); const whitelistedFolders = getWhitelistedFoldersByPath(plugin, path); @@ -25,21 +31,30 @@ export function getWhitelistedFolder(plugin: FolderNotesPlugin, path: string) { if (combinedWhitelistedFolders.length > 0) { for (const matchedFolder of combinedWhitelistedFolders) { propertiesToCopy.forEach((property) => { - if (matchedFolder[property] === true) { - (whitelistedFolder as any)[property] = true; - } else if (!matchedFolder[property]) { - (whitelistedFolder as any)[property] = false; + const value = (matchedFolder as Partial)[property]; + if (value === true) { + (whitelistedFolder as Partial)[property] = true as never; + } else if (!value) { + (whitelistedFolder as Partial)[property] = false as never; } }); } } - if ((whitelistedFolder instanceof Object) && Object.keys(whitelistedFolder).length === 0) { whitelistedFolder = undefined; } + if ( + whitelistedFolder + && Object.keys(whitelistedFolder).length === 0 + ) { + whitelistedFolder = undefined; + } - return whitelistedFolder; + return whitelistedFolder as WhitelistedFolder | WhitelistedPattern | undefined; } -export function getWhitelistedFolderByPath(plugin: FolderNotesPlugin, path: string) { +export function getWhitelistedFolderByPath( + plugin: FolderNotesPlugin, + path: string, +): WhitelistedFolder | WhitelistedPattern | undefined { return plugin.settings.whitelistFolders.find((whitelistedFolder) => { if (whitelistedFolder.path === path) { return true; } if (!whitelistedFolder.subFolders) { return false; } @@ -47,7 +62,10 @@ export function getWhitelistedFolderByPath(plugin: FolderNotesPlugin, path: stri }); } -export function getWhitelistedFoldersByPath(plugin: FolderNotesPlugin, path: string) { +export function getWhitelistedFoldersByPath( + plugin: FolderNotesPlugin, + path: string, +): Array { return plugin.settings.whitelistFolders.filter((whitelistedFolder) => { if (whitelistedFolder.path === path) { return true; } if (!whitelistedFolder.subFolders) { return false; } @@ -55,37 +73,58 @@ export function getWhitelistedFoldersByPath(plugin: FolderNotesPlugin, path: str }); } -export function addWhitelistedFolder(plugin: FolderNotesPlugin, whitelistedFolder: WhitelistedFolder) { +export function addWhitelistedFolder( + plugin: FolderNotesPlugin, + whitelistedFolder: WhitelistedFolder | WhitelistedPattern, +): void { plugin.settings.whitelistFolders.push(whitelistedFolder); - plugin.saveSettings(true); + void plugin.saveSettings(true); } -export function deleteWhitelistedFolder(plugin: FolderNotesPlugin, whitelistedFolder: WhitelistedFolder) { - plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== whitelistedFolder.id || folder.type === 'pattern'); - plugin.saveSettings(true); +export async function deleteWhitelistedFolder( + plugin: FolderNotesPlugin, + whitelistedFolder: WhitelistedFolder | WhitelistedPattern, +): Promise { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter( + (folder) => folder.id !== whitelistedFolder.id || folder.type === 'pattern', + ); + await plugin.saveSettings(true); resyncArray(plugin); } -export function updateWhitelistedFolder(plugin: FolderNotesPlugin, whitelistedFolder: WhitelistedFolder, newWhitelistFolder: WhitelistedFolder) { - plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== whitelistedFolder.id); +export function updateWhitelistedFolder( + plugin: FolderNotesPlugin, + whitelistedFolder: WhitelistedFolder, + newWhitelistFolder: WhitelistedFolder, +): void { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter( + (folder) => folder.id !== whitelistedFolder.id, + ); addWhitelistedFolder(plugin, newWhitelistFolder); } -export function resyncArray(plugin: FolderNotesPlugin) { - plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.sort((a, b) => a.position - b.position); +export function resyncArray(plugin: FolderNotesPlugin): void { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.sort( + (a, b) => a.position - b.position, + ); plugin.settings.whitelistFolders.forEach((folder, index) => { folder.position = index; }); - plugin.saveSettings(); + void plugin.saveSettings(); } - -export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: HTMLElement, whitelistedFolder: WhitelistedFolder) { +export function addWhitelistFolderListItem( + settings: SettingsTab, + containerEl: HTMLElement, + whitelistedFolder: WhitelistedFolder, +): void { const { plugin } = settings; const setting = new Setting(containerEl); setting.setClass('fn-exclude-folder-list'); - const inputContainer = setting.settingEl.createDiv({ cls: 'fn-whitelist-folder-input-container' }); + const inputContainer = setting.settingEl.createDiv({ + cls: 'fn-whitelist-folder-input-container', + }); const SearchComponent = new Setting(inputContainer); SearchComponent.addSearch((cb) => { new FolderSuggest( @@ -93,14 +132,19 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H plugin, true, ); - // @ts-ignore + // @ts-expect-error Obsidian's public types don't include this property cb.containerEl.addClass('fn-exclude-folder-path'); cb.setPlaceholder('Folder path'); cb.setValue(whitelistedFolder.path); cb.onChange((value) => { if (value.startsWith('{regex}') || value.includes('*')) { - deleteWhitelistedFolder(plugin, whitelistedFolder); - const pattern = new WhitelistedPattern(value, plugin.settings.whitelistFolders.length, undefined, plugin); + void deleteWhitelistedFolder(plugin, whitelistedFolder); + const pattern = new WhitelistedPattern( + value, + plugin.settings.whitelistFolders.length, + undefined, + plugin, + ); addWhitelistedFolder(plugin, pattern); addWhitelistedPatternListItem(settings, containerEl, pattern); setting.clear(); @@ -127,7 +171,9 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H if (whitelistedFolder.position === 0) { return; } whitelistedFolder.position -= 1; updateWhitelistedFolder(plugin, whitelistedFolder, whitelistedFolder); - const oldWhitelistedFolder = plugin.settings.whitelistFolders.find((folder) => folder.position === whitelistedFolder.position); + const oldWhitelistedFolder = plugin.settings.whitelistFolders.find( + (folder) => folder.position === whitelistedFolder.position, + ); if (oldWhitelistedFolder) { oldWhitelistedFolder.position += 1; if (oldWhitelistedFolder.type === 'pattern') { @@ -149,7 +195,9 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H whitelistedFolder.position += 1; updateWhitelistedFolder(plugin, whitelistedFolder, whitelistedFolder); - const oldWhitelistedFolder = plugin.settings.whitelistFolders.find((folder) => folder.position === whitelistedFolder.position); + const oldWhitelistedFolder = plugin.settings.whitelistFolders.find( + (folder) => folder.position === whitelistedFolder.position, + ); if (oldWhitelistedFolder) { oldWhitelistedFolder.position -= 1; if (oldWhitelistedFolder.type === 'pattern') { @@ -166,7 +214,7 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H .setIcon('trash-2') .setTooltip('Delete excluded folder') .onClick(() => { - deleteWhitelistedFolder(plugin, whitelistedFolder); + void deleteWhitelistedFolder(plugin, whitelistedFolder); setting.clear(); setting.settingEl.remove(); }); diff --git a/src/ExcludeFolders/functions/whitelistPatternFunctions.ts b/src/ExcludeFolders/functions/whitelistPatternFunctions.ts index 30f5363..6c50e68 100644 --- a/src/ExcludeFolders/functions/whitelistPatternFunctions.ts +++ b/src/ExcludeFolders/functions/whitelistPatternFunctions.ts @@ -6,84 +6,106 @@ import WhitelistPatternSettings from '../modals/WhitelistPatternSettings'; import type { WhitelistedPattern } from '../WhitelistPattern'; import { addWhitelistedFolder, updateWhitelistedFolder } from './whitelistFolderFunctions'; -export function updateWhitelistedPattern(plugin: FolderNotesPlugin, pattern: WhitelistedPattern, newPattern: WhitelistedPattern) { - plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== pattern.id); +const REGEX_PREFIX = '{regex}'; +const STAR = '*'; +const SLICE_START_ONE = 1; +const SLICE_EXCLUDE_LAST = -1; + +function matchesPatternSpec(raw: string | undefined, folderName: string): boolean { + if (!raw) return false; + const string = raw.trim(); + const isRegex = string.startsWith(REGEX_PREFIX); + const hasStartStar = string.startsWith(STAR); + const hasEndStar = string.endsWith(STAR); + if (!isRegex && !(hasStartStar || hasEndStar)) return false; + + if (isRegex) { + const body = string.replace(REGEX_PREFIX, '').trim(); + if (body === '') return false; + try { + return new RegExp(body).test(folderName); + } catch { + return false; + } + } + + if (hasStartStar && hasEndStar) { + const inner = string.slice(SLICE_START_ONE, SLICE_EXCLUDE_LAST); + return folderName.includes(inner); + } + if (hasStartStar) { + const suffix = string.slice(SLICE_START_ONE); + return folderName.endsWith(suffix); + } + if (hasEndStar) { + const prefix = string.slice(0, SLICE_EXCLUDE_LAST); + return folderName.startsWith(prefix); + } + return false; +} + +export function updateWhitelistedPattern( + plugin: FolderNotesPlugin, + pattern: WhitelistedPattern, + newPattern: WhitelistedPattern, +): void { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter( + (folder) => folder.id !== pattern.id, + ); addWhitelistedFolder(plugin, newPattern); } -export function deletePattern(plugin: FolderNotesPlugin, pattern: WhitelistedPattern) { - plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== pattern.id || folder.type === 'folder'); - plugin.saveSettings(true); +export async function deletePattern( + plugin: FolderNotesPlugin, + pattern: WhitelistedPattern, +): Promise { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter( + (folder) => folder.id !== pattern.id || folder.type === 'folder', + ); + await plugin.saveSettings(true); resyncArray(plugin); } -export function getWhitelistedFolderByPattern(plugin: FolderNotesPlugin, folderName: string) { - return plugin.settings.whitelistFolders.filter((s) => s.type === 'pattern').find((pattern) => { - if (!pattern.string) { return false; } - const string = pattern.string.trim(); - if (!string.startsWith('{regex}') && !(string.startsWith('*') || string.endsWith('*'))) { return false; } - const regex = string.replace('{regex}', '').trim(); - if (string.startsWith('{regex}') && regex === '') { return false; } - if (regex !== undefined && string.startsWith('{regex}')) { - const match = new RegExp(regex).exec(folderName); - if (match) { - return true; - } - } else if (string.startsWith('*') && string.endsWith('*')) { - if (folderName.includes(string.slice(1, -1))) { - return true; - } - } else if (string.startsWith('*')) { - if (folderName.endsWith(string.slice(1))) { - return true; - } - } else if (string.endsWith('*')) { - if (folderName.startsWith(string.slice(0, -1))) { - return true; - } - } - }); +export function getWhitelistedFolderByPattern( + plugin: FolderNotesPlugin, + folderName: string, +): WhitelistedPattern | undefined { + return ( + plugin.settings.whitelistFolders + .filter((s) => s.type === 'pattern') + .find((pattern) => matchesPatternSpec(pattern.string, folderName)) + ) as WhitelistedPattern | undefined; } -export function getWhitelistedFoldersByPattern(plugin: FolderNotesPlugin, folderName: string) { - return plugin.settings.whitelistFolders.filter((s) => s.type === 'pattern').filter((pattern) => { - if (!pattern.string) { return false; } - const string = pattern.string.trim(); - if (!string.startsWith('{regex}') && !(string.startsWith('*') || string.endsWith('*'))) { return false; } - const regex = string.replace('{regex}', '').trim(); - if (string.startsWith('{regex}') && regex === '') { return false; } - if (regex !== undefined && string.startsWith('{regex}')) { - const match = new RegExp(regex).exec(folderName); - if (match) { - return true; - } - } else if (string.startsWith('*') && string.endsWith('*')) { - if (folderName.includes(string.slice(1, -1))) { - return true; - } - } else if (string.startsWith('*')) { - if (folderName.endsWith(string.slice(1))) { - return true; - } - } else if (string.endsWith('*')) { - if (folderName.startsWith(string.slice(0, -1))) { - return true; - } - } - }); +export function getWhitelistedFoldersByPattern( + plugin: FolderNotesPlugin, + folderName: string, +): WhitelistedPattern[] { + return ( + plugin.settings.whitelistFolders + .filter((s) => s.type === 'pattern') + .filter((pattern) => matchesPatternSpec(pattern.string, folderName)) + ) as WhitelistedPattern[]; } -export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl: HTMLElement, pattern: WhitelistedPattern) { +export function addWhitelistedPatternListItem( + settings: SettingsTab, + containerEl: HTMLElement, + pattern: WhitelistedPattern, +): void { const { plugin } = settings; const setting = new Setting(containerEl); setting.setClass('fn-exclude-folder-list'); setting.addSearch((cb) => { - // @ts-ignore + // @ts-expect-error Obsidian's public types don't expose containerEl on this control cb.containerEl.addClass('fn-exclude-folder-path'); cb.setPlaceholder('Pattern'); cb.setValue(pattern.string); cb.onChange((value) => { - if (plugin.settings.whitelistFolders.find((folder) => folder.string === value)) { return; } + const exists = plugin.settings.whitelistFolders.some( + (folder) => folder.string === value, + ); + if (exists) { return; } pattern.string = value; updateWhitelistedPattern(plugin, pattern, pattern); }); @@ -103,11 +125,17 @@ export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl if (pattern.position === 0) { return; } pattern.position -= 1; updateWhitelistedPattern(plugin, pattern, pattern); - const oldPattern = plugin.settings.whitelistFolders.find((folder) => folder.position === pattern.position); + const oldPattern = plugin.settings.whitelistFolders.find( + (folder) => folder.position === pattern.position, + ); if (oldPattern) { oldPattern.position += 1; if (oldPattern.type === 'pattern') { - updateWhitelistedPattern(plugin, oldPattern, oldPattern); + updateWhitelistedPattern( + plugin, + oldPattern as WhitelistedPattern, + oldPattern as WhitelistedPattern, + ); } else { updateWhitelistedFolder(plugin, oldPattern, oldPattern); } @@ -126,11 +154,17 @@ export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl pattern.position += 1; updateWhitelistedPattern(plugin, pattern, pattern); - const oldPattern = plugin.settings.whitelistFolders.find((folder) => folder.position === pattern.position); + const oldPattern = plugin.settings.whitelistFolders.find( + (folder) => folder.position === pattern.position, + ); if (oldPattern) { oldPattern.position -= 1; if (oldPattern.type === 'pattern') { - updateWhitelistedPattern(plugin, oldPattern, oldPattern); + updateWhitelistedPattern( + plugin, + oldPattern as WhitelistedPattern, + oldPattern as WhitelistedPattern, + ); } else { updateWhitelistedFolder(plugin, oldPattern, oldPattern); } @@ -143,7 +177,7 @@ export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl cb.setIcon('trash-2'); cb.setTooltip('Delete pattern'); cb.onClick(() => { - deletePattern(plugin, pattern); + void deletePattern(plugin, pattern); setting.clear(); setting.settingEl.remove(); }); diff --git a/src/ExcludeFolders/modals/ExcludeFolderSettings.ts b/src/ExcludeFolders/modals/ExcludeFolderSettings.ts index 7f634b2..7537889 100644 --- a/src/ExcludeFolders/modals/ExcludeFolderSettings.ts +++ b/src/ExcludeFolders/modals/ExcludeFolderSettings.ts @@ -1,5 +1,4 @@ -import type { App } from 'obsidian'; -import { Modal, Setting } from 'obsidian'; +import { Modal, Setting, type App } from 'obsidian'; import type FolderNotesPlugin from '../../main'; import type { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; import { updateCSSClassesForFolder } from 'src/functions/styleFunctions'; @@ -13,10 +12,10 @@ export default class ExcludedFolderSettings extends Modal { this.app = app; this.excludedFolder = excludedFolder; } - onOpen() { + onOpen(): void { this.display(); } - display() { + display(): void { const { contentEl } = this; contentEl.empty(); contentEl.createEl('h2', { text: 'Excluded folder settings' }); @@ -108,9 +107,9 @@ export default class ExcludedFolderSettings extends Modal { }), ); } - } - onClose() { + + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/ExcludeFolders/modals/PatternSettings.ts b/src/ExcludeFolders/modals/PatternSettings.ts index 426f228..49d53c5 100644 --- a/src/ExcludeFolders/modals/PatternSettings.ts +++ b/src/ExcludeFolders/modals/PatternSettings.ts @@ -1,5 +1,4 @@ -import type { App } from 'obsidian'; -import { Modal, Setting } from 'obsidian'; +import { Modal, Setting, type App } from 'obsidian'; import type FolderNotesPlugin from '../../main'; import type { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern'; import { refreshAllFolderStyles } from 'src/functions/styleFunctions'; @@ -14,16 +13,19 @@ export default class PatternSettings extends Modal { this.app = app; this.pattern = pattern; } - onOpen() { + + onOpen(): void { this.display(); } - display() { + + display(): void { const { contentEl } = this; contentEl.empty(); contentEl.createEl('h2', { text: 'Pattern settings' }); new Setting(contentEl) .setName('Disable folder name sync') + // eslint-disable-next-line max-len .setDesc('Choose if the folder name should be renamed when the file name has been changed') .addToggle((toggle) => toggle @@ -36,6 +38,7 @@ export default class PatternSettings extends Modal { new Setting(contentEl) .setName('Disable auto creation of folder notes in this folder') + // eslint-disable-next-line max-len .setDesc('Choose if a folder note should be created when a new folder is created that matches this pattern') .addToggle((toggle) => toggle @@ -98,9 +101,9 @@ export default class PatternSettings extends Modal { }), ); } - } - onClose() { + + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/ExcludeFolders/modals/WhitelistFolderSettings.ts b/src/ExcludeFolders/modals/WhitelistFolderSettings.ts index 6f65cc7..f7e0c76 100644 --- a/src/ExcludeFolders/modals/WhitelistFolderSettings.ts +++ b/src/ExcludeFolders/modals/WhitelistFolderSettings.ts @@ -1,5 +1,4 @@ -import type { App } from 'obsidian'; -import { Modal, Setting } from 'obsidian'; +import { Modal, Setting, type App } from 'obsidian'; import type FolderNotesPlugin from '../../main'; import type { WhitelistedFolder } from '../WhitelistFolder'; export default class WhitelistFolderSettings extends Modal { @@ -12,10 +11,12 @@ export default class WhitelistFolderSettings extends Modal { this.app = app; this.whitelistedFolder = whitelistedFolder; } - onOpen() { + + onOpen(): void { this.display(); } - display() { + + display(): void { const { contentEl } = this; contentEl.empty(); contentEl.createEl('h2', { text: 'Whitelisted folder settings' }); @@ -33,6 +34,7 @@ export default class WhitelistFolderSettings extends Modal { new Setting(contentEl) .setName('Enable folder name sync') + // eslint-disable-next-line max-len .setDesc('Choose if the name of a folder note should be renamed when the folder name is changed') .addToggle((toggle) => toggle @@ -107,7 +109,7 @@ export default class WhitelistFolderSettings extends Modal { } } - onClose() { + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/ExcludeFolders/modals/WhitelistPatternSettings.ts b/src/ExcludeFolders/modals/WhitelistPatternSettings.ts index 45a3688..4f62f29 100644 --- a/src/ExcludeFolders/modals/WhitelistPatternSettings.ts +++ b/src/ExcludeFolders/modals/WhitelistPatternSettings.ts @@ -1,5 +1,4 @@ -import type { App } from 'obsidian'; -import { Modal, Setting } from 'obsidian'; +import { Modal, Setting, type App } from 'obsidian'; import type FolderNotesPlugin from '../../main'; import type { WhitelistedPattern } from '../WhitelistPattern'; @@ -13,15 +12,18 @@ export default class WhitelistPatternSettings extends Modal { this.app = app; this.pattern = pattern; } - onOpen() { + + onOpen(): void { this.display(); } - display() { + + display(): void { const { contentEl } = this; contentEl.empty(); contentEl.createEl('h2', { text: 'Whitelisted pattern settings' }); new Setting(contentEl) .setName('Enable folder name sync') + // eslint-disable-next-line max-len .setDesc('Choose if the name of a folder note should be renamed when the folder name is changed') .addToggle((toggle) => toggle @@ -82,9 +84,9 @@ export default class WhitelistPatternSettings extends Modal { }), ); } - } - onClose() { + + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts b/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts index 2321f34..b00342d 100644 --- a/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts +++ b/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts @@ -1,9 +1,11 @@ -import type { App } from 'obsidian'; -import { Modal, Setting } from 'obsidian'; +import { Modal, Setting, type App } from 'obsidian'; import type { SettingsTab } from 'src/settings/SettingsTab'; import type FolderNotesPlugin from '../../main'; import { WhitelistedFolder } from '../WhitelistFolder'; -import { addWhitelistFolderListItem, addWhitelistedFolder } from '../functions/whitelistFolderFunctions'; +import { + addWhitelistFolderListItem, + addWhitelistedFolder, +} from '../functions/whitelistFolderFunctions'; import { addWhitelistedPatternListItem } from '../functions/whitelistPatternFunctions'; export default class WhitelistedFoldersSettings extends Modal { @@ -16,7 +18,8 @@ export default class WhitelistedFoldersSettings extends Modal { this.settingsTab = settingsTab; this.app = settingsTab.app; } - onOpen() { + + onOpen(): void { const { contentEl } = this; contentEl.createEl('h2', { text: 'Manage whitelisted folders' }); @@ -29,22 +32,31 @@ export default class WhitelistedFoldersSettings extends Modal { cb.setClass('add-exclude-folder'); cb.setTooltip('Add whitelisted folder'); cb.onClick(() => { - const whitelistedFolder = new WhitelistedFolder('', this.plugin.settings.whitelistFolders.length, undefined, this.plugin); - addWhitelistFolderListItem(this.plugin.settingsTab, contentEl, whitelistedFolder); + const whitelistedFolder = new WhitelistedFolder( + '', this.plugin.settings.whitelistFolders.length, + undefined, this.plugin, + ); + addWhitelistFolderListItem( + this.plugin.settingsTab, contentEl, whitelistedFolder, + ); addWhitelistedFolder(this.plugin, whitelistedFolder); this.settingsTab.display(); }); }); - this.plugin.settings.whitelistFolders.sort((a, b) => a.position - b.position).forEach((whitelistedFolder) => { - if (whitelistedFolder.string?.trim() !== '' && whitelistedFolder.path?.trim() === '') { - addWhitelistedPatternListItem(this.settingsTab, contentEl, whitelistedFolder); - } else { - addWhitelistFolderListItem(this.settingsTab, contentEl, whitelistedFolder); - } - }); + + this.plugin.settings.whitelistFolders + .sort((a, b) => a.position - b.position) + .forEach((whitelistedFolder) => { + if (whitelistedFolder.string?.trim() !== '' && + whitelistedFolder.path?.trim() === '') { + addWhitelistedPatternListItem(this.settingsTab, contentEl, whitelistedFolder); + } else { + addWhitelistFolderListItem(this.settingsTab, contentEl, whitelistedFolder); + } + }); } - onClose() { + onClose(): void { const { contentEl } = this; contentEl.empty(); } From 7c31c7cec57ebd1aefdb69f0e365674abc134138 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:39:33 +0200 Subject: [PATCH 38/60] Every remaining file --- src/Commands.ts | 470 ++++++++++-------- src/functions/ListComponent.ts | 26 +- src/functions/excalidraw.ts | 33 +- src/functions/folderNoteFunctions.ts | 454 ++++++++++++----- src/functions/styleFunctions.ts | 83 +++- src/functions/utils.ts | 23 +- src/globals.d.ts | 2 - src/main.ts | 292 +++++++---- src/modals/AddSupportedFileType.ts | 12 +- src/modals/AskForExtension.ts | 27 +- src/modals/DeleteConfirmation.ts | 28 +- src/modals/ExistingNote.ts | 29 +- src/modals/FolderName.ts | 23 +- src/modals/NewFolderName.ts | 27 +- src/settings/ExcludedFoldersSettings.ts | 49 +- src/settings/FileExplorerSettings.ts | 19 +- src/settings/FolderOverviewSettings.ts | 15 +- src/settings/GeneralSettings.ts | 72 ++- src/settings/PathSettings.ts | 3 +- src/settings/SettingsTab.ts | 49 +- src/settings/modals/BackupWarning.ts | 25 +- src/settings/modals/CreateFnForEveryFolder.ts | 38 +- src/settings/modals/RenameFns.ts | 10 +- src/suggesters/FolderSuggester.ts | 14 +- src/suggesters/Suggest.ts | 17 +- src/suggesters/TemplateSuggester.ts | 18 +- src/template.ts | 66 ++- 27 files changed, 1319 insertions(+), 605 deletions(-) diff --git a/src/Commands.ts b/src/Commands.ts index 56f7dd1..a27b541 100644 --- a/src/Commands.ts +++ b/src/Commands.ts @@ -1,11 +1,35 @@ -import type { App, Menu, TAbstractFile, Editor, MarkdownView } from 'obsidian'; -import { TFolder, Notice, TFile, Platform } from 'obsidian'; +import { + TFolder, + Notice, + TFile, + Platform, + type App, + type Menu, + type TAbstractFile, + type Editor, + type MarkdownView, +} from 'obsidian'; import type FolderNotesPlugin from './main'; -import { getFolderNote, createFolderNote, deleteFolderNote, turnIntoFolderNote, openFolderNote, extractFolderName, detachFolderNote } from './functions/folderNoteFunctions'; +import { + getFolderNote, + createFolderNote, + deleteFolderNote, + turnIntoFolderNote, + openFolderNote, + extractFolderName, + detachFolderNote, +} from './functions/folderNoteFunctions'; import { ExcludedFolder } from './ExcludeFolders/ExcludeFolder'; import { getFolderPathFromString, getFileExplorerActiveFolder } from './functions/utils'; -import { deleteExcludedFolder, getDetachedFolder, getExcludedFolder } from './ExcludeFolders/functions/folderFunctions'; -import { hideFolderNoteInFileExplorer, showFolderNoteInFileExplorer } from './functions/styleFunctions'; +import { + deleteExcludedFolder, + getDetachedFolder, + getExcludedFolder, +} from './ExcludeFolders/functions/folderFunctions'; +import { + hideFolderNoteInFileExplorer, + showFolderNoteInFileExplorer, +} from './functions/styleFunctions'; @@ -16,12 +40,13 @@ export class Commands { this.plugin = plugin; this.app = app; } - registerCommands() { + registerCommands(): void { this.editorCommands(); this.fileCommands(); this.regularCommands(); } - regularCommands() { + + regularCommands(): void { this.plugin.addCommand({ id: 'turn-into-folder-note', name: 'Use this file as the folder note for its parent folder', @@ -52,7 +77,8 @@ export class Commands { if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { return new Notice('Folder already exists'); } - const automaticallyCreateFolderNote = this.plugin.settings.autoCreate; + const automaticallyCreateFolderNote = + this.plugin.settings.autoCreate; this.plugin.settings.autoCreate = false; this.plugin.saveSettings(); await this.plugin.app.vault.createFolder(newPath); @@ -187,6 +213,7 @@ export class Commands { const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':']; for (const char of blacklist) { if (text.includes(char)) { + // eslint-disable-next-line max-len new Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?'); return false; } @@ -207,13 +234,18 @@ export class Commands { createFolderNote(this.plugin, text, false); } else { - folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text); + const folderFullPath = folderPath + '/' + text; + folder = this.plugin.app.vault.getAbstractFileByPath(folderFullPath); if (folder instanceof TFolder) { new Notice('Folder note already exists'); return false; } if (this.plugin.settings.storageLocation === 'parentFolder') { - if (this.app.vault.getAbstractFileByPath(folderPath + '/' + text + this.plugin.settings.folderNoteType)) { + if ( + this.app.vault.getAbstractFileByPath( + folderPath + '/' + text + this.plugin.settings.folderNoteType, + ) + ) { new Notice('File already exists'); return false; } @@ -221,7 +253,9 @@ export class Commands { this.plugin.app.vault.createFolder(folderPath + '/' + text); createFolderNote(this.plugin, folderPath + '/' + text, false); } - const fileName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', text); + + const { folderNoteName } = this.plugin.settings; + const fileName = folderNoteName.replace('{{folder_name}}', text); if (fileName !== text) { editor.replaceSelection(`[[${fileName}]]`); } else { @@ -234,213 +268,243 @@ export class Commands { }); } - fileCommands() { - this.plugin.registerEvent(this.app.workspace.on('file-menu', (menu: Menu, file: TAbstractFile) => { - let folder: TAbstractFile | TFolder | null = file.parent; - if (file instanceof TFile) { - if (this.plugin.settings.storageLocation === 'insideFolder') { - folder = file.parent; - } else { - const fileName = extractFolderName(this.plugin.settings.folderNoteName, file.basename); - if (fileName) { - if (file.parent?.path === '' || file.parent?.path === '/') { - folder = this.plugin.app.vault.getAbstractFileByPath(fileName); - } else { - folder = this.plugin.app.vault.getAbstractFileByPath(file.parent?.path + '/' + fileName); + fileCommands(): void { + this.plugin.registerEvent( + this.app.workspace.on('file-menu', (menu: Menu, file: TAbstractFile) => { + let folder: TAbstractFile | TFolder | null = file.parent; + if (file instanceof TFile) { + if (this.plugin.settings.storageLocation === 'insideFolder') { + folder = file.parent; + } else { + const { folderNoteName } = this.plugin.settings; + const fileName = extractFolderName(folderNoteName, file.basename); + if (fileName) { + if (file.parent?.path === '' || file.parent?.path === '/') { + folder = this.plugin.app.vault.getAbstractFileByPath(fileName); + } else { + folder = this.plugin.app.vault.getAbstractFileByPath( + file.parent?.path + '/' + fileName, + ); + } } } - } - if (folder instanceof TFolder) { - const folderNote = getFolderNote(this.plugin, folder.path); - const excludedFolder = getExcludedFolder(this.plugin, folder.path, true); - if (folderNote?.path === file.path && !excludedFolder?.detached) { return; } - } else if (file.parent instanceof TFolder) { - folder = file.parent; + if (folder instanceof TFolder) { + const folderNote = getFolderNote(this.plugin, folder.path); + const excludedFolder = getExcludedFolder(this.plugin, folder.path, true); + if (folderNote?.path === file.path && !excludedFolder?.detached) { return; } + } else if (file.parent instanceof TFolder) { + folder = file.parent; + } } - } - menu.addItem(async (item) => { - if (Platform.isDesktop && !Platform.isTablet && this.plugin.settings.useSubmenus) { - item - .setTitle('Folder Note Commands') - .setIcon('folder-edit'); - } - let subMenu: Menu; - if (!Platform.isDesktopApp || !Platform.isDesktop || Platform.isTablet || !this.plugin.settings.useSubmenus) { - subMenu = menu; - item.setDisabled(true); - } else { - // @ts-ignore - subMenu = item.setSubmenu() as Menu; - } - if (file instanceof TFile) { - // @ts-ignore - subMenu.addItem((item) => { - item.setTitle('Create folder note') - .setIcon('edit') - .onClick(async () => { - if (!folder) return; - let newPath = folder.path + '/' + file.basename; - if (folder.path === '' || folder.path === '/') { - newPath = file.basename; - } - if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { - return new Notice('Folder already exists'); - } - const automaticallyCreateFolderNote = this.plugin.settings.autoCreate; - this.plugin.settings.autoCreate = false; - this.plugin.saveSettings(); - await this.plugin.app.vault.createFolder(newPath); - const newFolder = this.plugin.app.vault.getAbstractFileByPath(newPath); - if (!(newFolder instanceof TFolder)) return; - await createFolderNote(this.plugin, newFolder.path, true, '.' + file.extension, false, file); - this.plugin.settings.autoCreate = automaticallyCreateFolderNote; - this.plugin.saveSettings(); - }); - }); - if (getFolderPathFromString(file.path) === '') return; - if (!(folder instanceof TFolder)) return; - if (folder.path === '' || folder.path === '/') return; - subMenu.addItem((item) => { - item.setTitle(`Turn into folder note for ${folder?.name}`) - .setIcon('edit') - .onClick(() => { - if (!folder || !(folder instanceof TFolder)) return; - const folderNote = getFolderNote(this.plugin, folder.path); - turnIntoFolderNote(this.plugin, file, folder, folderNote); - }); - }); - } - if (!(file instanceof TFolder)) return; - const excludedFolder = getExcludedFolder(this.plugin, file.path, false); - const detachedExcludedFolder = getDetachedFolder(this.plugin, file.path); - if (excludedFolder && !excludedFolder.hideInSettings) { - // I'm not sure if I'm ever going to add this because of the possibility that a folder got more than one excluded - // subMenu.addItem((item) => { - // item.setTitle('Manage excluded folder') - // .setIcon('settings-2') - // .onClick(() => { - // if (excludedFolder instanceof ExcludedFolder) { - // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open(); - // } else if (excludedFolder instanceof ExcludePattern) { - // new PatternSettings(this.plugin.app, this.plugin, excludedFolder).open(); - // } - // }) - // }) + // eslint-disable-next-line complexity + menu.addItem(async (menuItem) => { + if ( + Platform.isDesktop && + !Platform.isTablet && + this.plugin.settings.useSubmenus + ) { + menuItem + .setTitle('Folder Note Commands') + .setIcon('folder-edit'); + } + let subMenu: Menu; + if ( + !Platform.isDesktopApp || + !Platform.isDesktop || + Platform.isTablet || + !this.plugin.settings.useSubmenus + ) { + subMenu = menu; + menuItem.setDisabled(true); + } else { + subMenu = menuItem.setSubmenu() as Menu; + } + if (file instanceof TFile) { + subMenu.addItem((subItem) => { + subItem.setTitle('Create folder note') + .setIcon('edit') + .onClick(async () => { + if (!folder) return; + let newPath = folder.path + '/' + file.basename; + if (folder.path === '' || folder.path === '/') { + newPath = file.basename; + } + if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { + return new Notice('Folder already exists'); + } + const automaticallyCreateFolderNote = + this.plugin.settings.autoCreate; + this.plugin.settings.autoCreate = false; + this.plugin.saveSettings(); + await this.plugin.app.vault.createFolder(newPath); + const newFolder = this.plugin.app.vault + .getAbstractFileByPath(newPath); + if (!(newFolder instanceof TFolder)) return; + await createFolderNote( + this.plugin, + newFolder.path, + true, + '.' + file.extension, + false, + file, + ); + this.plugin.settings.autoCreate = automaticallyCreateFolderNote; + this.plugin.saveSettings(); + }); + }); + if (getFolderPathFromString(file.path) === '') return; + if (!(folder instanceof TFolder)) return; + if (folder.path === '' || folder.path === '/') return; + subMenu.addItem((item) => { + item.setTitle(`Turn into folder note for ${folder?.name}`) + .setIcon('edit') + .onClick(() => { + if (!folder || !(folder instanceof TFolder)) return; + const folderNote = getFolderNote(this.plugin, folder.path); + turnIntoFolderNote(this.plugin, file, folder, folderNote); + }); + }); + } + if (!(file instanceof TFolder)) return; + const excludedFolder = getExcludedFolder(this.plugin, file.path, false); + const detachedExcludedFolder = getDetachedFolder(this.plugin, file.path); + if (excludedFolder && !excludedFolder.hideInSettings) { + // I'm not sure if I'm ever going to add this because of the possibility that a folder got more than one excluded + // subMenu.addItem((item) => { + // item.setTitle('Manage excluded folder') + // .setIcon('settings-2') + // .onClick(() => { + // if (excludedFolder instanceof ExcludedFolder) { + // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open(); + // } else if (excludedFolder instanceof ExcludePattern) { + // new PatternSettings(this.plugin.app, this.plugin, excludedFolder).open(); + // } + // }) + // }) + subMenu.addItem((item) => { + item.setTitle('Remove folder from excluded folders') + .setIcon('trash') + .onClick(() => { + this.plugin.settings.excludeFolders = + this.plugin.settings.excludeFolders.filter( + (excluded) => + (excluded.path !== file.path) || excluded.detached, + ); + this.plugin.saveSettings(true); + new Notice('Successfully removed folder from excluded folders'); + }); + }); + return; + } + if (detachedExcludedFolder) { + subMenu.addItem((item) => { + item.setTitle('Remove folder from detached folders') + .setIcon('trash') + .onClick(() => { + deleteExcludedFolder(this.plugin, detachedExcludedFolder); + }); + }); + } + if (detachedExcludedFolder) { return; } subMenu.addItem((item) => { - item.setTitle('Remove folder from excluded folders') - .setIcon('trash') + item.setTitle('Exclude folder from folder notes') + .setIcon('x-circle') .onClick(() => { - this.plugin.settings.excludeFolders = this.plugin.settings.excludeFolders.filter( - (folder) => (folder.path !== file.path) || folder.detached); + const newExcludedFolder = new ExcludedFolder( + file.path, + this.plugin.settings.excludeFolders.length, + undefined, + this.plugin, + ); + this.plugin.settings.excludeFolders.push(newExcludedFolder); this.plugin.saveSettings(true); - new Notice('Successfully removed folder from excluded folders'); + new Notice('Successfully excluded folder from folder notes'); }); }); - return; - } - if (detachedExcludedFolder) { - subMenu.addItem((item) => { - item.setTitle('Remove folder from detached folders') - .setIcon('trash') - .onClick(() => { - deleteExcludedFolder(this.plugin, detachedExcludedFolder); - }); - }); - } - if (detachedExcludedFolder) { return; } - subMenu.addItem((item) => { - item.setTitle('Exclude folder from folder notes') - .setIcon('x-circle') - .onClick(() => { - const excludedFolder = new ExcludedFolder(file.path, this.plugin.settings.excludeFolders.length, undefined, this.plugin); - this.plugin.settings.excludeFolders.push(excludedFolder); - this.plugin.saveSettings(true); - new Notice('Successfully excluded folder from folder notes'); + if (!(file instanceof TFolder)) return; + const folderNote = getFolderNote(this.plugin, file.path); + if (folderNote instanceof TFile && !detachedExcludedFolder) { + subMenu.addItem((item) => { + item.setTitle('Delete folder note') + .setIcon('trash') + .onClick(() => { + deleteFolderNote(this.plugin, folderNote, true); + }); }); - }); - if (!(file instanceof TFolder)) return; - const folderNote = getFolderNote(this.plugin, file.path); - if (folderNote instanceof TFile && !detachedExcludedFolder) { - subMenu.addItem((item) => { - item.setTitle('Delete folder note') - .setIcon('trash') - .onClick(() => { - deleteFolderNote(this.plugin, folderNote, true); - }); - }); - subMenu.addItem((item) => { - item.setTitle('Open folder note') - .setIcon('chevron-right-square') - .onClick(() => { - openFolderNote(this.plugin, folderNote); - }); - }); + subMenu.addItem((item) => { + item.setTitle('Open folder note') + .setIcon('chevron-right-square') + .onClick(() => { + openFolderNote(this.plugin, folderNote); + }); + }); - subMenu.addItem((item) => { - item.setTitle('Detach folder note') - .setIcon('unlink') - .onClick(() => { - detachFolderNote(this.plugin, folderNote); - }); - }); + subMenu.addItem((item) => { + item.setTitle('Detach folder note') + .setIcon('unlink') + .onClick(() => { + detachFolderNote(this.plugin, folderNote); + }); + }); - subMenu.addItem((item) => { - item.setTitle('Copy Obsidian URL') - .setIcon('link') - .onClick(() => { - // @ts-ignore - this.app.copyObsidianUrl(folderNote); - }); - }); + subMenu.addItem((item) => { + item.setTitle('Copy Obsidian URL') + .setIcon('link') + .onClick(() => { + this.app.copyObsidianUrl(folderNote); + }); + }); - if (this.plugin.settings.hideFolderNote) { - if (excludedFolder?.showFolderNote) { - subMenu.addItem((item) => { - item.setTitle('Hide folder note in explorer') - .setIcon('eye-off') - .onClick(() => { - hideFolderNoteInFileExplorer(file.path, this.plugin); - }); - }); - } else { - subMenu.addItem((item) => { - item.setTitle('Show folder note in explorer') - .setIcon('eye') - .onClick(() => { - showFolderNoteInFileExplorer(file.path, this.plugin); - }); - }); + if (this.plugin.settings.hideFolderNote) { + if (excludedFolder?.showFolderNote) { + subMenu.addItem((item) => { + item.setTitle('Hide folder note in explorer') + .setIcon('eye-off') + .onClick(() => { + hideFolderNoteInFileExplorer(file.path, this.plugin); + }); + }); + } else { + subMenu.addItem((item) => { + item.setTitle('Show folder note in explorer') + .setIcon('eye') + .onClick(() => { + showFolderNoteInFileExplorer(file.path, this.plugin); + }); + }); + } } - } - - } else { - subMenu.addItem((item) => { - item.setTitle('Create markdown folder note') - .setIcon('edit') - .onClick(() => { - createFolderNote(this.plugin, file.path, true, '.md'); - }); - }); - this.plugin.settings.supportedFileTypes.forEach((fileType) => { - if (fileType === 'md') return; + } else { subMenu.addItem((item) => { - item.setTitle(`Create ${fileType} folder note`) + item.setTitle('Create markdown folder note') .setIcon('edit') .onClick(() => { - createFolderNote(this.plugin, file.path, true, '.' + fileType); + createFolderNote(this.plugin, file.path, true, '.md'); }); }); - }); - } - }); - })); + + this.plugin.settings.supportedFileTypes.forEach((fileType) => { + if (fileType === 'md') return; + subMenu.addItem((item) => { + item.setTitle(`Create ${fileType} folder note`) + .setIcon('edit') + .onClick(() => { + // eslint-disable-next-line max-len + createFolderNote(this.plugin, file.path, true, '.' + fileType); + }); + }); + }); + } + }); + })); } - editorCommands() { + editorCommands(): void { + // eslint-disable-next-line max-len this.plugin.registerEvent(this.plugin.app.workspace.on('editor-menu', (menu: Menu, editor: Editor, view: MarkdownView) => { const text = editor.getSelection().trim(); if (!text || text.trim() === '') return; @@ -453,6 +517,7 @@ export class Commands { const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':']; for (const char of blacklist) { if (text.includes(char)) { + // eslint-disable-next-line max-len new Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?'); return; } @@ -461,9 +526,11 @@ export class Commands { new Notice('File name cannot end with a dot'); return; } + let folder: TAbstractFile | null; const folderPath = getFolderPathFromString(file.path); - const fileName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', text); + const { folderNoteName } = this.plugin.settings; + const fileName = folderNoteName.replace('{{folder_name}}', text); if (folderPath === '') { folder = this.plugin.app.vault.getAbstractFileByPath(text); if (folder instanceof TFolder) { @@ -473,12 +540,21 @@ export class Commands { createFolderNote(this.plugin, text, false); } else { - folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text); + folder = this.plugin.app.vault.getAbstractFileByPath( + folderPath + '/' + text, + ); if (folder instanceof TFolder) { return new Notice('Folder note already exists'); } if (this.plugin.settings.storageLocation === 'parentFolder') { - if (this.app.vault.getAbstractFileByPath(folderPath + '/' + fileName + this.plugin.settings.folderNoteType)) { + if ( + this.app.vault.getAbstractFileByPath( + folderPath + + '/' + + fileName + + this.plugin.settings.folderNoteType, + ) + ) { return new Notice('File already exists'); } } diff --git a/src/functions/ListComponent.ts b/src/functions/ListComponent.ts index 0c8e21e..f6486a8 100644 --- a/src/functions/ListComponent.ts +++ b/src/functions/ListComponent.ts @@ -18,19 +18,19 @@ export class ListComponent { this.defaultValues = defaultValues; } - on(event: string, listener: (data?: any) => void) { + on(event: string, listener: (data?: unknown) => void): void { this.emitter.on(event, listener); } - off(event: string, listener: (data?: any) => void) { + off(event: string, listener: (data?: unknown) => void): void { this.emitter.off(event, listener); } - private emit(event: string, data?: any) { + private emit(event: string, data?: unknown): void { this.emitter.emit(event, data); } - setValues(values: string[]) { + setValues(values: string[]): void { this.removeElements(); this.values = values; if (values.length !== 0) { @@ -41,11 +41,11 @@ export class ListComponent { this.emit('update', this.values); } - removeElements() { + removeElements(): void { this.listEl.empty(); } - addElement(value: string) { + addElement(value: string): void { this.listEl.createSpan('setting-hotkey', (span) => { if (value.toLocaleLowerCase() === 'md') { span.innerText = 'markdown'; @@ -53,35 +53,39 @@ export class ListComponent { span.innerText = value; } span.setAttribute('extension', value); + // eslint-disable-next-line max-len const removeSpan = span.createEl('span', { cls: 'ofn-list-item-remove setting-hotkey-icon' }); + // eslint-disable-next-line max-len const svg = ''; const svgElement = removeSpan.createEl('span', { cls: 'ofn-list-item-remove-icon' }); svgElement.innerHTML = svg; - removeSpan.onClickEvent((e) => { + removeSpan.onClickEvent(() => { this.removeValue(value); span.remove(); }); }); } - async addValue(value: string) { + async addValue(value: string): Promise { this.values.push(value); this.addElement(value); this.emit('add', value); this.emit('update', this.values); } - addResetButton() { + addResetButton(): this { + // eslint-disable-next-line max-len const resetButton = this.controlEl.createEl('span', { cls: 'clickable-icon setting-restore-hotkey-button' }); + // eslint-disable-next-line max-len const svg = ''; resetButton.innerHTML = svg; - resetButton.onClickEvent((e) => { + resetButton.onClickEvent(() => { this.setValues(this.defaultValues); }); return this; } - removeValue(value: string) { + removeValue(value: string): void { this.values = this.values.filter((v) => v !== value); this.listEl.find(`[extension='${value}']`).remove(); this.emit('remove', value); diff --git a/src/functions/excalidraw.ts b/src/functions/excalidraw.ts index 81eb8c8..8b0286e 100644 --- a/src/functions/excalidraw.ts +++ b/src/functions/excalidraw.ts @@ -1,19 +1,36 @@ import type { WorkspaceLeaf, App } from 'obsidian'; -export async function openExcalidrawView(leaf: WorkspaceLeaf) { - const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(this.app); - if (excalidrawEnabled) { +interface ExcalidrawPlugin { + setExcalidrawView(leaf: WorkspaceLeaf): void; +} + +export async function openExcalidrawView( + app: App, + leaf: WorkspaceLeaf, +): Promise { + const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(app); + if (excalidrawEnabled && excalidraw) { excalidraw.setExcalidrawView(leaf); } } -export async function getExcalidrawPlugin(app: App) { - const excalidraw = (app as any).plugins.plugins['obsidian-excalidraw-plugin']; - const excalidrawEnabled = (app as any).plugins.enabledPlugins.has( - 'obsidian-excalidraw-plugin', +export async function getExcalidrawPlugin( + app: App, +): Promise<{ excalidraw: ExcalidrawPlugin | null; excalidrawEnabled: boolean }> { + const { plugins: pluginManager } = app as App & { + plugins: { + plugins: Record; + enabledPlugins: Set; + }; + }; + const excalidraw = ( + pluginManager.plugins[ + 'obsidian-excalidraw-plugin' + ] as unknown as ExcalidrawPlugin | undefined ); + const excalidrawEnabled = pluginManager.enabledPlugins.has('obsidian-excalidraw-plugin'); return { - excalidraw, + excalidraw: excalidraw ?? null, excalidrawEnabled, }; } diff --git a/src/functions/folderNoteFunctions.ts b/src/functions/folderNoteFunctions.ts index 17f6944..69e1830 100644 --- a/src/functions/folderNoteFunctions.ts +++ b/src/functions/folderNoteFunctions.ts @@ -1,15 +1,39 @@ +/* eslint-disable complexity */ +/* eslint-disable max-len */ import type FolderNotesPlugin from '../main'; import ExistingFolderNoteModal from '../modals/ExistingNote'; import { applyTemplate } from '../template'; -import type { TAbstractFile } from 'obsidian'; -import { TFolder, TFile, Keymap } from 'obsidian'; +import { + TFolder, + TFile, + Keymap, + type TAbstractFile, + type MarkdownView, + type WorkspaceLeaf, +} from 'obsidian'; import DeleteConfirmationModal from '../modals/DeleteConfirmation'; -import { addExcludedFolder, deleteExcludedFolder, getDetachedFolder, getExcludedFolder, updateExcludedFolder } from '../ExcludeFolders/functions/folderFunctions'; +import { + addExcludedFolder, + deleteExcludedFolder, + getDetachedFolder, + getExcludedFolder, + updateExcludedFolder, +} from '../ExcludeFolders/functions/folderFunctions'; import { ExcludedFolder } from '../ExcludeFolders/ExcludeFolder'; import { openExcalidrawView } from './excalidraw'; import { AskForExtensionModal } from 'src/modals/AskForExtension'; -import { addCSSClassToFileExplorerEl, removeCSSClassFromFileExplorerEL, removeActiveFolder, setActiveFolder } from 'src/functions/styleFunctions'; -import { getFolderNameFromPathString, getFolderPathFromString, removeExtension } from 'src/functions/utils'; +import { + addCSSClassToFileExplorerEl, + removeCSSClassFromFileExplorerEL, + removeActiveFolder, + setActiveFolder, +} from 'src/functions/styleFunctions'; +import { + getFolderNameFromPathString, + getFolderPathFromString, + removeExtension, +} from 'src/functions/utils'; + const defaultExcalidrawTemplate = `--- @@ -27,24 +51,36 @@ tags: [excalidraw] \`\`\` %%`; -export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: string, openFile: boolean, extension?: string, displayModal?: boolean, preexistingNote?: TFile) { - const leaf = plugin.app.workspace.getLeaf(false); - const folderName = getFolderNameFromPathString(folderPath); - const fileName = plugin.settings.folderNoteName.replace('{{folder_name}}', folderName); - let folderNote = getFolderNote(plugin, folderPath); - if (preexistingNote) { - folderNote = preexistingNote; - } - let folderNoteType = extension ?? plugin.settings.folderNoteType; - const detachedFolder = getDetachedFolder(plugin, folderPath); - let path = ''; +export async function createFolderNote( + plugin: FolderNotesPlugin, + folderPath: string, + openFile: boolean, + extension?: string, + displayModal?: boolean, + preexistingNote?: TFile, +): Promise { + let { + leaf, + fileName, + folderNote, + folderNoteType, + detachedFolder, + path, + } = getArgs(plugin, folderPath, extension, preexistingNote); if (folderNoteType === '.excalidraw') { folderNoteType = '.md'; extension = '.excalidraw'; } else if (folderNoteType === '.ask') { if (plugin.askModalCurrentlyOpen) return; - return new AskForExtensionModal(plugin, folderPath, openFile, folderNoteType, displayModal, preexistingNote).open(); + return new AskForExtensionModal( + plugin, + folderPath, + openFile, + folderNoteType, + displayModal, + preexistingNote, + ).open(); } if (plugin.settings.storageLocation === 'parentFolder') { @@ -61,27 +97,7 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st } if (detachedFolder && folderNote?.extension !== extension && folderNote) { - deleteExcludedFolder(plugin, detachedFolder); - removeCSSClassFromFileExplorerEL(folderNote?.path, 'is-folder-note', false, plugin); - const folder = plugin.app.vault.getAbstractFileByPath(folderPath) as TFolder; - if (!folderNote || folderNote.basename !== fileName) return; - let count = 1; - let newName = removeExtension(folderNote.path) + ` (${count}).${folderNote.path.split('.').pop()}`; - while (count < 100 && plugin.app.vault.getAbstractFileByPath(newName)) { - count++; - newName = removeExtension(folderNote.path) + ` (${count}).${folderNote.path.split('.').pop()}`; - } - const [excludedFolder, excludedFolderExisted, disabledSync] = await tempDisableSync(plugin, folder); - - await plugin.app.fileManager.renameFile(folderNote, newName).then(() => { - if (!excludedFolder) return; - if (!excludedFolderExisted) { - deleteExcludedFolder(plugin, excludedFolder); - } else if (!disabledSync) { - excludedFolder.disableSync = false; - updateExcludedFolder(plugin, excludedFolder, excludedFolder); - } - }); + await handleTurnNoteIntoFolderNote(plugin, folderNote, detachedFolder, folderPath, fileName); } if (!extension) { @@ -89,33 +105,7 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st } if (!folderNote) { - let content = ''; - if (extension !== '.md' && extension) { - if (plugin.settings.templatePath && folderNoteType.split('.').pop() === plugin.settings.templatePath.split('.').pop()) { - const templateFile = plugin.app.vault.getAbstractFileByPath(plugin.settings.templatePath); - if (templateFile instanceof TFile) { - if (['md', 'canvas', 'txt'].includes(templateFile.extension)) { - content = await plugin.app.vault.read(templateFile); - if (extension === '.excalidraw' && !content.includes('==âš  Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. âš ==')) { - content = defaultExcalidrawTemplate; - } - } else { - return plugin.app.vault.readBinary(templateFile).then(async (data) => { - folderNote = await plugin.app.vault.createBinary(path, data); - if (openFile) { - await leaf.openFile(folderNote); - } - }); - } - } - } else if (plugin.settings.folderNoteType === '.excalidraw' || extension === '.excalidraw') { - content = defaultExcalidrawTemplate; - } else if (plugin.settings.folderNoteType === '.canvas') { - content = '{}'; - } - } - - folderNote = await plugin.app.vault.create(path, content); + folderNote = await handleCreateFolderNote(plugin, folderNoteType, openFile, leaf, folderNote, path, extension); } else { await plugin.app.fileManager.renameFile(folderNote, path); } @@ -131,11 +121,13 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st } await leaf.openFile(folderNote); if (plugin.settings.folderNoteType === '.excalidraw' || extension === '.excalidraw') { - openExcalidrawView(leaf); + openExcalidrawView(plugin.app, leaf); } } - const matchingExtension = extension?.split('.').pop() === plugin.settings.templatePath.split('.').pop(); + const matchingExtension = + extension?.split('.').pop() === + plugin.settings.templatePath.split('.').pop(); if (folderNote && matchingExtension && plugin.settings.folderNoteType !== '.excalidraw') { applyTemplate(plugin, folderNote, leaf, plugin.settings.templatePath); } @@ -146,19 +138,159 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin); } -export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile, folder: TFolder, folderNote?: TFile | null | TAbstractFile, skipConfirmation?: boolean) { +function getArgs( + plugin: FolderNotesPlugin, + folderPath: string, + extension?: string, + preexistingNote?: TFile, +): { + leaf: WorkspaceLeaf; + fileName: string; + folderNote: TFile | null | undefined; + folderNoteType: string; + detachedFolder: ExcludedFolder | undefined; + path: string | ''; +} { + const leaf = plugin.app.workspace.getLeaf(false); + const folderName = getFolderNameFromPathString(folderPath); + const fileName = plugin.settings.folderNoteName.replace('{{folder_name}}', folderName); + let folderNote = getFolderNote(plugin, folderPath); + if (preexistingNote) { + folderNote = preexistingNote; + } + let folderNoteType = extension ?? plugin.settings.folderNoteType; + const detachedFolder = getDetachedFolder(plugin, folderPath); + let path = ''; + + return { + leaf, + fileName, + folderNote, + folderNoteType, + detachedFolder, + path, + }; +} + +async function handleCreateFolderNote(plugin: FolderNotesPlugin, folderNoteType: string, openFile: boolean, leaf: WorkspaceLeaf, folderNote: TFile | null | undefined, path: string, extension?: string): Promise { + let content = ''; + if (extension !== '.md' && extension) { + if ( + plugin.settings.templatePath && + folderNoteType.split('.').pop() === + plugin.settings.templatePath.split('.').pop() + ) { + const templateFile = plugin.app.vault.getAbstractFileByPath( + plugin.settings.templatePath, + ); + if (templateFile instanceof TFile) { + if (['md', 'canvas', 'txt'].includes(templateFile.extension)) { + content = await plugin.app.vault.read(templateFile); + if ( + extension === '.excalidraw' && + !content.includes( + '==âš  Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. âš ==', + ) + ) { + content = defaultExcalidrawTemplate; + } + } else { + plugin.app.vault.readBinary(templateFile).then(async (data) => { + folderNote = await plugin.app.vault.createBinary(path, data); + if (openFile) { + await leaf.openFile(folderNote); + } + return folderNote; + }); + } + } + } else if ( + plugin.settings.folderNoteType === '.excalidraw' || + extension === '.excalidraw' + ) { + content = defaultExcalidrawTemplate; + } else if (plugin.settings.folderNoteType === '.canvas') { + content = '{}'; + } + } + + folderNote = await plugin.app.vault.create(path, content); + return folderNote; +} + +async function handleTurnNoteIntoFolderNote( + plugin: FolderNotesPlugin, + folderNote: TFile, + detachedFolder: ExcludedFolder, + folderPath: string, + fileName: string, +): Promise { + deleteExcludedFolder(plugin, detachedFolder); + removeCSSClassFromFileExplorerEL(folderNote?.path, 'is-folder-note', false, plugin); + const folder = plugin.app.vault.getAbstractFileByPath(folderPath) as TFolder; + if (!folderNote || folderNote.basename !== fileName) return; + let count = 1; + const baseName = removeExtension(folderNote.path); + const ext = folderNote.path.split('.').pop(); + let newName = `${baseName} (${count}).${ext}`; + const MAX_FOLDER_NOTE_RENAME_ATTEMPTS = 100; + + while ( + count < MAX_FOLDER_NOTE_RENAME_ATTEMPTS && + plugin.app.vault.getAbstractFileByPath(newName) + ) { + count++; + newName = `${baseName} (${count}).${ext}`; + } + const [ + excludedFolder, + excludedFolderExisted, + disabledSync, + ] = await tempDisableSync(plugin, folder); + + await plugin.app.fileManager.renameFile(folderNote, newName).then(() => { + if (!excludedFolder) return; + if (!excludedFolderExisted) { + deleteExcludedFolder(plugin, excludedFolder); + } else if (!disabledSync) { + excludedFolder.disableSync = false; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + } + }); +} + +export async function turnIntoFolderNote( + plugin: FolderNotesPlugin, + file: TFile, + folder: TFolder, + folderNote?: TFile | null | TAbstractFile, + skipConfirmation?: boolean, +): Promise { const { extension } = file; const detachedExcludedFolder = getDetachedFolder(plugin, folder.path); if (folderNote) { - if (plugin.settings.showRenameConfirmation && !skipConfirmation && !detachedExcludedFolder) { + if ( + plugin.settings.showRenameConfirmation && + !skipConfirmation && + !detachedExcludedFolder + ) { return new ExistingFolderNoteModal(plugin.app, plugin, file, folder, folderNote).open(); } removeCSSClassFromFileExplorerEL(folderNote.path, 'is-folder-note', false, plugin); - const [excludedFolder, excludedFolderExisted, disabledSync] = await tempDisableSync(plugin, folder); + const [ + excludedFolder, + excludedFolderExisted, + disabledSync, + ] = await tempDisableSync(plugin, folder); + + const CTIME_SLICE_START = 10; + const RANDOM_SUFFIX_MAX = 1000; + const randomSuffix = Math.floor(Math.random() * RANDOM_SUFFIX_MAX); + const ctimeSuffix = file.stat.ctime.toString().slice(CTIME_SLICE_START); + const newPath = `${folder.path}/${folder.name} (${ctimeSuffix}${randomSuffix}).${extension}`; - const newPath = `${folder.path}/${folder.name} (${file.stat.ctime.toString().slice(10) + Math.floor(Math.random() * 1000)}).${extension}`; plugin.app.fileManager.renameFile(folderNote, newPath).then(() => { if (!excludedFolder) return; if (!excludedFolderExisted) { @@ -195,14 +327,28 @@ export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile, setActiveFolder(folder.path, plugin); } -export async function tempDisableSync(plugin: FolderNotesPlugin, folder: TFolder): Promise<[excludedFolder: ExcludedFolder | undefined, excludedFolderExisted: boolean, disabledSync: boolean]> { +export async function tempDisableSync( + plugin: FolderNotesPlugin, + folder: TFolder, +): Promise< + [ + excludedFolder: ExcludedFolder | undefined, + excludedFolderExisted: boolean, + disabledSync: boolean + ] +> { let excludedFolder = getExcludedFolder(plugin, folder.path, false); let excludedFolderExisted = true; let disabledSync = false; if (!excludedFolder) { excludedFolderExisted = false; - excludedFolder = new ExcludedFolder(folder.path, plugin.settings.excludeFolders.length, undefined, plugin); + excludedFolder = new ExcludedFolder( + folder.path, + plugin.settings.excludeFolders.length, + undefined, + plugin, + ); excludedFolder.disableSync = true; addExcludedFolder(plugin, excludedFolder); } else if (!excludedFolder.disableSync) { @@ -214,7 +360,11 @@ export async function tempDisableSync(plugin: FolderNotesPlugin, folder: TFolder return [excludedFolder, excludedFolderExisted, disabledSync]; } -export async function openFolderNote(plugin: FolderNotesPlugin, file: TAbstractFile, evt?: MouseEvent) { +export async function openFolderNote( + plugin: FolderNotesPlugin, + file: TAbstractFile, + evt?: MouseEvent, +): Promise { const { path } = file; const focusExistingTab = plugin.settings.focusExistingTab && plugin.settings.openInNewTab; const activeFilePath = plugin.app.workspace.getActiveFile()?.path; @@ -230,7 +380,7 @@ export async function openFolderNote(plugin: FolderNotesPlugin, file: TAbstractF plugin.app.workspace.iterateAllLeaves((leaf) => { if ( leaf.getViewState().type === 'markdown' && - (leaf.view as import('obsidian').MarkdownView).file?.path === path + (leaf.view as MarkdownView).file?.path === path ) { foundLeaf = leaf; } @@ -240,14 +390,19 @@ export async function openFolderNote(plugin: FolderNotesPlugin, file: TAbstractF if (foundLeaf) { plugin.app.workspace.setActiveLeaf(foundLeaf, { focus: true }); } else { - const leaf = plugin.app.workspace.getLeaf(Keymap.isModEvent(evt) || plugin.settings.openInNewTab); + const shouldOpenInNewTab = Keymap.isModEvent(evt) || plugin.settings.openInNewTab; + const leaf = plugin.app.workspace.getLeaf(shouldOpenInNewTab); if (file instanceof TFile) { await leaf.openFile(file); } } } -export async function deleteFolderNote(plugin: FolderNotesPlugin, file: TFile, displayModal: boolean) { +export async function deleteFolderNote( + plugin: FolderNotesPlugin, + file: TFile, + displayModal: boolean, +): Promise { if (plugin.settings.showDeleteConfirmation && displayModal) { return new DeleteConfirmationModal(plugin.app, plugin, file).open(); } @@ -272,7 +427,7 @@ export async function deleteFolderNote(plugin: FolderNotesPlugin, file: TFile, d } } -export function extractFolderName(template: string, changedFileName: string) { +export function extractFolderName(template: string, changedFileName: string): string | null { const [prefix, suffix] = template.split('{{folder_name}}'); if (prefix.trim() === '' && suffix.trim() === '') { return changedFileName; @@ -288,39 +443,23 @@ export function extractFolderName(template: string, changedFileName: string) { return null; } -export function getFolderNote(plugin: FolderNotesPlugin, folderPath: string, storageLocation?: string, file?: TFile, oldFolderNoteName?: string) { - if (!folderPath) return null; - const folder = { - path: folderPath, - name: getFolderNameFromPathString(folderPath), - }; - const folderNoteName = oldFolderNoteName ?? plugin.settings.folderNoteName; - - let fileName = folderNoteName.replace('{{folder_name}}', folder.name); - if (file) { - fileName = folderNoteName.replace('{{folder_name}}', file.basename); - } - if (!fileName) return null; - - if ((plugin.settings.storageLocation === 'parentFolder' || storageLocation === 'parentFolder') && storageLocation !== 'insideFolder') { - folder.path = getFolderPathFromString(folderPath); +function findFolderNoteFile( + plugin: FolderNotesPlugin, + path: string, + primaryType: string, +): TFile | null { + let folderNote = plugin.app.vault.getAbstractFileByPath(path + primaryType); + if ( + folderNote instanceof TFile && + plugin.settings.supportedFileTypes.includes(primaryType.replace('.', '')) + ) { + return folderNote; } + const supportedFileTypes = plugin.settings.supportedFileTypes.filter( + (type) => type !== primaryType.replace('.', ''), + ); - let path = `${folder.path}/${fileName}`; - folder.path === '/' ? path = fileName : path = `${folder.path}/${fileName}`; - - - let { folderNoteType } = plugin.settings; - if (folderNoteType === '.excalidraw') { - folderNoteType = '.md'; - } - - let folderNote = plugin.app.vault.getAbstractFileByPath(path + folderNoteType); - if (folderNote instanceof TFile && plugin.settings.supportedFileTypes.includes(plugin.settings.folderNoteType.replace('.', ''))) { - return folderNote; - } - const supportedFileTypes = plugin.settings.supportedFileTypes.filter((type) => type !== plugin.settings.folderNoteType.replace('.', '')); for (let type of supportedFileTypes) { if (type === 'excalidraw' || type === '.excalidraw') { type = '.md'; @@ -333,13 +472,40 @@ export function getFolderNote(plugin: FolderNotesPlugin, folderPath: string, sto return folderNote; } } + return null; +} + +export function getFolderNote( + plugin: FolderNotesPlugin, + folderPath: string, + storageLocation?: string, + file?: TFile, + oldFolderNoteName?: string, +): TFile | null | undefined { + const folder = getFolderInfo(folderPath); + if (!folder) return null; + + let fileName = resolveFileName(plugin, folder, file, oldFolderNoteName); + if (!fileName) return null; + adjustFolderPathForStorage(folder, folderPath, plugin, storageLocation); + + const path = buildFullPath(folder, fileName); + const primaryType = normalizeFolderNoteType(plugin.settings.folderNoteType); + + return findFolderNoteFile(plugin, path, primaryType); } -export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile) { + +export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile): void { const folder = getFolder(plugin, file); if (!folder) return; - const excludedFolder = new ExcludedFolder(folder.path, plugin.settings.excludeFolders.length, undefined, plugin); + const excludedFolder = new ExcludedFolder( + folder.path, + plugin.settings.excludeFolders.length, + undefined, + plugin, + ); excludedFolder.hideInSettings = true; excludedFolder.disableFolderNote = true; excludedFolder.disableSync = true; @@ -351,16 +517,28 @@ export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile) { } -export function getFolder(plugin: FolderNotesPlugin, file: TFile, storageLocation?: string) { +export function getFolder( + plugin: FolderNotesPlugin, + file: TFile, + storageLocation?: string, +): TFolder | TAbstractFile | null { if (!file) return null; let folderName = extractFolderName(plugin.settings.folderNoteName, file.basename); - if (plugin.settings.folderNoteName === file.basename && plugin.settings.storageLocation === 'insideFolder') { + if ( + plugin.settings.folderNoteName === file.basename && + plugin.settings.storageLocation === 'insideFolder' + ) { folderName = file.parent?.name ?? ''; } if (!folderName) return null; let folderPath = getFolderPathFromString(file.path); let folder: TFolder | TAbstractFile | null = null; - if ((plugin.settings.storageLocation === 'parentFolder' || storageLocation === 'parentFolder') && storageLocation !== 'insideFolder') { + + if ( + (plugin.settings.storageLocation === 'parentFolder' || + storageLocation === 'parentFolder') && + storageLocation !== 'insideFolder' + ) { if (folderPath.trim() === '' || folderPath === '/') { folderPath = folderName; } else { @@ -370,11 +548,16 @@ export function getFolder(plugin: FolderNotesPlugin, file: TFile, storageLocatio } else { folder = plugin.app.vault.getAbstractFileByPath(folderPath); } + if (!folder) { return null; } return folder; } -export function getFolderNoteFolder(plugin: FolderNotesPlugin, folderNote: TFile | string, fileName: string) { +export function getFolderNoteFolder( + plugin: FolderNotesPlugin, + folderNote: TFile | string, + fileName: string, +): TFolder | TAbstractFile | null { if (!folderNote) return null; let filePath = ''; if (typeof folderNote === 'string') { @@ -399,3 +582,46 @@ export function getFolderNoteFolder(plugin: FolderNotesPlugin, folderNote: TFile if (!folder) { return null; } return folder; } + +function getFolderInfo(folderPath: string): { path: string; name: string } | null { + if (!folderPath) return null; + return { + path: folderPath, + name: getFolderNameFromPathString(folderPath), + }; +} + +function resolveFileName( + plugin: FolderNotesPlugin, + folder: { path: string; name: string }, + file?: TFile, + oldFolderNoteName?: string, +): string | null { + const templateName = oldFolderNoteName ?? plugin.settings.folderNoteName; + if (!templateName) return null; + const nameSource = file ? file.basename : folder.name; + return templateName.replace('{{folder_name}}', nameSource); +} + +function adjustFolderPathForStorage( + folder: { path: string; name: string }, + folderPath: string, + plugin: FolderNotesPlugin, + storageLocation?: string, +): void { + if ( + (plugin.settings.storageLocation === 'parentFolder' || + storageLocation === 'parentFolder') && + storageLocation !== 'insideFolder' + ) { + folder.path = getFolderPathFromString(folderPath); + } +} + +function buildFullPath(folder: { path: string }, fileName: string): string { + return folder.path === '/' ? fileName : `${folder.path}/${fileName}`; +} + +function normalizeFolderNoteType(type: string): string { + return type === '.excalidraw' ? '.md' : type; +} diff --git a/src/functions/styleFunctions.ts b/src/functions/styleFunctions.ts index d65d886..a99c6b8 100644 --- a/src/functions/styleFunctions.ts +++ b/src/functions/styleFunctions.ts @@ -1,6 +1,10 @@ import { TFile, TFolder } from 'obsidian'; import type FolderNotesPlugin from '../main'; -import { getDetachedFolder, getExcludedFolder, addExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; +import { + getDetachedFolder, + getExcludedFolder, + addExcludedFolder, +} from 'src/ExcludeFolders/functions/folderFunctions'; import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions'; import { getFileExplorer } from './utils'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; @@ -9,7 +13,7 @@ import type FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main'; /** * @description Refreshes the CSS classes for all folder notes in the file explorer. */ -export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesPlugin) { +export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesPlugin): void { if (plugin.activeFileExplorer === getFileExplorer(plugin) && !forceReload) { return; } plugin.activeFileExplorer = getFileExplorer(plugin); plugin.app.vault.getAllLoadedFiles().forEach(async (file) => { @@ -22,7 +26,10 @@ export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesP /** * @description Updates the CSS classes for a specific folder in the file explorer. */ -export async function updateCSSClassesForFolder(folderPath: string, plugin: FolderNotesPlugin) { +export async function updateCSSClassesForFolder( + folderPath: string, + plugin: FolderNotesPlugin, +): Promise { const folder = plugin.app.vault.getAbstractFileByPath(folderPath); if (!folder || !(folder instanceof TFolder)) { return; } @@ -64,7 +71,10 @@ export async function updateCSSClassesForFolder(folderPath: string, plugin: Fold /** * @description Updates the CSS classes for a folder note file in the file explorer and then also updates the folder it belongs to. */ -export async function updateCSSClassesForFolderNote(filePath: string, plugin: FolderNotesPlugin) { +export async function updateCSSClassesForFolderNote( + filePath: string, + plugin: FolderNotesPlugin, +): Promise { const file = plugin.app.vault.getAbstractFileByPath(filePath); if (!file || !(file instanceof TFile)) { return; } @@ -74,17 +84,25 @@ export async function updateCSSClassesForFolderNote(filePath: string, plugin: Fo updateCSSClassesForFolder(folder.path, plugin); } -export function markFolderAndNoteWithClasses(file: TFile, folder: TFolder, plugin: FolderNotesPlugin) { +export function markFolderAndNoteWithClasses( + file: TFile, + folder: TFolder, + plugin: FolderNotesPlugin, +): void { markFileAsFolderNote(file, plugin); markFolderWithFolderNoteClasses(folder, plugin); } -export function clearFolderAndNoteClasses(folder: TFolder, file: TFile, plugin: FolderNotesPlugin) { +export function clearFolderAndNoteClasses( + folder: TFolder, + file: TFile, + plugin: FolderNotesPlugin, +): void { unmarkFileAsFolderNote(file, plugin); clearFolderNoteClassesFromFolder(folder, plugin); } -export function markFolderWithFolderNoteClasses(folder: TFolder, plugin: FolderNotesPlugin) { +export function markFolderWithFolderNoteClasses(folder: TFolder, plugin: FolderNotesPlugin): void { addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin); if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { addCSSClassToFileExplorerEl(folder.path, 'only-has-folder-note', true, plugin); @@ -93,20 +111,20 @@ export function markFolderWithFolderNoteClasses(folder: TFolder, plugin: FolderN } } -export function markFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin) { +export function markFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin): void { addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); } -export function unmarkFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin) { +export function unmarkFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin): void { removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin); } -export function unmarkFolderAsFolderNote(folder: TFolder, plugin: FolderNotesPlugin) { +export function unmarkFolderAsFolderNote(folder: TFolder, plugin: FolderNotesPlugin): void { removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); } -export function clearFolderNoteClassesFromFolder(folder: TFolder, plugin: FolderNotesPlugin) { +export function clearFolderNoteClassesFromFolder(folder: TFolder, plugin: FolderNotesPlugin): void { removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); } @@ -115,11 +133,21 @@ export function clearFolderNoteClassesFromFolder(folder: TFolder, plugin: Folder * @param path Can be a folder or file path * @returns nothing */ -export async function addCSSClassToFileExplorerEl(path: string, cssClass: string, parent = false, plugin: FolderNotesPlugin, waitForCreate = false, count = 0) { +export async function addCSSClassToFileExplorerEl( + path: string, + cssClass: string, + parent = false, + plugin: FolderNotesPlugin, + waitForCreate = false, + count = 0, +): Promise { const fileExplorerItem = getFileExplorerElement(path, plugin); + const MAX_RETRIES = 5; + const RETRY_DELAY = 500; + if (!fileExplorerItem) { - if (waitForCreate && count < 5) { - await new Promise((r) => setTimeout(r, 500)); + if (waitForCreate && count < MAX_RETRIES) { + await new Promise((r) => setTimeout(r, RETRY_DELAY)); addCSSClassToFileExplorerEl(path, cssClass, parent, plugin, waitForCreate, count + 1); return; } @@ -143,7 +171,12 @@ export async function addCSSClassToFileExplorerEl(path: string, cssClass: string * @param cssClass The CSS class to remove from the file explorer element * @returns nothing */ -export function removeCSSClassFromFileExplorerEL(path: string | undefined, cssClass: string, parent: boolean, plugin: FolderNotesPlugin) { +export function removeCSSClassFromFileExplorerEL( + path: string | undefined, + cssClass: string, + parent: boolean, + plugin: FolderNotesPlugin, +): void { if (!path) return; const fileExplorerItem = getFileExplorerElement(path, plugin); document.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => { @@ -161,15 +194,23 @@ export function removeCSSClassFromFileExplorerEL(path: string | undefined, cssCl } -export function getFileExplorerElement(path: string, plugin: FolderNotesPlugin | FolderOverviewPlugin): HTMLElement | null { +export function getFileExplorerElement( + path: string, + plugin: FolderNotesPlugin | FolderOverviewPlugin, +): HTMLElement | null { const fileExplorer = getFileExplorer(plugin); if (!fileExplorer?.view?.fileItems) { return null; } const fileExplorerItem = fileExplorer.view.fileItems?.[path]; return fileExplorerItem?.selfEl ?? fileExplorerItem?.titleEl ?? null; } -export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPlugin) { - const excludedFolder = new ExcludedFolder(path, plugin.settings.excludeFolders.length, undefined, plugin); +export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPlugin): void { + const excludedFolder = new ExcludedFolder( + path, + plugin.settings.excludeFolders.length, + undefined, + plugin, + ); excludedFolder.subFolders = false; excludedFolder.disableSync = false; excludedFolder.disableAutoCreate = false; @@ -183,7 +224,7 @@ export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPl updateCSSClassesForFolder(path, plugin); } -export function hideFolderNoteInFileExplorer(folderPath: string, plugin: FolderNotesPlugin) { +export function hideFolderNoteInFileExplorer(folderPath: string, plugin: FolderNotesPlugin): void { plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter( (folder) => (folder.path !== folderPath) && folder.showFolderNote); plugin.saveSettings(false); @@ -191,7 +232,7 @@ export function hideFolderNoteInFileExplorer(folderPath: string, plugin: FolderN updateCSSClassesForFolder(folderPath, plugin); } -export function setActiveFolder(folderPath: string, plugin: FolderNotesPlugin) { +export function setActiveFolder(folderPath: string, plugin: FolderNotesPlugin): void { const fileExplorerItem = getFileExplorerElement(folderPath, plugin); if (fileExplorerItem) { fileExplorerItem.addClass('fn-is-active'); @@ -199,7 +240,7 @@ export function setActiveFolder(folderPath: string, plugin: FolderNotesPlugin) { } } -export function removeActiveFolder(plugin: FolderNotesPlugin) { +export function removeActiveFolder(plugin: FolderNotesPlugin): void { if (plugin.activeFolderDom) { plugin.activeFolderDom.removeClass('fn-is-active'); plugin.activeFolderDom?.removeClass('has-focus'); diff --git a/src/functions/utils.ts b/src/functions/utils.ts index 468c5de..ec9f2ff 100644 --- a/src/functions/utils.ts +++ b/src/functions/utils.ts @@ -2,7 +2,7 @@ import { TFolder, TFile, View } from 'obsidian'; import type { FileExplorerWorkspaceLeaf, FileExplorerView } from 'src/globals'; import { getFolderNote } from './folderNoteFunctions'; import type FolderNotesPlugin from 'src/main'; -import type { FileExplorerLeaf } from 'obsidian-typings'; +import type { FileExplorerLeaf, FileTreeItem, TreeNode } from 'obsidian-typings'; import type FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main'; export function getFileNameFromPathString(path: string): string { @@ -10,11 +10,12 @@ export function getFileNameFromPathString(path: string): string { } export function getFolderNameFromPathString(path: string): string { + const PARENT_FOLDER_INDEX = -2; + const LAST_FOLDER_INDEX = -1; if (path.endsWith('.md') || path.endsWith('.canvas')) { - return path.split('/').slice(-2)[0]; + return path.split('/').slice(PARENT_FOLDER_INDEX)[0]; } - return path.split('/').slice(-1)[0]; - + return path.split('/').slice(LAST_FOLDER_INDEX)[0]; } export function removeExtension(name: string): string { @@ -39,16 +40,20 @@ export function getParentFolderPath(path: string): string { return this.getFolderPathFromString(this.getFolderPathFromString(path)); } -export function getFileExplorer(plugin: FolderNotesPlugin | FolderOverviewPlugin) { - return plugin.app.workspace.getLeavesOfType('file-explorer')[0] as any as FileExplorerWorkspaceLeaf; +export function getFileExplorer( + plugin: FolderNotesPlugin | FolderOverviewPlugin, +): FileExplorerWorkspaceLeaf { + // eslint-disable-next-line max-len + const leaf = plugin.app.workspace.getLeavesOfType('file-explorer')[0] as unknown as FileExplorerWorkspaceLeaf; + return leaf; } -export function getFileExplorerView(plugin: FolderNotesPlugin) { +export function getFileExplorerView(plugin: FolderNotesPlugin): FileExplorerView { return getFileExplorer(plugin).view; } -export function getFocusedItem(plugin: FolderNotesPlugin) { - const fileExplorer = getFileExplorer(plugin) as any as FileExplorerLeaf; +export function getFocusedItem(plugin: FolderNotesPlugin): TreeNode | null { + const fileExplorer = getFileExplorer(plugin) as unknown as FileExplorerLeaf; const { focusedItem } = fileExplorer.view.tree; return focusedItem; } diff --git a/src/globals.d.ts b/src/globals.d.ts index 1229822..53fff77 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -40,7 +40,6 @@ interface FileItem { el: HTMLDivElement; file: TFile; fileExplorer: FileExplorerView; - info: any; selfEl: HTMLDivElement; innerEl: HTMLDivElement; } @@ -48,7 +47,6 @@ interface FileItem { interface FolderItem { el: HTMLDivElement; fileExplorer: FileExplorerView; - info: any; selfEl: HTMLDivElement; innerEl: HTMLDivElement; file: TFolder; diff --git a/src/main.ts b/src/main.ts index 6f18134..ee94694 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,23 @@ - -import type { TAbstractFile, MarkdownPostProcessorContext, WorkspaceLeaf } from 'obsidian'; -import { Plugin, TFile, TFolder, parseYaml, Notice, Keymap, requireApiVersion, Platform, debounce } from 'obsidian'; -import type { FolderNotesSettings } from './settings/SettingsTab'; -import { DEFAULT_SETTINGS, SettingsTab } from './settings/SettingsTab'; +import { + type TAbstractFile, + type MarkdownPostProcessorContext, + type WorkspaceLeaf, + Plugin, TFile, TFolder, + parseYaml, Notice, Keymap, + requireApiVersion, Platform, debounce, +} from 'obsidian'; +import { + type FolderNotesSettings, DEFAULT_SETTINGS, SettingsTab, +} from './settings/SettingsTab'; import { Commands } from './Commands'; import type { FileExplorerWorkspaceLeaf } from './globals'; -import { registerFileExplorerObserver, unregisterFileExplorerObserver } from './events/MutationObserver'; +import { + registerFileExplorerObserver, unregisterFileExplorerObserver, +} from './events/MutationObserver'; import { handleRename } from './events/handleRename'; -import { getFolderNote, getFolder, openFolderNote, createFolderNote } from './functions/folderNoteFunctions'; +import { + getFolderNote, getFolder, openFolderNote, createFolderNote, +} from './functions/folderNoteFunctions'; import { handleCreate } from './events/handleCreate'; import { FrontMatterTitlePluginHandler } from './events/FrontMatterTitle'; import { FolderOverviewSettings } from './obsidian-folder-overview/src/modals/Settings'; @@ -15,10 +25,12 @@ import { FolderOverview } from './obsidian-folder-overview/src/FolderOverview'; import { TabManager } from './events/TabManager'; import './functions/ListComponent'; import { handleDelete } from './events/handleDelete'; -import { addCSSClassToFileExplorerEl, getFileExplorerElement, removeCSSClassFromFileExplorerEL, refreshAllFolderStyles, setActiveFolder, removeActiveFolder } from './functions/styleFunctions'; +import { + addCSSClassToFileExplorerEl, getFileExplorerElement, removeCSSClassFromFileExplorerEL, + refreshAllFolderStyles, setActiveFolder, removeActiveFolder, +} from './functions/styleFunctions'; import { getExcludedFolder } from './ExcludeFolders/functions/folderFunctions'; import type { FileExplorerView, InternalPlugin } from 'obsidian-typings'; -// import { getFocusedItem } from './functions/utils'; import { FOLDER_OVERVIEW_VIEW, FolderOverviewView } from './obsidian-folder-overview/src/view'; import { registerOverviewCommands } from './obsidian-folder-overview/src/Commands'; import { updateOverviewView, updateViewDropdown } from './obsidian-folder-overview/src/main'; @@ -26,7 +38,6 @@ import { FvIndexDB } from './obsidian-folder-overview/src/utils/IndexDB'; import { updateAllOverviews } from './obsidian-folder-overview/src/utils/functions'; export default class FolderNotesPlugin extends Plugin { - observer: MutationObserver; settings: FolderNotesSettings; settingsTab: SettingsTab; activeFolderDom: HTMLElement | null; @@ -43,7 +54,7 @@ export default class FolderNotesPlugin extends Plugin { private fileExplorerPlugin!: InternalPlugin; private fileExplorerView!: FileExplorerView; - async onload() { + async onload(): Promise { console.log('loading folder notes plugin'); await this.loadSettings(); this.settingsTab = new SettingsTab(this.app, this); @@ -54,17 +65,29 @@ export default class FolderNotesPlugin extends Plugin { // Add CSS Classes document.body.classList.add('folder-notes-plugin'); if (this.settings.hideFolderNote) { document.body.classList.add('hide-folder-note'); } - if (this.settings.hideCollapsingIconForEmptyFolders) { document.body.classList.add('fn-hide-empty-collapse-icon'); } + if (this.settings.hideCollapsingIconForEmptyFolders) { + document.body.classList.add('fn-hide-empty-collapse-icon'); + } if (this.settings.underlineFolder) { document.body.classList.add('folder-note-underline'); } if (this.settings.boldName) { document.body.classList.add('folder-note-bold'); } if (this.settings.cursiveName) { document.body.classList.add('folder-note-cursive'); } if (this.settings.boldNameInPath) { document.body.classList.add('folder-note-bold-path'); } - if (this.settings.cursiveNameInPath) { document.body.classList.add('folder-note-cursive-path'); } - if (this.settings.underlineFolderInPath) { document.body.classList.add('folder-note-underline-path'); } - if (this.settings.stopWhitespaceCollapsing) { document.body.classList.add('fn-whitespace-stop-collapsing'); } - if (this.settings.hideCollapsingIcon) { document.body.classList.add('fn-hide-collapse-icon'); } - if (!this.settings.highlightFolder) { document.body.classList.add('disable-folder-highlight'); } - // document.body.classList.add('fv-hide-link-list'); + if (this.settings.cursiveNameInPath) { + document.body.classList.add('folder-note-cursive-path'); + } + if (this.settings.underlineFolderInPath) { + document.body.classList.add('folder-note-underline-path'); + } + if (this.settings.stopWhitespaceCollapsing) { + document.body.classList.add('fn-whitespace-stop-collapsing'); + } + if (this.settings.hideCollapsingIcon) { + document.body.classList.add('fn-hide-collapse-icon'); + } + if (!this.settings.highlightFolder) { + document.body.classList.add('disable-folder-highlight'); + } + if (requireApiVersion('1.7.2')) { document.body.classList.add('version-1-7-2'); } @@ -74,23 +97,11 @@ export default class FolderNotesPlugin extends Plugin { this.app.workspace.onLayoutReady(this.onLayoutReady.bind(this)); - // this.observer.observe(document.body, { - // childList: true, - // subtree: true, - // }); - if (!this.settings.persistentSettingsTab.afterRestart) { this.settings.settingsTab = 'general'; } this.registerDomEvent(window, 'keydown', (event: KeyboardEvent) => { - // Was a bit too buggy - // if (event.key === 'Enter') { - // const folderNote = getFolderNote(this, getFocusedItem(this)?.file?.path || ''); - // if (!folderNote) return; - // openFolderNote(this, folderNote); - // } - const { hoveredElement } = this; if (this.hoverLinkTriggered) return; if (!hoveredElement) return; @@ -143,12 +154,15 @@ export default class FolderNotesPlugin extends Plugin { this.handleVaultChange(); })); - this.registerMarkdownCodeBlockProcessor('folder-overview', (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => { - this.handleOverviewBlock(source, el, ctx); - }); + this.registerMarkdownCodeBlockProcessor( + 'folder-overview', + (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => { + this.handleOverviewBlock(source, el, ctx); + }, + ); } - onLayoutReady() { + onLayoutReady(): void { if (!this._loaded) { return; } @@ -175,8 +189,9 @@ export default class FolderNotesPlugin extends Plugin { const fileExplorerPlugin = this.app.internalPlugins.getEnabledPluginById('file-explorer'); if (fileExplorerPlugin) { - const originalRevealInFolder = fileExplorerPlugin.revealInFolder.bind(fileExplorerPlugin); - fileExplorerPlugin.revealInFolder = (file: TAbstractFile) => { + const originalRevealInFolder = fileExplorerPlugin.revealInFolder + .bind(fileExplorerPlugin); + fileExplorerPlugin.revealInFolder = (file: TAbstractFile): void => { if (file instanceof TFile) { const folder = getFolder(this, file); if (folder instanceof TFolder) { @@ -186,9 +201,10 @@ export default class FolderNotesPlugin extends Plugin { } document.body.classList.remove('hide-folder-note'); originalRevealInFolder.call(fileExplorerPlugin, folder); + const FOLDER_REVEAL_DELAY = 100; setTimeout(() => { document.body.classList.add('hide-folder-note'); - }, 100); + }, FOLDER_REVEAL_DELAY); return; } } @@ -201,26 +217,29 @@ export default class FolderNotesPlugin extends Plugin { if (!view) { return; } - // @ts-ignore - const editMode = view.editMode ?? view.sourceMode ?? this.app.workspace.activeEditor?.editMode; + // @ts-expect-error use internal API + const editMode = view.editMode ?? view.sourceMode + // @ts-expect-error use internal API + ?? this.app.workspace.activeEditor?.editMode; const plugin = this; if (!editMode) { return; } - // @ts-ignore const clipboardProto = editMode.clipboardManager.constructor.prototype; const originalHandleDragOver = clipboardProto.handleDragOver; const originalHandleDrop = clipboardProto.handleDrop; - clipboardProto.handleDragOver = function (evt: DragEvent, ...args: any[]) { + clipboardProto.handleDragOver = function (evt: DragEvent, ...args: unknown[]): void { const { dragManager } = this.app; const draggable = dragManager?.draggable; if (draggable?.file instanceof TFolder) { const folderNote = getFolderNote(plugin, draggable.file.path); if (folderNote) { - dragManager.setAction(window.i18next.t('interface.drag-and-drop.insert-link-here')); + dragManager.setAction( + window.i18next.t('interface.drag-and-drop.insert-link-here'), + ); return; } } @@ -228,7 +247,7 @@ export default class FolderNotesPlugin extends Plugin { return originalHandleDragOver.call(this, evt, ...args); }; - clipboardProto.handleDrop = function (evt: DragEvent, ...args: any[]) { + clipboardProto.handleDrop = function (evt: DragEvent, ...args: unknown[]): void { const { dragManager } = this.app; const draggable = dragManager?.draggable; @@ -248,57 +267,35 @@ export default class FolderNotesPlugin extends Plugin { } } - handleVaultChange() { + handleVaultChange(): void { if (!this.settings.fvGlobalSettings.autoUpdateLinks) return; + const DEBOUNCE_DELAY = 2000; debounce(() => { updateAllOverviews(this); - }, 2000, true)(); + }, DEBOUNCE_DELAY, true)(); } - handleFileExplorerClick(evt: MouseEvent) { + handleFileExplorerClick(evt: MouseEvent): void { const target = evt.target as HTMLElement; if (evt.shiftKey) return; + if (this.isMobileClickDisabled()) return; - if (Platform.isMobile && this.settings.disableOpenFolderNoteOnClick) return; - - // Check if the click is on a folder - const folderTitleEl = target.closest('.nav-folder-title') as HTMLElement; + const { folderTitleEl, onlyClickedOnFolderTitle } = this.getFolderTitleInfo(target); if (!folderTitleEl) return; + if (this.shouldIgnoreClickByWhitespaceOrCollapse(target, onlyClickedOnFolderTitle)) return; - const onlyClickedOnFolderTitle = !!target.closest('.nav-folder-title-content'); - // const folderTitleContentEl = target.closest('.nav-folder-title-content') as HTMLElement; - // if (folderTitleContentEl) { - // const rect = folderTitleContentEl.getBoundingClientRect(); - // const clickOffsetX = evt.clientX - rect.left; - // // Ignore clicks within the first N pixels, e.g., 20px where the icon is displayed - // if (clickOffsetX < 20) return; - // } - if (!this.settings.stopWhitespaceCollapsing && !onlyClickedOnFolderTitle) return; - - // Ignore clicks on the collapse icon - if (target.closest('.collapse-icon')) return; - - const folderPath = folderTitleEl.getAttribute('data-path'); + const folderPath = this.getValidFolderPath(folderTitleEl); if (!folderPath) return; - const excludedFolder = getExcludedFolder(this, folderPath, true); - if (excludedFolder?.disableFolderNote) return; - - const usedCtrl = Platform.isMacOS ? evt.metaKey : evt.ctrlKey; - + const usedCtrl = this.isCtrlUsed(evt); const folderNote = getFolderNote(this, folderPath); - if (!folderNote && (evt.altKey || Keymap.isModEvent(evt) === 'tab')) { - if ((this.settings.altKey && evt.altKey) || (usedCtrl && this.settings.ctrlKey)) { - createFolderNote(this, folderPath, true, undefined, true); - addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, this); - removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, this); - return; - } + + if (!folderNote && this.shouldCreateNote(evt, usedCtrl)) { + this.createNoteAndMark(folderPath); + return; } if (!(folderNote instanceof TFile)) return; - - if (this.settings.openWithCtrl && !usedCtrl) return; - if (this.settings.openWithAlt && !evt.altKey) return; + if (!this.shouldOpenNote(usedCtrl, evt)) return; if (!this.settings.enableCollapsing || usedCtrl) { evt.preventDefault(); @@ -308,6 +305,58 @@ export default class FolderNotesPlugin extends Plugin { openFolderNote(this, folderNote, evt); } + private isMobileClickDisabled(): boolean { + return Platform.isMobile && this.settings.disableOpenFolderNoteOnClick; + } + + private getFolderTitleInfo(target: HTMLElement): { + folderTitleEl: HTMLElement | null; + onlyClickedOnFolderTitle: boolean; + } { + const folderTitleEl = target.closest('.nav-folder-title') as HTMLElement | null; + const onlyClickedOnFolderTitle = !!target.closest('.nav-folder-title-content'); + return { folderTitleEl, onlyClickedOnFolderTitle }; + } + + private shouldIgnoreClickByWhitespaceOrCollapse( + target: HTMLElement, + onlyClickedOnFolderTitle: boolean, + ): boolean { + if (!this.settings.stopWhitespaceCollapsing && !onlyClickedOnFolderTitle) return true; + if (target.closest('.collapse-icon')) return true; + return false; + } + + private getValidFolderPath(folderTitleEl: HTMLElement): string | null { + const folderPath = folderTitleEl.getAttribute('data-path'); + if (!folderPath) return null; + const excludedFolder = getExcludedFolder(this, folderPath, true); + if (excludedFolder?.disableFolderNote) return null; + return folderPath; + } + + private isCtrlUsed(evt: MouseEvent): boolean { + return Platform.isMacOS ? evt.metaKey : evt.ctrlKey; + } + + private shouldCreateNote(evt: MouseEvent, usedCtrl: boolean): boolean { + const isTabMod = Keymap.isModEvent(evt) === 'tab'; + if (!(evt.altKey || isTabMod)) return false; + return (this.settings.altKey && evt.altKey) || (usedCtrl && this.settings.ctrlKey); + } + + private createNoteAndMark(folderPath: string): void { + createFolderNote(this, folderPath, true, undefined, true); + addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, this); + removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, this); + } + + private shouldOpenNote(usedCtrl: boolean, evt: MouseEvent): boolean { + if (this.settings.openWithCtrl && !usedCtrl) return false; + if (this.settings.openWithAlt && !evt.altKey) return false; + return true; + } + handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext): void { const observer = new MutationObserver(() => { const editButton = el.parentElement?.childNodes.item(1); @@ -317,8 +366,12 @@ export default class FolderNotesPlugin extends Plugin { e.preventDefault(); e.stopPropagation(); new FolderOverviewSettings( - this.app, this, parseYaml(source), - ctx, el, this.settings.defaultOverview, + this.app, + this, + parseYaml(source), + ctx, + el, + this.settings.defaultOverview, ).open(); }, { capture: true }); } @@ -331,21 +384,35 @@ export default class FolderNotesPlugin extends Plugin { try { if (this.app.workspace.layoutReady) { - const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview); + const { defaultOverview } = this.settings; + const folderOverview = new FolderOverview( + this, + ctx, + source, + el, + defaultOverview, + ); folderOverview.create(this, el, ctx); } else { this.app.workspace.onLayoutReady(() => { - const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview); + const folderOverview = new FolderOverview( + this, + ctx, + source, + el, + this.settings.defaultOverview, + ); folderOverview.create(this, el, ctx); }); } } catch (e) { + // eslint-disable-next-line max-len new Notice('Error creating folder overview (folder notes plugin) - check console for more details'); console.error(e); } } - async activateOverviewView() { + async activateOverviewView(): Promise { const { workspace } = this.app; let leaf: WorkspaceLeaf | null = null; @@ -362,13 +429,14 @@ export default class FolderNotesPlugin extends Plugin { workspace.revealLeaf(leaf); } - updateOverviewView = updateOverviewView; - updateViewDropdown = updateViewDropdown; + updateOverviewView: typeof updateOverviewView = updateOverviewView; + updateViewDropdown: typeof updateViewDropdown = updateViewDropdown; isEmptyFolderNoteFolder(folder: TFolder): boolean { const attachmentFolderPath = this.app.vault.getConfig('attachmentFolderPath') as string; const cleanAttachmentFolderPath = attachmentFolderPath?.replace('./', '') || ''; - const attachmentsAreInRootFolder = attachmentFolderPath === './' || attachmentFolderPath === ''; + const attachmentsAreInRootFolder = attachmentFolderPath === './' + || attachmentFolderPath === ''; const threshold = this.settings.storageLocation === 'insideFolder' ? 1 : 0; if (folder.children.length === 0) { addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this); @@ -379,10 +447,13 @@ export default class FolderNotesPlugin extends Plugin { } else if (folder.children.length > threshold) { if (attachmentsAreInRootFolder) { return false; - } else if (this.settings.ignoreAttachmentFolder && this.app.vault.getAbstractFileByPath(`${folder.path}/${cleanAttachmentFolderPath}`)) { + } else if (this.settings.ignoreAttachmentFolder + && this.app.vault.getAbstractFileByPath( + `${folder.path}/${cleanAttachmentFolderPath}`)) { const folderPath = `${folder.path}/${cleanAttachmentFolderPath}`; const attachmentFolder = this.app.vault.getAbstractFileByPath(folderPath); - if (attachmentFolder instanceof TFolder && folder.children.length <= threshold + 1) { + if (attachmentFolder instanceof TFolder + && folder.children.length <= threshold + 1) { if (!folder.collapsed) { getFileExplorerElement(folder.path, this)?.click(); } @@ -395,13 +466,20 @@ export default class FolderNotesPlugin extends Plugin { return true; } - async changeFolderNameInExplorer(folder: TFolder, newName: string | null | undefined, waitForCreate = false, count = 0) { + async changeFolderNameInExplorer( + folder: TFolder, + newName: string | null | undefined, + waitForCreate = false, + count = 0, + ): Promise { + const MAX_RETRY_COUNT = 5; + const RETRY_DELAY_MS = 500; if (!newName) newName = folder.name; let fileExplorerItem = getFileExplorerElement(folder.path, this); if (!fileExplorerItem) { - if (waitForCreate && count < 5) { - await new Promise((r) => setTimeout(r, 500)); - this.changeFolderNameInExplorer(folder, newName, waitForCreate, count + 1); + if (waitForCreate && count < MAX_RETRY_COUNT) { + await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS)); + void this.changeFolderNameInExplorer(folder, newName, waitForCreate, count + 1); return; } return; @@ -410,15 +488,19 @@ export default class FolderNotesPlugin extends Plugin { fileExplorerItem = fileExplorerItem?.querySelector('div.nav-folder-title-content'); if (!fileExplorerItem) { return; } if (this.settings.frontMatterTitle.explorer && this.settings.frontMatterTitle.enabled) { - fileExplorerItem.innerText = newName; - fileExplorerItem.setAttribute('old-name', folder.name); + (fileExplorerItem as HTMLElement).innerText = newName; + (fileExplorerItem as HTMLElement).setAttribute('old-name', folder.name); } else { - fileExplorerItem.innerText = folder.name; - fileExplorerItem.removeAttribute('old-name'); + (fileExplorerItem as HTMLElement).innerText = folder.name; + (fileExplorerItem as HTMLElement).removeAttribute('old-name'); } } - async changeFolderNameInPath(folder: TFolder, newName: string | null | undefined, breadcrumb: HTMLElement) { + async changeFolderNameInPath( + folder: TFolder, + newName: string | null | undefined, + breadcrumb: HTMLElement, + ): Promise { if (!newName) newName = folder.name; breadcrumb.textContent = folder.newName || folder.name; @@ -429,7 +511,7 @@ export default class FolderNotesPlugin extends Plugin { /** * Updates all folder names in the path above the note editor */ - updateAllBreadcrumbs(remove?: boolean) { + updateAllBreadcrumbs(remove?: boolean): void { if (!this.settings.frontMatterTitle.path && !remove) { return; } const viewHeaderItems = document.querySelectorAll('span.view-header-breadcrumb'); const files = this.app.vault.getAllLoadedFiles().filter((file) => file instanceof TFolder); @@ -449,7 +531,7 @@ export default class FolderNotesPlugin extends Plugin { }); } - onunload() { + onunload(): void { unregisterFileExplorerObserver(); document.body.classList.remove('folder-notes-plugin'); document.body.classList.remove('folder-note-underline'); @@ -461,7 +543,7 @@ export default class FolderNotesPlugin extends Plugin { } } - async loadSettings() { + async loadSettings(): Promise { const data = await this.loadData(); if (data) { if (data.allowWhitespaceCollapsing === true) { @@ -479,12 +561,14 @@ export default class FolderNotesPlugin extends Plugin { } if (!data) { return; } - const overview = (data as any).defaultOverview; + const overview = data.defaultOverview; if (!overview) { return; } - this.settings.defaultOverview = Object.assign({}, DEFAULT_SETTINGS.defaultOverview, overview); + this.settings.defaultOverview = Object.assign( + {}, DEFAULT_SETTINGS.defaultOverview, overview, + ); } - async saveSettings(reloadStyles?: boolean) { + async saveSettings(reloadStyles?: boolean): Promise { await this.saveData(this.settings); // cleanup any css if we need too if ((!this.settingsOpened || reloadStyles === true) && reloadStyles !== false) { diff --git a/src/modals/AddSupportedFileType.ts b/src/modals/AddSupportedFileType.ts index c4e802d..787f4e1 100644 --- a/src/modals/AddSupportedFileType.ts +++ b/src/modals/AddSupportedFileType.ts @@ -1,5 +1,4 @@ -import type { App, SettingTab } from 'obsidian'; -import { Modal, Setting, Notice } from 'obsidian'; +import { Modal, Setting, Notice, type App, type SettingTab } from 'obsidian'; import type FolderNotesPlugin from '../main'; import type { ListComponent } from 'src/functions/ListComponent'; @@ -17,7 +16,8 @@ export default class AddSupportedFileModal extends Modal { this.list = list; this.settingsTab = settingsTab; } - onOpen() { + + onOpen(): void { const { contentEl } = this; // close when user presses enter contentEl.addEventListener('keydown', (e) => { @@ -38,7 +38,7 @@ export default class AddSupportedFileModal extends Modal { }), ); } - async onClose() { + async onClose(): Promise { if (this.name.toLocaleLowerCase() === 'markdown') { this.name = 'md'; } @@ -47,9 +47,9 @@ export default class AddSupportedFileModal extends Modal { contentEl.empty(); this.settingsTab.display(); } else if (this.plugin.settings.supportedFileTypes.includes(this.name.toLowerCase())) { - return new Notice('This extension is already supported'); + new Notice('This extension is already supported'); + return; } else { - // @ts-ignore await this.list.addValue(this.name.toLowerCase()); this.settingsTab.display(); this.plugin.saveSettings(); diff --git a/src/modals/AskForExtension.ts b/src/modals/AskForExtension.ts index 50b5ee8..7acd569 100644 --- a/src/modals/AskForExtension.ts +++ b/src/modals/AskForExtension.ts @@ -1,5 +1,4 @@ -import type { TFile } from 'obsidian'; -import { FuzzySuggestModal } from 'obsidian'; +import { FuzzySuggestModal, type TFile } from 'obsidian'; import type FolderNotesPlugin from 'src/main'; import { createFolderNote } from 'src/functions/folderNoteFunctions'; export class AskForExtensionModal extends FuzzySuggestModal { @@ -9,7 +8,14 @@ export class AskForExtensionModal extends FuzzySuggestModal { openFile: boolean; useModal: boolean | undefined; existingNote: TFile | undefined; - constructor(plugin: FolderNotesPlugin, folderPath: string, openFile: boolean, extension: string, useModal?: boolean, existingNote?: TFile) { + constructor( + plugin: FolderNotesPlugin, + folderPath: string, + openFile: boolean, + extension: string, + useModal?: boolean, + existingNote?: TFile, + ) { super(plugin.app); this.plugin = plugin; this.folderPath = folderPath; @@ -21,17 +27,26 @@ export class AskForExtensionModal extends FuzzySuggestModal { } getItems(): string[] { - return this.plugin.settings.supportedFileTypes.filter((item) => item.toLowerCase() !== '.ask'); + return this.plugin.settings.supportedFileTypes.filter( + (item) => item.toLowerCase() !== '.ask', + ); } getItemText(item: string): string { return item; } - onChooseItem(item: string, evt: MouseEvent | KeyboardEvent) { + onChooseItem(item: string, _evt: MouseEvent | KeyboardEvent): void { this.plugin.askModalCurrentlyOpen = false; this.extension = '.' + item; - createFolderNote(this.plugin, this.folderPath, this.openFile, this.extension, this.useModal, this.existingNote); + createFolderNote( + this.plugin, + this.folderPath, + this.openFile, + this.extension, + this.useModal, + this.existingNote, + ); this.close(); } } diff --git a/src/modals/DeleteConfirmation.ts b/src/modals/DeleteConfirmation.ts index c179ec4..ac857b6 100644 --- a/src/modals/DeleteConfirmation.ts +++ b/src/modals/DeleteConfirmation.ts @@ -1,5 +1,4 @@ -import type { App, TFile } from 'obsidian'; -import { Modal, Platform } from 'obsidian'; +import { Modal, Platform, type App, type TFile } from 'obsidian'; import type FolderNotesPlugin from '../main'; import { deleteFolderNote } from 'src/functions/folderNoteFunctions'; export default class DeleteConfirmationModal extends Modal { @@ -12,21 +11,25 @@ export default class DeleteConfirmationModal extends Modal { this.app = app; this.file = file; } - onOpen() { + onOpen(): void { const { contentEl, plugin } = this; const modalTitle = contentEl.createDiv({ cls: 'fn-modal-title' }); const modalContent = contentEl.createDiv({ cls: 'fn-modal-content' }); modalTitle.createEl('h2', { text: 'Delete folder note' }); + // eslint-disable-next-line max-len modalContent.createEl('p', { text: `Are you sure you want to delete the folder note '${this.file.name}' ?` }); switch (plugin.settings.deleteFilesAction) { case 'trash': modalContent.createEl('p', { text: 'It will be moved to your system trash.' }); break; case 'obsidianTrash': + // eslint-disable-next-line max-len modalContent.createEl('p', { text: 'It will be moved to your Obsidian trash, which is located in the ".trash" hidden folder in your vault.' }); break; case 'delete': - modalContent.createEl('p', { text: 'It will be permanently deleted.' }).setCssStyles({ color: 'red' }); + modalContent + .createEl('p', { text: 'It will be permanently deleted.' }) + .setCssStyles({ color: 'red' }); break; } @@ -47,7 +50,10 @@ export default class DeleteConfirmationModal extends Modal { plugin.saveSettings(); }); } else { - const confirmButton = buttonContainer.createEl('button', { text: 'Delete and don\'t ask again', cls: 'mod-destructive' }); + const confirmButton = buttonContainer.createEl('button', { + text: 'Delete and don\'t ask again', + cls: 'mod-destructive', + }); confirmButton.addEventListener('click', async () => { plugin.settings.showDeleteConfirmation = false; plugin.saveSettings(); @@ -56,19 +62,25 @@ export default class DeleteConfirmationModal extends Modal { }); } - const deleteButton = buttonContainer.createEl('button', { text: 'Delete', cls: 'mod-warning' }); + const deleteButton = buttonContainer.createEl('button', { + text: 'Delete', + cls: 'mod-warning', + }); deleteButton.addEventListener('click', async () => { this.close(); deleteFolderNote(plugin, this.file, false); }); deleteButton.focus(); - const cancelButton = buttonContainer.createEl('button', { text: 'Cancel', cls: 'mod-cancel' }); + const cancelButton = buttonContainer.createEl('button', { + text: 'Cancel', + cls: 'mod-cancel', + }); cancelButton.addEventListener('click', async () => { this.close(); }); } - onClose() { + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/modals/ExistingNote.ts b/src/modals/ExistingNote.ts index d3c680c..95ac4da 100644 --- a/src/modals/ExistingNote.ts +++ b/src/modals/ExistingNote.ts @@ -1,5 +1,12 @@ -import type { App, TFile, TFolder, TAbstractFile } from 'obsidian'; -import { Modal, Setting, Platform } from 'obsidian'; +import { + Modal, + Setting, + Platform, + type App, + type TFile, + type TFolder, + type TAbstractFile, +} from 'obsidian'; import type FolderNotesPlugin from '../main'; import { turnIntoFolderNote } from 'src/functions/folderNoteFunctions'; export default class ExistingFolderNoteModal extends Modal { @@ -8,7 +15,13 @@ export default class ExistingFolderNoteModal extends Modal { file: TFile; folder: TFolder; folderNote: TAbstractFile; - constructor(app: App, plugin: FolderNotesPlugin, file: TFile, folder: TFolder, folderNote: TAbstractFile) { + constructor( + app: App, + plugin: FolderNotesPlugin, + file: TFile, + folder: TFolder, + folderNote: TAbstractFile, + ) { super(app); this.plugin = plugin; this.app = app; @@ -16,18 +29,22 @@ export default class ExistingFolderNoteModal extends Modal { this.folder = folder; this.folderNote = folderNote; } - onOpen() { + onOpen(): void { const { contentEl } = this; contentEl.createEl('h2', { text: 'A folder note for this folder already exists' }); const setting = new Setting(contentEl); + // eslint-disable-next-line max-len setting.infoEl.createEl('p', { text: 'Are you sure you want to turn the note into a folder note and rename the existing folder note?' }); setting.infoEl.parentElement?.classList.add('fn-delete-confirmation-modal'); // Create a container for the buttons and the checkbox + // eslint-disable-next-line max-len const buttonContainer = setting.infoEl.createEl('div', { cls: 'fn-delete-confirmation-modal-buttons' }); if (Platform.isMobileApp) { - const confirmButton = buttonContainer.createEl('button', { text: 'Rename and don\'t ask again' }); + const confirmButton = buttonContainer.createEl('button', { + text: 'Rename and don\'t ask again', + }); confirmButton.classList.add('mod-warning', 'fn-confirmation-modal-button'); confirmButton.addEventListener('click', async () => { this.plugin.settings.showRenameConfirmation = false; @@ -64,7 +81,7 @@ export default class ExistingFolderNoteModal extends Modal { }); } - onClose() { + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/modals/FolderName.ts b/src/modals/FolderName.ts index bdef456..b50cf57 100644 --- a/src/modals/FolderName.ts +++ b/src/modals/FolderName.ts @@ -1,5 +1,4 @@ -import type { App, TFolder } from 'obsidian'; -import { Modal, Setting } from 'obsidian'; +import { Modal, Setting, type App, type TFolder } from 'obsidian'; import type FolderNotesPlugin from '../main'; export default class FolderNameModal extends Modal { plugin: FolderNotesPlugin; @@ -11,7 +10,8 @@ export default class FolderNameModal extends Modal { this.app = app; this.folder = folder; } - onOpen() { + + onOpen(): void { const { contentEl } = this; // close when user presses enter contentEl.addEventListener('keydown', (e) => { @@ -27,14 +27,25 @@ export default class FolderNameModal extends Modal { .setValue(this.folder.name.replace(this.plugin.settings.folderNoteType, '')) .onChange(async (value) => { if (value.trim() !== '') { - if (!this.app.vault.getAbstractFileByPath(this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + value.trim())) { - this.plugin.app.fileManager.renameFile(this.folder, this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + value.trim()); + const parentPath = this.folder.path.slice( + 0, + this.folder.path.lastIndexOf('/') + 1, + ); + const newFolderPath = parentPath + value.trim(); + if ( + !this.app.vault.getAbstractFileByPath(newFolderPath) + ) { + this.plugin.app.fileManager.renameFile( + this.folder, + newFolderPath, + ); } } }), ); } - onClose() { + + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/modals/NewFolderName.ts b/src/modals/NewFolderName.ts index 64fa454..8ecb63f 100644 --- a/src/modals/NewFolderName.ts +++ b/src/modals/NewFolderName.ts @@ -1,5 +1,4 @@ -import type { App, TFolder } from 'obsidian'; -import { Modal } from 'obsidian'; +import { Modal, type App, type TFolder } from 'obsidian'; import type FolderNotesPlugin from '../main'; export default class NewFolderNameModal extends Modal { plugin: FolderNotesPlugin; @@ -11,7 +10,8 @@ export default class NewFolderNameModal extends Modal { this.app = app; this.folder = folder; } - onOpen() { + + onOpen(): void { const { contentEl } = this; contentEl.addEventListener('keydown', (e) => { @@ -37,7 +37,7 @@ export default class NewFolderNameModal extends Modal { }, }); - textarea.addEventListener('focus', function() { + textarea.addEventListener('focus', function () { this.select(); }); @@ -50,23 +50,32 @@ export default class NewFolderNameModal extends Modal { this.close(); }); - const cancelButton = buttonContainer.createEl('button', { text: 'Cancel', cls: 'mod-cancel' }); + const cancelButton = buttonContainer.createEl('button', { + text: 'Cancel', + cls: 'mod-cancel', + }); cancelButton.addEventListener('click', () => { this.close(); }); } - onClose() { + + onClose(): void { const { contentEl } = this; contentEl.empty(); } - saveFolderName() { + saveFolderName(): void { const textarea = this.contentEl.querySelector('textarea'); if (textarea) { const newName = textarea.value.trim(); if (newName.trim() !== '') { - if (!this.app.vault.getAbstractFileByPath(this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + newName.trim())) { - this.plugin.app.fileManager.renameFile(this.folder, this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + newName.trim()); + const folderBasePath = this.folder.path.slice( + 0, + this.folder.path.lastIndexOf('/') + 1, + ); + const newFolderPath = folderBasePath + newName.trim(); + if (!this.app.vault.getAbstractFileByPath(newFolderPath)) { + this.plugin.app.fileManager.renameFile(this.folder, newFolderPath); } } } diff --git a/src/settings/ExcludedFoldersSettings.ts b/src/settings/ExcludedFoldersSettings.ts index e4126fb..b8b9345 100644 --- a/src/settings/ExcludedFoldersSettings.ts +++ b/src/settings/ExcludedFoldersSettings.ts @@ -1,4 +1,7 @@ -import { addExcludeFolderListItem, addExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; +import { + addExcludeFolderListItem, + addExcludedFolder, +} from 'src/ExcludeFolders/functions/folderFunctions'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; import { addExcludePatternListItem } from 'src/ExcludeFolders/functions/patternFunctions'; import { Setting } from 'obsidian'; @@ -8,7 +11,7 @@ import PatternSettings from 'src/ExcludeFolders/modals/PatternSettings'; import WhitelistedFoldersSettings from 'src/ExcludeFolders/modals/WhitelistedFoldersSettings'; // import ExcludedFoldersWhitelist from 'src/ExcludeFolders/modals/WhitelistModal'; -export async function renderExcludeFolders(settingsTab: SettingsTab) { +export async function renderExcludeFolders(settingsTab: SettingsTab): Promise { const containerEl = settingsTab.settingsPage; const manageExcluded = new Setting(containerEl) .setHeading() @@ -25,9 +28,12 @@ export async function renderExcludeFolders(settingsTab: SettingsTab) { 'Use * after the folder name to exclude folders that start with the folder name.', ); manageExcluded.setDesc(desc3); + // eslint-disable-next-line max-len manageExcluded.infoEl.appendText('The regexes and wildcards are only for the folder name, not the path.'); manageExcluded.infoEl.createEl('br'); + // eslint-disable-next-line max-len manageExcluded.infoEl.appendText('If you want to switch to a folder path delete the pattern first.'); + // eslint-disable-next-line max-len manageExcluded.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; @@ -48,7 +54,11 @@ export async function renderExcludeFolders(settingsTab: SettingsTab) { cb.setButtonText('Manage'); cb.setCta(); cb.onClick(async () => { - new ExcludedFolderSettings(settingsTab.app, settingsTab.plugin, settingsTab.plugin.settings.excludeFolderDefaultSettings).open(); + new ExcludedFolderSettings( + settingsTab.app, + settingsTab.plugin, + settingsTab.plugin.settings.excludeFolderDefaultSettings, + ).open(); }); }); @@ -58,7 +68,11 @@ export async function renderExcludeFolders(settingsTab: SettingsTab) { cb.setButtonText('Manage'); cb.setCta(); cb.onClick(async () => { - new PatternSettings(settingsTab.app, settingsTab.plugin, settingsTab.plugin.settings.excludePatternDefaultSettings).open(); + new PatternSettings( + settingsTab.app, + settingsTab.plugin, + settingsTab.plugin.settings.excludePatternDefaultSettings, + ).open(); }); }); @@ -71,18 +85,29 @@ export async function renderExcludeFolders(settingsTab: SettingsTab) { cb.setClass('add-exclude-folder'); cb.setTooltip('Add excluded folder'); cb.onClick(() => { - const excludedFolder = new ExcludedFolder('', settingsTab.plugin.settings.excludeFolders.length, undefined, settingsTab.plugin); + const excludedFolder = new ExcludedFolder( + '', + settingsTab.plugin.settings.excludeFolders.length, + undefined, + settingsTab.plugin, + ); addExcludeFolderListItem(settingsTab, containerEl, excludedFolder); addExcludedFolder(settingsTab.plugin, excludedFolder); settingsTab.display(); }); }); - settingsTab.plugin.settings.excludeFolders.filter((folder) => !folder.hideInSettings).sort((a, b) => a.position - b.position).forEach((excludedFolder) => { - if (excludedFolder.string?.trim() !== '' && excludedFolder.path?.trim() === '') { - addExcludePatternListItem(settingsTab, containerEl, excludedFolder); - } else { - addExcludeFolderListItem(settingsTab, containerEl, excludedFolder); - } - }); + settingsTab.plugin.settings.excludeFolders + .filter((folder) => !folder.hideInSettings) + .sort((a, b) => a.position - b.position) + .forEach((excludedFolder) => { + if ( + excludedFolder.string?.trim() !== '' && + excludedFolder.path?.trim() === '' + ) { + addExcludePatternListItem(settingsTab, containerEl, excludedFolder); + } else { + addExcludeFolderListItem(settingsTab, containerEl, excludedFolder); + } + }); } diff --git a/src/settings/FileExplorerSettings.ts b/src/settings/FileExplorerSettings.ts index 0d79841..c69e620 100644 --- a/src/settings/FileExplorerSettings.ts +++ b/src/settings/FileExplorerSettings.ts @@ -1,6 +1,7 @@ +/* eslint-disable max-len */ import { Setting } from 'obsidian'; import type { SettingsTab } from './SettingsTab'; -export async function renderFileExplorer(settingsTab: SettingsTab) { +export async function renderFileExplorer(settingsTab: SettingsTab): Promise { const containerEl = settingsTab.settingsPage; new Setting(containerEl) @@ -34,7 +35,8 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { ); setting2.infoEl.appendText('Requires a restart to take effect'); - setting2.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; + const setting2AccentColor = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; + setting2.infoEl.style.color = setting2AccentColor; new Setting(containerEl) .setName('Open folder notes by only clicking directly on the folder name') @@ -65,7 +67,8 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { }), ); disableSetting.infoEl.appendText('Requires a restart to take effect'); - disableSetting.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; + const accentColor = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed'; + disableSetting.infoEl.style.color = accentColor; new Setting(containerEl) .setName('Use submenus') @@ -91,7 +94,15 @@ export async function renderFileExplorer(settingsTab: SettingsTab) { settingsTab.plugin.settings.frontMatterTitle.explorer = value; await settingsTab.plugin.saveSettings(); settingsTab.plugin.app.vault.getFiles().forEach((file) => { - settingsTab.plugin.fmtpHandler?.fmptUpdateFileName({ id: '', result: false, path: file.path, pathOnly: false }, false); + settingsTab.plugin.fmtpHandler?.fmptUpdateFileName( + { + id: '', + result: false, + path: file.path, + pathOnly: false, + }, + false, + ); }); }), ); diff --git a/src/settings/FolderOverviewSettings.ts b/src/settings/FolderOverviewSettings.ts index 310fb1e..de253e3 100644 --- a/src/settings/FolderOverviewSettings.ts +++ b/src/settings/FolderOverviewSettings.ts @@ -2,7 +2,7 @@ import { Setting } from 'obsidian'; import type { SettingsTab } from './SettingsTab'; import { createOverviewSettings } from 'src/obsidian-folder-overview/src/settings'; -export async function renderFolderOverview(settingsTab: SettingsTab) { +export async function renderFolderOverview(settingsTab: SettingsTab): Promise { const { plugin } = settingsTab; const defaultOverviewSettings = plugin.settings.defaultOverview; const containerEl = settingsTab.settingsPage; @@ -10,6 +10,7 @@ export async function renderFolderOverview(settingsTab: SettingsTab) { containerEl.createEl('h3', { text: 'Global settings' }); new Setting(containerEl) .setName('Auto-update links without opening the overview') + // eslint-disable-next-line max-len .setDesc('If enabled, the links that appear in the graph view will be updated even when you don\'t have the overview open somewhere.') .addToggle((toggle) => toggle @@ -35,5 +36,15 @@ export async function renderFolderOverview(settingsTab: SettingsTab) { span.setAttr('style', `color: ${accentColor};`); pEl.appendChild(span); - createOverviewSettings(containerEl, defaultOverviewSettings, plugin, plugin.settings.defaultOverview, settingsTab.display, undefined, undefined, undefined, settingsTab); + createOverviewSettings( + containerEl, + defaultOverviewSettings, + plugin, + plugin.settings.defaultOverview, + settingsTab.display, + undefined, + undefined, + undefined, + settingsTab, + ); } diff --git a/src/settings/GeneralSettings.ts b/src/settings/GeneralSettings.ts index 3a15b8e..27b026a 100644 --- a/src/settings/GeneralSettings.ts +++ b/src/settings/GeneralSettings.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ import { Setting, Platform } from 'obsidian'; import type { SettingsTab } from './SettingsTab'; import { ListComponent } from '../functions/ListComponent'; @@ -11,7 +12,8 @@ import RenameFolderNotesModal from './modals/RenameFns'; let debounceTimer: NodeJS.Timeout; -export async function renderGeneral(settingsTab: SettingsTab) { +// eslint-disable-next-line complexity +export async function renderGeneral(settingsTab: SettingsTab): Promise { const containerEl = settingsTab.settingsPage; const nameSetting = new Setting(containerEl) .setName('Folder note name template') @@ -25,6 +27,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { await settingsTab.plugin.saveSettings(); clearTimeout(debounceTimer); + const FOLDER_NOTE_NAME_DEBOUNCE_MS = 2000; debounceTimer = setTimeout(() => { if (!value.includes('{{folder_name}}')) { if (!settingsTab.showFolderNameInTabTitleSetting) { @@ -37,7 +40,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.showFolderNameInTabTitleSetting = false; } } - }, 2000); + }, FOLDER_NOTE_NAME_DEBOUNCE_MS); }), ) .addButton((button) => @@ -91,13 +94,24 @@ export async function renderGeneral(settingsTab: SettingsTab) { } }); - if (!settingsTab.plugin.settings.supportedFileTypes.includes(settingsTab.plugin.settings.folderNoteType.replace('.', '')) && settingsTab.plugin.settings.folderNoteType !== '.ask') { + if ( + !settingsTab.plugin.settings.supportedFileTypes.includes( + settingsTab.plugin.settings.folderNoteType.replace('.', ''), + ) && + settingsTab.plugin.settings.folderNoteType !== '.ask' + ) { settingsTab.plugin.settings.folderNoteType = '.md'; settingsTab.plugin.saveSettings(); } - let defaultType = settingsTab.plugin.settings.folderNoteType.startsWith('.') ? settingsTab.plugin.settings.folderNoteType : '.' + settingsTab.plugin.settings.folderNoteType; - if (!settingsTab.plugin.settings.supportedFileTypes.includes(defaultType.replace('.', ''))) { + let defaultType = settingsTab.plugin.settings.folderNoteType.startsWith('.') + ? settingsTab.plugin.settings.folderNoteType + : '.' + settingsTab.plugin.settings.folderNoteType; + if ( + !settingsTab.plugin.settings.supportedFileTypes.includes( + defaultType.replace('.', ''), + ) + ) { defaultType = '.ask'; settingsTab.plugin.settings.folderNoteType = defaultType; } @@ -118,14 +132,22 @@ export async function renderGeneral(settingsTab: SettingsTab) { 'Specify which file types are allowed as folder notes. Applies to both new and existing folders. Adding many types may affect performance.', ); setting0.setDesc(desc0); - const list = new ListComponent(setting0.settingEl, settingsTab.plugin.settings.supportedFileTypes || [], ['md', 'canvas']); + const list = new ListComponent( + setting0.settingEl, + settingsTab.plugin.settings.supportedFileTypes || [], + ['md', 'canvas'], + ); list.on('update', async (values: string[]) => { settingsTab.plugin.settings.supportedFileTypes = values; await settingsTab.plugin.saveSettings(); settingsTab.display(); }); - if (!settingsTab.plugin.settings.supportedFileTypes.includes('md') || !settingsTab.plugin.settings.supportedFileTypes.includes('canvas') || !settingsTab.plugin.settings.supportedFileTypes.includes('excalidraw')) { + if ( + !settingsTab.plugin.settings.supportedFileTypes.includes('md') || + !settingsTab.plugin.settings.supportedFileTypes.includes('canvas') || + !settingsTab.plugin.settings.supportedFileTypes.includes('excalidraw') + ) { setting0.addDropdown((dropdown) => { const options = [ { value: 'md', label: 'Markdown' }, @@ -144,7 +166,12 @@ export async function renderGeneral(settingsTab: SettingsTab) { dropdown.setValue('+'); dropdown.onChange(async (value) => { if (value === 'custom') { - return new AddSupportedFileModal(settingsTab.app, settingsTab.plugin, settingsTab, list as ListComponent).open(); + return new AddSupportedFileModal( + settingsTab.app, + settingsTab.plugin, + settingsTab, + list as ListComponent, + ).open(); } await list.addValue(value.toLowerCase()); settingsTab.display(); @@ -157,7 +184,12 @@ export async function renderGeneral(settingsTab: SettingsTab) { .setButtonText('Add custom file type') .setCta() .onClick(async () => { - new AddSupportedFileModal(settingsTab.app, settingsTab.plugin, settingsTab, list as ListComponent).open(); + new AddSupportedFileModal( + settingsTab.app, + settingsTab.plugin, + settingsTab, + list as ListComponent, + ).open(); }), ); } @@ -169,7 +201,11 @@ export async function renderGeneral(settingsTab: SettingsTab) { .addSearch((cb) => { new TemplateSuggest(cb.inputEl, settingsTab.plugin); cb.setPlaceholder('Template path'); - cb.setValue(settingsTab.plugin.app.vault.getAbstractFileByPath(settingsTab.plugin.settings.templatePath)?.name.replace('.md', '') || ''); + const templateFile = settingsTab.plugin.app.vault.getAbstractFileByPath( + settingsTab.plugin.settings.templatePath, + ); + const templateName = templateFile?.name.replace('.md', '') || ''; + cb.setValue(templateName); cb.onChange(async (value) => { if (value.trim() === '') { settingsTab.plugin.settings.templatePath = ''; @@ -464,16 +500,26 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.frontMatterTitle.enabled = value; await settingsTab.plugin.saveSettings(); if (value) { - settingsTab.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(settingsTab.plugin); + settingsTab.plugin.fmtpHandler = + new FrontMatterTitlePluginHandler(settingsTab.plugin); } else { if (settingsTab.plugin.fmtpHandler) { settingsTab.plugin.updateAllBreadcrumbs(true); } settingsTab.plugin.app.vault.getFiles().forEach((file) => { - settingsTab.plugin.fmtpHandler?.fmptUpdateFileName({ id: '', result: false, path: file.path, pathOnly: false }, false); + settingsTab.plugin.fmtpHandler?.fmptUpdateFileName( + { + id: '', + result: false, + path: file.path, + pathOnly: false, + }, + false, + ); }); settingsTab.plugin.fmtpHandler?.deleteEvent(); - settingsTab.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(settingsTab.plugin); + settingsTab.plugin.fmtpHandler = + new FrontMatterTitlePluginHandler(settingsTab.plugin); } settingsTab.display(); }), diff --git a/src/settings/PathSettings.ts b/src/settings/PathSettings.ts index 3ea8be2..034b1ed 100644 --- a/src/settings/PathSettings.ts +++ b/src/settings/PathSettings.ts @@ -1,6 +1,7 @@ +/* eslint-disable max-len */ import { Setting } from 'obsidian'; import type { SettingsTab } from './SettingsTab'; -export async function renderPath(settingsTab: SettingsTab) { +export async function renderPath(settingsTab: SettingsTab): Promise { const containerEl = settingsTab.settingsPage; new Setting(containerEl) .setName('Open folder note through path') diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index e6723c8..2eeb650 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -1,5 +1,7 @@ -import type { App, MarkdownPostProcessorContext } from 'obsidian'; -import { Notice, PluginSettingTab, TFile, TFolder } from 'obsidian'; +import { + Notice, PluginSettingTab, TFile, + TFolder, type App, type MarkdownPostProcessorContext, +} from 'obsidian'; import type FolderNotesPlugin from '../main'; import type { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern'; import type { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; @@ -236,7 +238,8 @@ export class SettingsTab extends PluginSettingTab { id: 'path', }, }; - renderSettingsPage(tabId: string) { + + renderSettingsPage(tabId: string): void { this.settingsPage.empty(); switch (tabId.toLocaleLowerCase()) { case this.TABS.GENERAL.id: @@ -257,7 +260,17 @@ export class SettingsTab extends PluginSettingTab { } } - display(contentEl?: HTMLElement, yaml?: defaultOverviewSettings, plugin?: FolderNotesPlugin, defaultSettings?: boolean, display?: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: this) { + display( + contentEl?: HTMLElement, + yaml?: defaultOverviewSettings, + plugin?: FolderNotesPlugin, + defaultSettings?: boolean, + display?: CallableFunction, + el?: HTMLElement, + ctx?: MarkdownPostProcessorContext, + file?: TFile | null, + settingsTab?: this, + ): void { plugin = this?.plugin ?? plugin; if (plugin) { plugin.settingsOpened = true; @@ -274,13 +287,17 @@ export class SettingsTab extends PluginSettingTab { for (const [tabId, tabInfo] of Object.entries(settingsTab.TABS)) { const tabEl = tabBar.createEl('div', { cls: 'fn-settings-tab' }); tabEl.createEl('div', { cls: 'fn-settings-tab-name', text: tabInfo.name }); - if (plugin && plugin.settings.settingsTab.toLocaleLowerCase() === tabId.toLocaleLowerCase()) { + if ( + plugin && + plugin.settings.settingsTab.toLocaleLowerCase() === + tabId.toLocaleLowerCase() + ) { tabEl.addClass('fn-settings-tab-active'); } tabEl.addEventListener('click', () => { - // @ts-ignore - for (const tabEl of tabBar.children) { - tabEl.removeClass('fn-settings-tab-active'); + // @ts-expect-error: tabBar.children may not have removeClass method, but we know it works in this context + for (const child of tabBar.children) { + child.removeClass('fn-settings-tab-active'); if (!plugin) { return; } plugin.settings.settingsTab = tabId.toLocaleLowerCase(); plugin.saveSettings(); @@ -300,23 +317,31 @@ export class SettingsTab extends PluginSettingTab { } } - renameFolderNotes() { + renameFolderNotes(): void { new Notice('Starting to update folder notes...'); const oldTemplate = this.plugin.settings.oldFolderNoteName ?? '{{folder_name}}'; for (const folder of this.app.vault.getAllLoadedFiles()) { if (folder instanceof TFolder) { - const folderNote = getFolderNote(this.plugin, folder.path, undefined, undefined, oldTemplate); + const folderNote = getFolderNote( + this.plugin, + folder.path, + undefined, + undefined, + oldTemplate, + ); if (!(folderNote instanceof TFile)) { continue; } const folderName = extractFolderName(oldTemplate, folderNote.basename) ?? ''; - const newFolderNoteName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', folderName); + const newFolderNoteName = this.plugin.settings.folderNoteName + .replace('{{folder_name}}', folderName); let newPath = ''; if (this.plugin.settings.storageLocation === 'parentFolder') { if (getFolderPathFromString(folder.path).trim() === '/') { newPath = `${newFolderNoteName}.${folderNote.extension}`; } else { + // eslint-disable-next-line max-len newPath = `${folderNote.parent?.path}/${newFolderNoteName}.${folderNote.extension}`; } } else if (this.plugin.settings.storageLocation === 'insideFolder') { @@ -332,7 +357,7 @@ export class SettingsTab extends PluginSettingTab { new Notice('Finished updating folder notes'); } - switchStorageLocation(oldMethod: string) { + switchStorageLocation(oldMethod: string): void { new Notice('Starting to switch storage location...'); this.app.vault.getAllLoadedFiles().forEach((file) => { if (file instanceof TFolder) { diff --git a/src/settings/modals/BackupWarning.ts b/src/settings/modals/BackupWarning.ts index a0deb27..f6c8694 100644 --- a/src/settings/modals/BackupWarning.ts +++ b/src/settings/modals/BackupWarning.ts @@ -5,10 +5,16 @@ export default class BackupWarningModal extends Modal { plugin: FolderNotesPlugin; title: string; desc: string; - callback: (...args: any[]) => void; - args: any[]; - - constructor(plugin: FolderNotesPlugin, title: string, description: string, callback: (...args: any[]) => void, args: any[] = []) { + callback: (...args: unknown[]) => void; + args: unknown[]; + + constructor( + plugin: FolderNotesPlugin, + title: string, + description: string, + callback: (...args: unknown[]) => void, + args: unknown[] = [], + ) { super(plugin.app); this.plugin = plugin; this.title = title; @@ -17,7 +23,7 @@ export default class BackupWarningModal extends Modal { this.desc = description; } - onOpen() { + onOpen(): void { this.modalEl.addClass('fn-backup-warning-modal'); const { contentEl } = this; @@ -25,8 +31,7 @@ export default class BackupWarningModal extends Modal { contentEl.createEl('p', { text: this.desc }); - this.insertCustomHtml(); - + // eslint-disable-next-line max-len contentEl.createEl('p', { text: 'Make sure to backup your vault before using this feature.' }).style.color = '#fb464c'; const buttonContainer = contentEl.createDiv({ cls: 'fn-modal-button-container' }); @@ -45,11 +50,7 @@ export default class BackupWarningModal extends Modal { }); } - insertCustomHtml(): void { - - } - - onClose() { + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/settings/modals/CreateFnForEveryFolder.ts b/src/settings/modals/CreateFnForEveryFolder.ts index 0380886..048664e 100644 --- a/src/settings/modals/CreateFnForEveryFolder.ts +++ b/src/settings/modals/CreateFnForEveryFolder.ts @@ -1,5 +1,4 @@ -import type { App, ButtonComponent } from 'obsidian'; -import { Modal, Setting, TFolder, Notice } from 'obsidian'; +import { Modal, Setting, TFolder, Notice, type App, type ButtonComponent } from 'obsidian'; import type FolderNotesPlugin from '../../main'; import { createFolderNote, getFolderNote } from 'src/functions/folderNoteFunctions'; import { getTemplatePlugins } from 'src/template'; @@ -15,7 +14,8 @@ export default class ConfirmationModal extends Modal { this.app = app; this.extension = plugin.settings.folderNoteType; } - onOpen() { + + onOpen(): void { this.modalEl.addClass('fn-confirmation-modal'); let templateFolderPath: string; const { templateFolder, templaterPlugin } = getTemplatePlugins(this.plugin.app); @@ -23,19 +23,29 @@ export default class ConfirmationModal extends Modal { templateFolderPath = ''; } if (templaterPlugin) { - templateFolderPath = templaterPlugin.plugin?.settings?.templates_folder as string; - } else { + templateFolderPath = ( + templaterPlugin as unknown as { + plugin?: { settings?: { templates_folder?: string } } + } + ).plugin?.settings?.templates_folder as string; + } else if (templateFolder) { templateFolderPath = templateFolder; } const { contentEl } = this; contentEl.createEl('h2', { text: 'Create folder note for every folder' }); const setting = new Setting(contentEl); + // eslint-disable-next-line max-len setting.infoEl.createEl('p', { text: 'Make sure to backup your vault before using this feature.' }).style.color = '#fb464c'; + // eslint-disable-next-line max-len setting.infoEl.createEl('p', { text: 'This feature will create a folder note for every folder in your vault.' }); + // eslint-disable-next-line max-len setting.infoEl.createEl('p', { text: 'Every folder that already has a folder note will be ignored.' }); setting.infoEl.createEl('p', { text: 'Every excluded folder will be ignored.' }); - if (!this.plugin.settings.templatePath || this.plugin.settings.templatePath?.trim() === '') { + if ( + !this.plugin.settings.templatePath || + this.plugin.settings.templatePath?.trim() === '' + ) { new Setting(contentEl) .setName('Folder note file extension') .setDesc('Choose the file extension for the folder notes.') @@ -56,17 +66,24 @@ export default class ConfirmationModal extends Modal { cb.setCta(); cb.buttonEl.focus(); cb.onClick(async () => { - if (this.plugin.settings.templatePath && this.plugin.settings.templatePath.trim() !== '') { + if ( + this.plugin.settings.templatePath && + this.plugin.settings.templatePath.trim() !== '' + ) { this.extension = '.' + this.plugin.settings.templatePath.split('.').pop(); } if (this.extension === '.ask') { return new Notice('Please choose a file extension'); } this.close(); - const folders = this.app.vault.getAllLoadedFiles().filter((file) => file.parent instanceof TFolder); + const folders = this.app.vault + .getAllLoadedFiles() + .filter((file) => file.parent instanceof TFolder); for (const folder of folders) { if (folder instanceof TFolder) { - const excludedFolder = getExcludedFolder(this.plugin, folder.path, true); + const excludedFolder = getExcludedFolder( + this.plugin, folder.path, true, + ); if (excludedFolder) continue; if (folder.path === templateFolderPath) continue; const folderNote = getFolderNote(this.plugin, folder.path); @@ -83,7 +100,8 @@ export default class ConfirmationModal extends Modal { }); }); } - onClose() { + + onClose(): void { const { contentEl } = this; contentEl.empty(); } diff --git a/src/settings/modals/RenameFns.ts b/src/settings/modals/RenameFns.ts index 56cd925..b2183c6 100644 --- a/src/settings/modals/RenameFns.ts +++ b/src/settings/modals/RenameFns.ts @@ -3,7 +3,13 @@ import type FolderNotesPlugin from 'src/main'; import { Setting } from 'obsidian'; export default class RenameFolderNotesModal extends BackupWarningModal { - constructor(plugin: FolderNotesPlugin, title: string, description: string, callback: (...args: any[]) => void, args: any[] = []) { + constructor( + plugin: FolderNotesPlugin, + title: string, + description: string, + callback: (...args: unknown[]) => void, + args: unknown[] = [], + ) { super(plugin, title, description, callback, args); } @@ -11,6 +17,7 @@ export default class RenameFolderNotesModal extends BackupWarningModal { const { contentEl } = this; new Setting(contentEl) .setName('Old Folder Note Name') + // eslint-disable-next-line max-len .setDesc('Every folder note that matches this name will be renamed to the new folder note name.') .addText((text) => text .setPlaceholder('Enter the old folder note name') @@ -22,6 +29,7 @@ export default class RenameFolderNotesModal extends BackupWarningModal { new Setting(contentEl) .setName('New Folder Note Name') + // eslint-disable-next-line max-len .setDesc('Every folder note that matches the old folder note name will be renamed to this name.') .addText((text) => text .setPlaceholder('Enter the new folder note name') diff --git a/src/suggesters/FolderSuggester.ts b/src/suggesters/FolderSuggester.ts index e3d07b6..ce4d34e 100644 --- a/src/suggesters/FolderSuggester.ts +++ b/src/suggesters/FolderSuggester.ts @@ -1,7 +1,6 @@ // Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater -import type { TAbstractFile } from 'obsidian'; -import { TFolder } from 'obsidian'; +import { TFolder, type TAbstractFile } from 'obsidian'; import { TextInputSuggest } from './Suggest'; import type FolderNotesPlugin from '../main'; export enum FileSuggestMode { @@ -36,13 +35,18 @@ export class FolderSuggest extends TextInputSuggest { if (this.folder) { files = this.folder.children; } else { - files = this.plugin.app.vault.getAllLoadedFiles().slice(0,100); + const MAX_FILE_SUGGESTIONS = 100; + files = this.plugin.app.vault.getAllLoadedFiles().slice(0, MAX_FILE_SUGGESTIONS); } files.forEach((folder: TAbstractFile) => { if ( folder instanceof TFolder && - folder.path.toLowerCase().contains(lower_input_str) && - (!this.plugin.settings.excludeFolders.find((f) => f.path === folder.path) || this.whitelistSuggester) + folder.path.toLowerCase().contains(lower_input_str) && + ( + !this.plugin.settings.excludeFolders.find( + (f) => f.path === folder.path, + ) || this.whitelistSuggester + ) ) { folders.push(folder); } diff --git a/src/suggesters/Suggest.ts b/src/suggesters/Suggest.ts index 4a3fb02..9f2822b 100644 --- a/src/suggesters/Suggest.ts +++ b/src/suggesters/Suggest.ts @@ -1,10 +1,7 @@ // Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater - -import type { ISuggestOwner } from 'obsidian'; -import { Scope } from 'obsidian'; -import type { Instance as PopperInstance } from '@popperjs/core'; -import { createPopper } from '@popperjs/core'; +import { Scope, type ISuggestOwner } from 'obsidian'; +import { createPopper, type Instance as PopperInstance } from '@popperjs/core'; import type FolderNotesPlugin from 'src/main'; const wrapAround = (value: number, size: number): number => { @@ -73,7 +70,7 @@ class Suggest { this.setSelectedItem(item, false); } - setSuggestions(values: T[]) { + setSuggestions(values: T[]): void { this.containerEl.empty(); const suggestionEls: HTMLDivElement[] = []; @@ -88,14 +85,14 @@ class Suggest { this.setSelectedItem(0, false); } - useSelectedItem(event: MouseEvent | KeyboardEvent) { + useSelectedItem(event: MouseEvent | KeyboardEvent): void { const currentValue = this.values[this.selectedItem]; if (currentValue) { this.owner.selectSuggestion(currentValue, event); } } - setSelectedItem(selectedIndex: number, scrollIntoView: boolean) { + setSelectedItem(selectedIndex: number, scrollIntoView: boolean): void { const normalizedIndex = wrapAround( selectedIndex, this.suggestions.length, @@ -157,7 +154,7 @@ export abstract class TextInputSuggest implements ISuggestOwner { if (suggestions.length > 0) { this.suggest.setSuggestions(suggestions); - // @ts-ignore + // @ts-expect-error App may not exist this.open(app.dom.appContainerEl, this.inputEl); } else { this.close(); @@ -175,7 +172,7 @@ export abstract class TextInputSuggest implements ISuggestOwner { { name: 'sameWidth', enabled: true, - fn: ({ state, instance }) => { + fn: ({ state, instance }): void => { // Note: positioning needs to be calculated twice - // first pass - positioning it according to the width of the popper // second pass - position it with the width bound to the reference element diff --git a/src/suggesters/TemplateSuggester.ts b/src/suggesters/TemplateSuggester.ts index 0e26932..60b6f83 100644 --- a/src/suggesters/TemplateSuggester.ts +++ b/src/suggesters/TemplateSuggester.ts @@ -1,5 +1,4 @@ -import type { TAbstractFile } from 'obsidian'; -import { TFile, TFolder, Vault, AbstractInputSuggest } from 'obsidian'; +import { TFile, TFolder, Vault, AbstractInputSuggest, type TAbstractFile } from 'obsidian'; import type FolderNotesPlugin from '../main'; import { getTemplatePlugins } from 'src/template'; export enum FileSuggestMode { @@ -39,12 +38,21 @@ export class TemplateSuggest extends AbstractInputSuggest { let folder: TFolder | TAbstractFile | null = null; if (templaterPlugin) { folder = this.plugin.app.vault.getAbstractFileByPath( - templaterPlugin.plugin?.settings?.templates_folder as string, + (templaterPlugin as unknown as { + plugin?: { settings?: { templates_folder?: string } } + }).plugin?.settings?.templates_folder as string, ); if (!(folder instanceof TFolder)) { - return [{ path: '', name: 'You need to set the Templates folder in the Templater settings first.' } as TFile]; + return [ + { + path: '', + name: + // eslint-disable-next-line max-len + 'You need to set the Templates folder in the Templater settings first.', + } as TFile, + ]; } - } else { + } else if (templateFolder) { folder = this.plugin.app.vault.getAbstractFileByPath(templateFolder) as TFolder; } diff --git a/src/template.ts b/src/template.ts index 09753cb..48240fd 100644 --- a/src/template.ts +++ b/src/template.ts @@ -1,12 +1,41 @@ import { TFile, WorkspaceLeaf, type App } from 'obsidian'; import type FolderNotesPlugin from './main'; +interface TemplatesPlugin { + enabled: boolean; + instance: { + options: { + folder: string; + }; + insertTemplate: (templateFile: TFile) => Promise; + }; +} + +interface TemplaterPlugin { + settings?: { + empty_file_template?: string; + template_folder?: string; + }; + templater?: { + write_template_to_file: (templateFile: TFile, targetFile: TFile) => Promise; + }; +} + +interface TemplatePluginReturn { + templatesPlugin: TemplatesPlugin | null; + templatesEnabled: boolean; + templaterPlugin: TemplaterPlugin['templater'] | null; + templaterEnabled: boolean; + templaterEmptyFileTemplate?: string; + templateFolder?: string; +} + export async function applyTemplate( plugin: FolderNotesPlugin, file: TFile, leaf?: WorkspaceLeaf | null, templatePath?: string, -) { +): Promise { const fileContent = await plugin.app.vault.read(file).catch((err) => { console.error(`Error reading file ${file.path}:`, err); }); @@ -25,14 +54,15 @@ export async function applyTemplate( templaterPlugin, } = getTemplatePlugins(plugin.app); const templateContent = await plugin.app.vault.read(templateFile); + // eslint-disable-next-line max-len if (templateContent.includes('==âš  Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. âš ==')) { return; } // Prioritize Templater if both plugins are enabled - if (templaterEnabled) { + if (templaterEnabled && templaterPlugin) { return await templaterPlugin.write_template_to_file(templateFile, file); - } else if (templatesEnabled) { + } else if (templatesEnabled && templatesPlugin) { if (leaf instanceof WorkspaceLeaf) { await leaf.openFile(file); } @@ -47,23 +77,37 @@ export async function applyTemplate( } } -export function getTemplatePlugins(app: App) { - const templatesPlugin = (app as any).internalPlugins.plugins.templates; - const templatesEnabled = templatesPlugin.enabled; - const templaterPlugin = (app as any).plugins.plugins['templater-obsidian']; - const templaterEnabled = (app as any).plugins.enabledPlugins.has('templater-obsidian'); +export function getTemplatePlugins(app: App): TemplatePluginReturn { + const appAsUnknown = app as unknown as { + internalPlugins: { + plugins: { + templates: TemplatesPlugin; + }; + }; + plugins: { + plugins: { + 'templater-obsidian': TemplaterPlugin; + }; + enabledPlugins: Set; + }; + }; + + const templatesPlugin = appAsUnknown.internalPlugins.plugins.templates; + const templatesEnabled = templatesPlugin?.enabled ?? false; + const templaterPlugin = appAsUnknown.plugins.plugins['templater-obsidian']; + const templaterEnabled = appAsUnknown.plugins.enabledPlugins.has('templater-obsidian'); const templaterEmptyFileTemplate = templaterPlugin && templaterPlugin.settings?.empty_file_template; const templateFolder = templatesEnabled ? templatesPlugin.instance.options.folder - : templaterPlugin?.settings.template_folder; + : templaterPlugin?.settings?.template_folder; return { - templatesPlugin, + templatesPlugin: templatesPlugin || null, templatesEnabled, - templaterPlugin: templaterPlugin?.templater, + templaterPlugin: templaterPlugin?.templater || null, templaterEnabled, templaterEmptyFileTemplate, templateFolder, From e88ccfeb06a9e2a0a0c340e918287e47e90eaf10 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:46:07 +0200 Subject: [PATCH 39/60] Update obsidian-folder-overview --- src/obsidian-folder-overview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 5323f27..37008ed 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 5323f27922aff0165dca78770f24a7d8a99de7c0 +Subproject commit 37008ed555811b06e5e9c3547079368e0cce8113 From 3e23140bfb76c435b0d090ffb8486ef23239f4b0 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Wed, 13 Aug 2025 15:36:40 +0200 Subject: [PATCH 40/60] Fix # 275 --- src/main.ts | 30 ++++++++++++++++++---------- src/obsidian-folder-overview | 2 +- src/settings/FileExplorerSettings.ts | 9 ++++++++- styles.css | 6 ++++-- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/main.ts b/src/main.ts index ee94694..ad5bf0b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -84,6 +84,9 @@ export default class FolderNotesPlugin extends Plugin { if (this.settings.hideCollapsingIcon) { document.body.classList.add('fn-hide-collapse-icon'); } + if (this.settings.ignoreAttachmentFolder) { + document.body.classList.add('fn-ignore-attachment-folder'); + } if (!this.settings.highlightFolder) { document.body.classList.add('disable-folder-highlight'); } @@ -433,7 +436,7 @@ export default class FolderNotesPlugin extends Plugin { updateViewDropdown: typeof updateViewDropdown = updateViewDropdown; isEmptyFolderNoteFolder(folder: TFolder): boolean { - const attachmentFolderPath = this.app.vault.getConfig('attachmentFolderPath') as string; + let attachmentFolderPath = this.app.vault.getConfig('attachmentFolderPath') as string; const cleanAttachmentFolderPath = attachmentFolderPath?.replace('./', '') || ''; const attachmentsAreInRootFolder = attachmentFolderPath === './' || attachmentFolderPath === ''; @@ -441,22 +444,27 @@ export default class FolderNotesPlugin extends Plugin { if (folder.children.length === 0) { addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this); } + attachmentFolderPath = `${folder.path}/${cleanAttachmentFolderPath}`; if (folder.children.length === threshold) { + addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this); return true; } else if (folder.children.length > threshold) { if (attachmentsAreInRootFolder) { return false; - } else if (this.settings.ignoreAttachmentFolder - && this.app.vault.getAbstractFileByPath( - `${folder.path}/${cleanAttachmentFolderPath}`)) { - const folderPath = `${folder.path}/${cleanAttachmentFolderPath}`; - const attachmentFolder = this.app.vault.getAbstractFileByPath(folderPath); - if (attachmentFolder instanceof TFolder - && folder.children.length <= threshold + 1) { - if (!folder.collapsed) { - getFileExplorerElement(folder.path, this)?.click(); - } + } else if ( + this.app.vault.getAbstractFileByPath(attachmentFolderPath) instanceof TFolder + ) { + const attachmentFolder = this.app.vault.getAbstractFileByPath(attachmentFolderPath); + if ( + attachmentFolder instanceof TFolder && + folder.children.length <= threshold + 1 + ) { + addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this); + addCSSClassToFileExplorerEl( + folder.path, 'fn-has-attachment-folder', + false, this, + ); } return folder.children.length <= threshold + 1; } diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 37008ed..69f6e80 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 37008ed555811b06e5e9c3547079368e0cce8113 +Subproject commit 69f6e80b248e6317ca3d9587f27891ce86f55c79 diff --git a/src/settings/FileExplorerSettings.ts b/src/settings/FileExplorerSettings.ts index c69e620..ee9297e 100644 --- a/src/settings/FileExplorerSettings.ts +++ b/src/settings/FileExplorerSettings.ts @@ -135,12 +135,14 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise { settingsTab.plugin.settings.hideCollapsingIcon = value; - await settingsTab.plugin.saveSettings(); if (value) { + console.log(document.body.classList.contains('fn-hide-collapse-icon')); document.body.classList.add('fn-hide-collapse-icon'); } else { + console.log(document.body.classList.contains('fn-hide-collapse-icon')); document.body.classList.remove('fn-hide-collapse-icon'); } + await settingsTab.plugin.saveSettings(); settingsTab.display(); }), ); @@ -170,6 +172,11 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise { + if (value) { + document.body.classList.add('fn-ignore-attachment-folder'); + } else { + document.body.classList.remove('fn-ignore-attachment-folder'); + } settingsTab.plugin.settings.ignoreAttachmentFolder = value; await settingsTab.plugin.saveSettings(); }), diff --git a/styles.css b/styles.css index 1bfa9ea..84eb68f 100644 --- a/styles.css +++ b/styles.css @@ -223,8 +223,10 @@ li:has(.fv-link-list-item), .fn-has-no-files .collapse-icon, .fn-hide-collapse-icon .has-folder-note.only-has-folder-note .tree-item-icon, -.fn-hide-empty-collapse-icon .fn-empty-folder .tree-item-icon, -.only-has-folder-note:not(.is-collapsed):not(.show-folder-note-in-explorer)>.nav-folder-children { +body.fn-ignore-attachment-folder.fn-hide-collapse-icon .only-has-folder-note .fn-empty-folder.fn-has-attachment-folder .tree-item-icon, +body.fn-hide-collapse-icon .only-has-folder-note .fn-empty-folder:not(.fn-has-attachment-folder) .tree-item-icon, +body.fn-hide-empty-collapse-icon :not(.only-has-folder-note) > .fn-empty-folder:not(.fn-has-attachment-folder) .tree-item-icon, +body.fn-hide-collapse-icon.only-has-folder-note:not(.is-collapsed):not(.show-folder-note-in-explorer)>.nav-folder-children { display: none; } From f670410cf26cc5707700790597d6cf2b7ab9c79e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 13 Aug 2025 13:37:12 +0000 Subject: [PATCH 41/60] Update manifest-beta.json to version 1.8.17-3-beta in main branch --- manifest-beta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest-beta.json b/manifest-beta.json index c97ba08..56c64be 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes beta", - "version": "1.8.17-2-beta", + "version": "1.8.17-3-beta", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From b24b25ef342b272058aeb9e3d51434ceea6119df Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Wed, 13 Aug 2025 15:40:30 +0200 Subject: [PATCH 42/60] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f459e2a..7bf0f65 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "author": "Lost Paul", "license": "GPL-3.0-or-later", "devDependencies": { - "@eslint/js": "^9.32.0", + "@eslint/js": "^8.57.1", "@types/node": "^16.11.6", "@typescript-eslint/eslint-plugin": "5.29.0", "@typescript-eslint/parser": "5.29.0", From 8e2c3376a67c5a09a4e3f26005ca5c4be8ec8d65 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 13 Aug 2025 13:41:20 +0000 Subject: [PATCH 43/60] Update manifest-beta.json to version 1.8.17-4-beta in main branch --- manifest-beta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest-beta.json b/manifest-beta.json index 56c64be..fc5c57a 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes beta", - "version": "1.8.17-3-beta", + "version": "1.8.17-4-beta", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From 765c048fbd9cdfcc2fcbf93f94d7be7c7eec5c67 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:14:58 +0200 Subject: [PATCH 44/60] Update packages --- package-lock.json | 1430 +++++++++------------------------------------ package.json | 4 +- 2 files changed, 280 insertions(+), 1154 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7886651..552d51a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,10 @@ "@popperjs/core": "^2.11.6" }, "devDependencies": { - "@eslint/js": "^9.32.0", + "@eslint/js": "^8.57.1", "@types/node": "^16.11.6", - "@typescript-eslint/eslint-plugin": "5.29.0", - "@typescript-eslint/parser": "5.29.0", + "@typescript-eslint/eslint-plugin": "^8.38.0", + "@typescript-eslint/parser": "^8.38.0", "builtin-modules": "3.3.0", "esbuild": "0.14.47", "eslint": "^9.32.0", @@ -198,16 +198,13 @@ } }, "node_modules/@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@eslint/object-schema": { @@ -447,124 +444,69 @@ "@types/estree": "*" } }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz", - "integrity": "sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/type-utils": "5.29.0", - "@typescript-eslint/utils": "5.29.0", - "debug": "^4.3.4", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", - "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", + "integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==", "dev": true, "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/typescript-estree": "5.29.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/type-utils": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" + "@typescript-eslint/parser": "^8.39.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "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": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.29.0.tgz", - "integrity": "sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", + "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/typescript-estree": "5.29.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { @@ -589,31 +531,18 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", - "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/scope-manager": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", - "integrity": "sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/visitor-keys": "5.29.0" + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -638,131 +567,174 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz", - "integrity": "sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz", + "integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "5.29.0", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^2.1.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", - "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", "dev": true, "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/typescript-estree": "5.29.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.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": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" }, "engines": { - "node": ">=8.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", - "integrity": "sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==", + "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": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=16 || 14 >=14.17" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz", - "integrity": "sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/visitor-keys": "5.29.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", - "integrity": "sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.29.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -831,15 +803,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -847,14 +810,6 @@ "dev": true, "license": "MIT" }, - "node_modules/boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1072,64 +1027,6 @@ "node": ">=10" } }, - "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, - "optional": true, - "peer": true, - "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, - "optional": true, - "peer": true, - "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/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/electron": { "version": "31.4.0", "resolved": "https://registry.npmjs.org/electron/-/electron-31.4.0.tgz", @@ -1179,39 +1076,6 @@ "node": ">=6" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "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, - "optional": true, - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/esbuild": { "version": "0.14.47", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.47.tgz", @@ -1678,58 +1542,30 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "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": "MIT", - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/eslint/node_modules/@eslint/js": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/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", + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" } }, "node_modules/espree": { @@ -1750,19 +1586,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/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/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -1984,44 +1807,6 @@ "node": ">=6 <7 || >=8" } }, - "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, - "optional": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -2050,25 +1835,6 @@ "node": ">=10.13.0" } }, - "node_modules/global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, "node_modules/globals": { "version": "16.3.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", @@ -2082,58 +1848,6 @@ "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, - "optional": true, - "peer": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/got": { "version": "11.8.6", "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", @@ -2183,62 +1897,6 @@ "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, - "optional": true, - "peer": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "optional": true, - "peer": true, - "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, - "optional": true, - "peer": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -2375,14 +2033,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -2447,20 +2097,6 @@ "node": ">=8" } }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2541,17 +2177,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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, - "optional": true, - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/obsidian": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.6.6.tgz", @@ -2677,15 +2302,6 @@ "node": ">=8" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -2780,18 +2396,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", @@ -2832,25 +2436,6 @@ "node": ">=0.10.0" } }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2887,45 +2472,6 @@ "node": ">=10" } }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2949,23 +2495,6 @@ "node": ">=8" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3043,27 +2572,6 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3308,19 +2816,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/typescript-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/typescript-eslint/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -3559,9 +3054,9 @@ } }, "@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true }, "@eslint/object-schema": { @@ -3740,75 +3235,41 @@ "@types/estree": "*" } }, - "@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@types/node": "*" - } - }, "@typescript-eslint/eslint-plugin": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz", - "integrity": "sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", + "integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/type-utils": "5.29.0", - "@typescript-eslint/utils": "5.29.0", - "debug": "^4.3.4", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/type-utils": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" }, "dependencies": { - "@typescript-eslint/utils": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", - "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/typescript-estree": "5.29.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true } } }, "@typescript-eslint/parser": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.29.0.tgz", - "integrity": "sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", + "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/typescript-estree": "5.29.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", "debug": "^4.3.4" } }, @@ -3821,24 +3282,16 @@ "@typescript-eslint/tsconfig-utils": "^8.38.0", "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" - }, - "dependencies": { - "@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", - "dev": true - } } }, "@typescript-eslint/scope-manager": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", - "integrity": "sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/visitor-keys": "5.29.0" + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" } }, "@typescript-eslint/tsconfig-utils": { @@ -3849,77 +3302,100 @@ "requires": {} }, "@typescript-eslint/type-utils": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz", - "integrity": "sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz", + "integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.29.0", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.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" }, "dependencies": { - "@typescript-eslint/utils": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", - "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", "dev": true, "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.29.0", - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/typescript-estree": "5.29.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" } }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "requires": {} + }, + "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, "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "balanced-match": "^1.0.0" } }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } } } }, - "@typescript-eslint/types": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", - "integrity": "sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz", - "integrity": "sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==", + "@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.29.0", - "@typescript-eslint/visitor-keys": "5.29.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" } }, "@typescript-eslint/visitor-keys": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", - "integrity": "sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==", + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", "dev": true, "requires": { - "@typescript-eslint/types": "5.29.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" } }, "acorn": { @@ -3962,26 +3438,12 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "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 }, - "boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "dev": true, - "optional": true, - "peer": true - }, "brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -4136,49 +3598,6 @@ "dev": true, "peer": true }, - "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, - "optional": true, - "peer": true, - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - } - }, - "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, - "optional": true, - "peer": true, - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "optional": true, - "peer": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, "electron": { "version": "31.4.0", "resolved": "https://registry.npmjs.org/electron/-/electron-31.4.0.tgz", @@ -4220,33 +3639,6 @@ "dev": true, "peer": true }, - "es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "get-intrinsic": "^1.2.4" - } - }, - "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, - "optional": true, - "peer": true - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "optional": true, - "peer": true - }, "esbuild": { "version": "0.14.47", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.47.tgz", @@ -4464,10 +3856,10 @@ "optionator": "^0.9.3" }, "dependencies": { - "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==", + "@eslint/js": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true } } @@ -4482,27 +3874,10 @@ "estraverse": "^5.2.0" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "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 }, "espree": { @@ -4514,14 +3889,6 @@ "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" - }, - "dependencies": { - "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 - } } }, "esquery": { @@ -4693,35 +4060,6 @@ "universalify": "^0.1.0" } }, - "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, - "optional": true, - "peer": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, "get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -4741,65 +4079,12 @@ "is-glob": "^4.0.3" } }, - "global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - } - }, "globals": { "version": "16.3.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true }, - "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, - "optional": true, - "peer": true, - "requires": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, "got": { "version": "11.8.6", "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", @@ -4839,44 +4124,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "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, - "optional": true, - "peer": true, - "requires": { - "es-define-property": "^1.0.0" - } - }, - "has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "optional": true, - "peer": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "optional": true, - "peer": true - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "function-bind": "^1.1.2" - } - }, "http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -4977,14 +4224,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "optional": true, - "peer": true - }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -5036,17 +4275,6 @@ "dev": true, "peer": true }, - "matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "escape-string-regexp": "^4.0.0" - } - }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5104,14 +4332,6 @@ "dev": true, "peer": true }, - "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, - "optional": true, - "peer": true - }, "obsidian": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.6.6.tgz", @@ -5199,12 +4419,6 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -5261,12 +4475,6 @@ "dev": true, "peer": true }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", @@ -5296,22 +4504,6 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - } - }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5327,35 +4519,6 @@ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "optional": true, - "peer": true - }, - "serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "type-fest": "^0.13.1" - }, - "dependencies": { - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "optional": true, - "peer": true - } - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5371,20 +4534,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "optional": true, - "peer": true - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5439,23 +4588,6 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5591,12 +4723,6 @@ "balanced-match": "^1.0.0" } }, - "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 - }, "ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", diff --git a/package.json b/package.json index 7bf0f65..22564a3 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "devDependencies": { "@eslint/js": "^8.57.1", "@types/node": "^16.11.6", - "@typescript-eslint/eslint-plugin": "5.29.0", - "@typescript-eslint/parser": "5.29.0", + "@typescript-eslint/eslint-plugin": "^8.38.0", + "@typescript-eslint/parser": "^8.38.0", "builtin-modules": "3.3.0", "esbuild": "0.14.47", "eslint": "^9.32.0", From e826fe674077acaa4bdf4a73b8ddf3246f5d1a2e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 Aug 2025 08:15:24 +0000 Subject: [PATCH 45/60] Update manifest-beta.json to version 1.8.17-5-beta in main branch --- manifest-beta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest-beta.json b/manifest-beta.json index fc5c57a..c97263b 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes beta", - "version": "1.8.17-4-beta", + "version": "1.8.17-5-beta", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From 03b6de9db74137463fef71aadbdd1dc31767b965 Mon Sep 17 00:00:00 2001 From: Nightwish2710 Date: Mon, 18 Aug 2025 14:21:10 +0700 Subject: [PATCH 46/60] feat: update command menu for mobile rendering --- src/Commands.ts | 330 ++++++++++++++++++++++++------------------------ 1 file changed, 168 insertions(+), 162 deletions(-) diff --git a/src/Commands.ts b/src/Commands.ts index a27b541..1c0843f 100644 --- a/src/Commands.ts +++ b/src/Commands.ts @@ -298,208 +298,214 @@ export class Commands { } } - // eslint-disable-next-line complexity - menu.addItem(async (menuItem) => { - if ( - Platform.isDesktop && - !Platform.isTablet && - this.plugin.settings.useSubmenus - ) { - menuItem - .setTitle('Folder Note Commands') - .setIcon('folder-edit'); - } - let subMenu: Menu; - if ( - !Platform.isDesktopApp || - !Platform.isDesktop || - Platform.isTablet || - !this.plugin.settings.useSubmenus - ) { - subMenu = menu; - menuItem.setDisabled(true); - } else { - subMenu = menuItem.setSubmenu() as Menu; - } - if (file instanceof TFile) { - subMenu.addItem((subItem) => { - subItem.setTitle('Create folder note') - .setIcon('edit') - .onClick(async () => { - if (!folder) return; - let newPath = folder.path + '/' + file.basename; - if (folder.path === '' || folder.path === '/') { - newPath = file.basename; - } - if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { - return new Notice('Folder already exists'); - } - const automaticallyCreateFolderNote = - this.plugin.settings.autoCreate; - this.plugin.settings.autoCreate = false; - this.plugin.saveSettings(); - await this.plugin.app.vault.createFolder(newPath); - const newFolder = this.plugin.app.vault - .getAbstractFileByPath(newPath); - if (!(newFolder instanceof TFolder)) return; - await createFolderNote( - this.plugin, - newFolder.path, - true, - '.' + file.extension, - false, - file, - ); - this.plugin.settings.autoCreate = automaticallyCreateFolderNote; - this.plugin.saveSettings(); - }); + const addFolderNoteActions = (menu: Menu) => { + if (file instanceof TFile) { + menu.addItem((item) => { + item.setTitle('Create folder note'); + item.setIcon('edit'); + item.onClick(async () => { + if (!folder) return; + let newPath = folder.path + '/' + file.basename; + if (folder.path === '' || folder.path === '/') { + newPath = file.basename; + } + if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { + return new Notice('Folder already exists'); + } + const automaticallyCreateFolderNote = + this.plugin.settings.autoCreate; + this.plugin.settings.autoCreate = false; + this.plugin.saveSettings(); + await this.plugin.app.vault.createFolder(newPath); + const newFolder = this.plugin.app.vault + .getAbstractFileByPath(newPath); + if (!(newFolder instanceof TFolder)) return; + await createFolderNote( + this.plugin, + newFolder.path, + true, + '.' + file.extension, + false, + file, + ); + this.plugin.settings.autoCreate = automaticallyCreateFolderNote; + this.plugin.saveSettings(); + }); }); + if (getFolderPathFromString(file.path) === '') return; - if (!(folder instanceof TFolder)) return; - if (folder.path === '' || folder.path === '/') return; - subMenu.addItem((item) => { - item.setTitle(`Turn into folder note for ${folder?.name}`) - .setIcon('edit') - .onClick(() => { - if (!folder || !(folder instanceof TFolder)) return; - const folderNote = getFolderNote(this.plugin, folder.path); - turnIntoFolderNote(this.plugin, file, folder, folderNote); - }); + + if (!(folder instanceof TFolder)) return; + + if (folder.path === '' || folder.path === '/') return; + + menu.addItem((item) => { + item.setTitle(`Turn into folder note for ${folder?.name}`); + item.setIcon('edit'); + item.onClick(() => { + if (!folder || !(folder instanceof TFolder)) return; + const folderNote = getFolderNote(this.plugin, folder.path); + turnIntoFolderNote(this.plugin, file, folder, folderNote); + }); }); } + if (!(file instanceof TFolder)) return; + const excludedFolder = getExcludedFolder(this.plugin, file.path, false); const detachedExcludedFolder = getDetachedFolder(this.plugin, file.path); - if (excludedFolder && !excludedFolder.hideInSettings) { + + if (excludedFolder && !excludedFolder.hideInSettings) { // I'm not sure if I'm ever going to add this because of the possibility that a folder got more than one excluded - // subMenu.addItem((item) => { - // item.setTitle('Manage excluded folder') - // .setIcon('settings-2') - // .onClick(() => { - // if (excludedFolder instanceof ExcludedFolder) { - // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open(); - // } else if (excludedFolder instanceof ExcludePattern) { - // new PatternSettings(this.plugin.app, this.plugin, excludedFolder).open(); - // } - // }) - // }) - subMenu.addItem((item) => { - item.setTitle('Remove folder from excluded folders') - .setIcon('trash') - .onClick(() => { - this.plugin.settings.excludeFolders = - this.plugin.settings.excludeFolders.filter( - (excluded) => - (excluded.path !== file.path) || excluded.detached, - ); - this.plugin.saveSettings(true); - new Notice('Successfully removed folder from excluded folders'); - }); + // menu.addItem((item) => { + // item.setTitle('Manage excluded folder'); + // item.setIcon('settings-2'); + // item.onClick(() => { + // if (excludedFolder instanceof ExcludedFolder) { + // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open(); + // } else if (excludedFolder instanceof ExcludePattern) { + // new PatternSettings(this.plugin.app, this.plugin, excludedFolder).open(); + // } + // }); + // }); + + menu.addItem((item) => { + item.setTitle('Remove folder from excluded folders'); + item.setIcon('trash'); + item.onClick(() => { + this.plugin.settings.excludeFolders = + this.plugin.settings.excludeFolders.filter( + (excluded) => + (excluded.path !== file.path) || excluded.detached, + ); + this.plugin.saveSettings(true); + new Notice('Successfully removed folder from excluded folders'); + }); }); + return; } + if (detachedExcludedFolder) { - subMenu.addItem((item) => { - item.setTitle('Remove folder from detached folders') - .setIcon('trash') - .onClick(() => { - deleteExcludedFolder(this.plugin, detachedExcludedFolder); - }); + menu.addItem((item) => { + item.setTitle('Remove folder from detached folders'); + item.setIcon('trash'); + item.onClick(() => { + deleteExcludedFolder(this.plugin, detachedExcludedFolder); + }); }); } + if (detachedExcludedFolder) { return; } - subMenu.addItem((item) => { - item.setTitle('Exclude folder from folder notes') - .setIcon('x-circle') - .onClick(() => { - const newExcludedFolder = new ExcludedFolder( - file.path, - this.plugin.settings.excludeFolders.length, - undefined, - this.plugin, - ); - this.plugin.settings.excludeFolders.push(newExcludedFolder); - this.plugin.saveSettings(true); - new Notice('Successfully excluded folder from folder notes'); - }); + + menu.addItem((item) => { + item.setTitle('Exclude folder from folder notes'); + item.setIcon('x-circle'); + item.onClick(() => { + const newExcludedFolder = new ExcludedFolder( + file.path, + this.plugin.settings.excludeFolders.length, + undefined, + this.plugin, + ); + this.plugin.settings.excludeFolders.push(newExcludedFolder); + this.plugin.saveSettings(true); + new Notice('Successfully excluded folder from folder notes'); + }); }); + if (!(file instanceof TFolder)) return; - const folderNote = getFolderNote(this.plugin, file.path); - if (folderNote instanceof TFile && !detachedExcludedFolder) { - subMenu.addItem((item) => { - item.setTitle('Delete folder note') - .setIcon('trash') - .onClick(() => { - deleteFolderNote(this.plugin, folderNote, true); - }); + + const folderNote = getFolderNote(this.plugin, file.path); + + if (folderNote instanceof TFile && !detachedExcludedFolder) { + menu.addItem((item) => { + item.setTitle('Delete folder note'); + item.setIcon('trash'); + item.onClick(() => { + deleteFolderNote(this.plugin, folderNote, true); + }); }); - subMenu.addItem((item) => { - item.setTitle('Open folder note') - .setIcon('chevron-right-square') - .onClick(() => { - openFolderNote(this.plugin, folderNote); - }); + menu.addItem((item) => { + item.setTitle('Open folder note'); + item.setIcon('chevron-right-square'); + item.onClick(() => { + openFolderNote(this.plugin, folderNote); + }); }); - subMenu.addItem((item) => { - item.setTitle('Detach folder note') - .setIcon('unlink') - .onClick(() => { - detachFolderNote(this.plugin, folderNote); - }); + menu.addItem((item) => { + item.setTitle('Detach folder note'); + item.setIcon('unlink'); + item.onClick(() => { + detachFolderNote(this.plugin, folderNote); + }); }); - subMenu.addItem((item) => { - item.setTitle('Copy Obsidian URL') - .setIcon('link') - .onClick(() => { - this.app.copyObsidianUrl(folderNote); - }); + menu.addItem((item) => { + item.setTitle('Copy Obsidian URL'); + item.setIcon('link'); + item.onClick(() => { + this.app.copyObsidianUrl(folderNote); + }); }); if (this.plugin.settings.hideFolderNote) { if (excludedFolder?.showFolderNote) { - subMenu.addItem((item) => { - item.setTitle('Hide folder note in explorer') - .setIcon('eye-off') - .onClick(() => { - hideFolderNoteInFileExplorer(file.path, this.plugin); - }); + menu.addItem((item) => { + item.setTitle('Hide folder note in explorer'); + item.setIcon('eye-off'); + item.onClick(() => { + hideFolderNoteInFileExplorer(file.path, this.plugin); + }); }); } else { - subMenu.addItem((item) => { - item.setTitle('Show folder note in explorer') - .setIcon('eye') - .onClick(() => { - showFolderNoteInFileExplorer(file.path, this.plugin); - }); + menu.addItem((item) => { + item.setTitle('Show folder note in explorer'); + item.setIcon('eye'); + item.onClick(() => { + showFolderNoteInFileExplorer(file.path, this.plugin); + }); }); } } - } else { - subMenu.addItem((item) => { - item.setTitle('Create markdown folder note') - .setIcon('edit') - .onClick(() => { - createFolderNote(this.plugin, file.path, true, '.md'); - }); + menu.addItem((item) => { + item.setTitle('Create markdown folder note'); + item.setIcon('edit'); + item.onClick(() => { + createFolderNote(this.plugin, file.path, true, '.md'); + }); }); this.plugin.settings.supportedFileTypes.forEach((fileType) => { if (fileType === 'md') return; - subMenu.addItem((item) => { - item.setTitle(`Create ${fileType} folder note`) - .setIcon('edit') - .onClick(() => { - // eslint-disable-next-line max-len - createFolderNote(this.plugin, file.path, true, '.' + fileType); - }); + menu.addItem((item) => { + item.setTitle(`Create ${fileType} folder note`); + item.setIcon('edit'); + item.onClick(() => { + // eslint-disable-next-line max-len + createFolderNote(this.plugin, file.path, true, '.' + fileType); + }); }); }); } - }); + }; + + if ( + Platform.isDesktop && + !Platform.isTablet && + this.plugin.settings.useSubmenus + ) { + menu.addItem(async (item) => { + item.setTitle('Folder Note Commands').setIcon('folder-edit'); + let subMenu: Menu = item.setSubmenu() as Menu; + addFolderNoteActions(subMenu); + }) + } else { + addFolderNoteActions(menu); + } })); } From 203d9f4119df3b66790004f89d025d90f2501cbc Mon Sep 17 00:00:00 2001 From: Nightwish2710 Date: Mon, 18 Aug 2025 14:28:23 +0700 Subject: [PATCH 47/60] feat: update command menu for mobile rendering --- src/Commands.ts | 188 ++++++++++++++++++++++++------------------------ 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/src/Commands.ts b/src/Commands.ts index 1c0843f..b8f2a97 100644 --- a/src/Commands.ts +++ b/src/Commands.ts @@ -298,55 +298,55 @@ export class Commands { } } - const addFolderNoteActions = (menu: Menu) => { - if (file instanceof TFile) { + const addFolderNoteActions = (menu: Menu) => { + if (file instanceof TFile) { menu.addItem((item) => { item.setTitle('Create folder note'); item.setIcon('edit'); item.onClick(async () => { - if (!folder) return; - let newPath = folder.path + '/' + file.basename; - if (folder.path === '' || folder.path === '/') { - newPath = file.basename; - } - if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { - return new Notice('Folder already exists'); - } - const automaticallyCreateFolderNote = - this.plugin.settings.autoCreate; - this.plugin.settings.autoCreate = false; - this.plugin.saveSettings(); - await this.plugin.app.vault.createFolder(newPath); - const newFolder = this.plugin.app.vault - .getAbstractFileByPath(newPath); - if (!(newFolder instanceof TFolder)) return; - await createFolderNote( - this.plugin, - newFolder.path, - true, - '.' + file.extension, - false, - file, - ); - this.plugin.settings.autoCreate = automaticallyCreateFolderNote; - this.plugin.saveSettings(); - }); + if (!folder) return; + let newPath = folder.path + '/' + file.basename; + if (folder.path === '' || folder.path === '/') { + newPath = file.basename; + } + if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { + return new Notice('Folder already exists'); + } + const automaticallyCreateFolderNote = + this.plugin.settings.autoCreate; + this.plugin.settings.autoCreate = false; + this.plugin.saveSettings(); + await this.plugin.app.vault.createFolder(newPath); + const newFolder = this.plugin.app.vault + .getAbstractFileByPath(newPath); + if (!(newFolder instanceof TFolder)) return; + await createFolderNote( + this.plugin, + newFolder.path, + true, + '.' + file.extension, + false, + file, + ); + this.plugin.settings.autoCreate = automaticallyCreateFolderNote; + this.plugin.saveSettings(); + }); }); if (getFolderPathFromString(file.path) === '') return; - if (!(folder instanceof TFolder)) return; + if (!(folder instanceof TFolder)) return; - if (folder.path === '' || folder.path === '/') return; + if (folder.path === '' || folder.path === '/') return; - menu.addItem((item) => { + menu.addItem((item) => { item.setTitle(`Turn into folder note for ${folder?.name}`); item.setIcon('edit'); item.onClick(() => { - if (!folder || !(folder instanceof TFolder)) return; - const folderNote = getFolderNote(this.plugin, folder.path); - turnIntoFolderNote(this.plugin, file, folder, folderNote); - }); + if (!folder || !(folder instanceof TFolder)) return; + const folderNote = getFolderNote(this.plugin, folder.path); + turnIntoFolderNote(this.plugin, file, folder, folderNote); + }); }); } @@ -355,32 +355,32 @@ export class Commands { const excludedFolder = getExcludedFolder(this.plugin, file.path, false); const detachedExcludedFolder = getDetachedFolder(this.plugin, file.path); - if (excludedFolder && !excludedFolder.hideInSettings) { + if (excludedFolder && !excludedFolder.hideInSettings) { // I'm not sure if I'm ever going to add this because of the possibility that a folder got more than one excluded // menu.addItem((item) => { // item.setTitle('Manage excluded folder'); // item.setIcon('settings-2'); // item.onClick(() => { - // if (excludedFolder instanceof ExcludedFolder) { - // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open(); - // } else if (excludedFolder instanceof ExcludePattern) { - // new PatternSettings(this.plugin.app, this.plugin, excludedFolder).open(); - // } - // }); + // if (excludedFolder instanceof ExcludedFolder) { + // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open(); + // } else if (excludedFolder instanceof ExcludePattern) { + // new PatternSettings(this.plugin.app, this.plugin, excludedFolder).open(); + // } + // }); // }); menu.addItem((item) => { item.setTitle('Remove folder from excluded folders'); item.setIcon('trash'); item.onClick(() => { - this.plugin.settings.excludeFolders = - this.plugin.settings.excludeFolders.filter( - (excluded) => - (excluded.path !== file.path) || excluded.detached, - ); - this.plugin.saveSettings(true); - new Notice('Successfully removed folder from excluded folders'); - }); + this.plugin.settings.excludeFolders = + this.plugin.settings.excludeFolders.filter( + (excluded) => + (excluded.path !== file.path) || excluded.detached, + ); + this.plugin.saveSettings(true); + new Notice('Successfully removed folder from excluded folders'); + }); }); return; @@ -391,64 +391,64 @@ export class Commands { item.setTitle('Remove folder from detached folders'); item.setIcon('trash'); item.onClick(() => { - deleteExcludedFolder(this.plugin, detachedExcludedFolder); - }); + deleteExcludedFolder(this.plugin, detachedExcludedFolder); + }); }); } if (detachedExcludedFolder) { return; } - menu.addItem((item) => { + menu.addItem((item) => { item.setTitle('Exclude folder from folder notes'); item.setIcon('x-circle'); item.onClick(() => { - const newExcludedFolder = new ExcludedFolder( - file.path, - this.plugin.settings.excludeFolders.length, - undefined, - this.plugin, - ); - this.plugin.settings.excludeFolders.push(newExcludedFolder); - this.plugin.saveSettings(true); - new Notice('Successfully excluded folder from folder notes'); - }); + const newExcludedFolder = new ExcludedFolder( + file.path, + this.plugin.settings.excludeFolders.length, + undefined, + this.plugin, + ); + this.plugin.settings.excludeFolders.push(newExcludedFolder); + this.plugin.saveSettings(true); + new Notice('Successfully excluded folder from folder notes'); + }); }); if (!(file instanceof TFolder)) return; - const folderNote = getFolderNote(this.plugin, file.path); + const folderNote = getFolderNote(this.plugin, file.path); - if (folderNote instanceof TFile && !detachedExcludedFolder) { + if (folderNote instanceof TFile && !detachedExcludedFolder) { menu.addItem((item) => { item.setTitle('Delete folder note'); item.setIcon('trash'); item.onClick(() => { - deleteFolderNote(this.plugin, folderNote, true); - }); + deleteFolderNote(this.plugin, folderNote, true); + }); }); menu.addItem((item) => { item.setTitle('Open folder note'); item.setIcon('chevron-right-square'); item.onClick(() => { - openFolderNote(this.plugin, folderNote); - }); + openFolderNote(this.plugin, folderNote); + }); }); menu.addItem((item) => { item.setTitle('Detach folder note'); item.setIcon('unlink'); item.onClick(() => { - detachFolderNote(this.plugin, folderNote); - }); + detachFolderNote(this.plugin, folderNote); + }); }); menu.addItem((item) => { item.setTitle('Copy Obsidian URL'); item.setIcon('link'); item.onClick(() => { - this.app.copyObsidianUrl(folderNote); - }); + this.app.copyObsidianUrl(folderNote); + }); }); if (this.plugin.settings.hideFolderNote) { @@ -457,16 +457,16 @@ export class Commands { item.setTitle('Hide folder note in explorer'); item.setIcon('eye-off'); item.onClick(() => { - hideFolderNoteInFileExplorer(file.path, this.plugin); - }); + hideFolderNoteInFileExplorer(file.path, this.plugin); + }); }); } else { menu.addItem((item) => { item.setTitle('Show folder note in explorer'); item.setIcon('eye'); item.onClick(() => { - showFolderNoteInFileExplorer(file.path, this.plugin); - }); + showFolderNoteInFileExplorer(file.path, this.plugin); + }); }); } } @@ -475,8 +475,8 @@ export class Commands { item.setTitle('Create markdown folder note'); item.setIcon('edit'); item.onClick(() => { - createFolderNote(this.plugin, file.path, true, '.md'); - }); + createFolderNote(this.plugin, file.path, true, '.md'); + }); }); this.plugin.settings.supportedFileTypes.forEach((fileType) => { @@ -485,27 +485,27 @@ export class Commands { item.setTitle(`Create ${fileType} folder note`); item.setIcon('edit'); item.onClick(() => { - // eslint-disable-next-line max-len - createFolderNote(this.plugin, file.path, true, '.' + fileType); - }); + // eslint-disable-next-line max-len + createFolderNote(this.plugin, file.path, true, '.' + fileType); + }); }); }); } - }; + }; - if ( + if ( Platform.isDesktop && !Platform.isTablet && this.plugin.settings.useSubmenus - ) { - menu.addItem(async (item) => { - item.setTitle('Folder Note Commands').setIcon('folder-edit'); - let subMenu: Menu = item.setSubmenu() as Menu; - addFolderNoteActions(subMenu); - }) - } else { - addFolderNoteActions(menu); - } + ) { + menu.addItem(async (item) => { + item.setTitle('Folder Note Commands').setIcon('folder-edit'); + let subMenu: Menu = item.setSubmenu() as Menu; + addFolderNoteActions(subMenu); + }) + } else { + addFolderNoteActions(menu); + } })); } From 08b8056df64dcd9672a462ccbc136b3fc75e19b9 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:53:19 +0200 Subject: [PATCH 48/60] Fix linter issues --- src/Commands.ts | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/Commands.ts b/src/Commands.ts index b8f2a97..c64830d 100644 --- a/src/Commands.ts +++ b/src/Commands.ts @@ -270,6 +270,7 @@ export class Commands { fileCommands(): void { this.plugin.registerEvent( + // eslint-disable-next-line complexity this.app.workspace.on('file-menu', (menu: Menu, file: TAbstractFile) => { let folder: TAbstractFile | TFolder | null = file.parent; if (file instanceof TFile) { @@ -298,9 +299,10 @@ export class Commands { } } - const addFolderNoteActions = (menu: Menu) => { + // eslint-disable-next-line complexity + const addFolderNoteActions = (folderMenu: Menu): void => { if (file instanceof TFile) { - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Create folder note'); item.setIcon('edit'); item.onClick(async () => { @@ -339,7 +341,7 @@ export class Commands { if (folder.path === '' || folder.path === '/') return; - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle(`Turn into folder note for ${folder?.name}`); item.setIcon('edit'); item.onClick(() => { @@ -369,7 +371,7 @@ export class Commands { // }); // }); - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Remove folder from excluded folders'); item.setIcon('trash'); item.onClick(() => { @@ -387,7 +389,7 @@ export class Commands { } if (detachedExcludedFolder) { - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Remove folder from detached folders'); item.setIcon('trash'); item.onClick(() => { @@ -398,7 +400,7 @@ export class Commands { if (detachedExcludedFolder) { return; } - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Exclude folder from folder notes'); item.setIcon('x-circle'); item.onClick(() => { @@ -419,7 +421,7 @@ export class Commands { const folderNote = getFolderNote(this.plugin, file.path); if (folderNote instanceof TFile && !detachedExcludedFolder) { - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Delete folder note'); item.setIcon('trash'); item.onClick(() => { @@ -427,7 +429,7 @@ export class Commands { }); }); - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Open folder note'); item.setIcon('chevron-right-square'); item.onClick(() => { @@ -435,7 +437,7 @@ export class Commands { }); }); - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Detach folder note'); item.setIcon('unlink'); item.onClick(() => { @@ -443,7 +445,7 @@ export class Commands { }); }); - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Copy Obsidian URL'); item.setIcon('link'); item.onClick(() => { @@ -453,7 +455,7 @@ export class Commands { if (this.plugin.settings.hideFolderNote) { if (excludedFolder?.showFolderNote) { - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Hide folder note in explorer'); item.setIcon('eye-off'); item.onClick(() => { @@ -461,7 +463,7 @@ export class Commands { }); }); } else { - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Show folder note in explorer'); item.setIcon('eye'); item.onClick(() => { @@ -471,7 +473,7 @@ export class Commands { } } } else { - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle('Create markdown folder note'); item.setIcon('edit'); item.onClick(() => { @@ -481,11 +483,10 @@ export class Commands { this.plugin.settings.supportedFileTypes.forEach((fileType) => { if (fileType === 'md') return; - menu.addItem((item) => { + folderMenu.addItem((item) => { item.setTitle(`Create ${fileType} folder note`); item.setIcon('edit'); item.onClick(() => { - // eslint-disable-next-line max-len createFolderNote(this.plugin, file.path, true, '.' + fileType); }); }); @@ -494,15 +495,15 @@ export class Commands { }; if ( - Platform.isDesktop && - !Platform.isTablet && - this.plugin.settings.useSubmenus + Platform.isDesktop && + !Platform.isTablet && + this.plugin.settings.useSubmenus ) { menu.addItem(async (item) => { item.setTitle('Folder Note Commands').setIcon('folder-edit'); let subMenu: Menu = item.setSubmenu() as Menu; addFolderNoteActions(subMenu); - }) + }); } else { addFolderNoteActions(menu); } From 997ffb583bcdaad8ca67944e35b8e44b28addc44 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Aug 2025 09:13:43 +0000 Subject: [PATCH 49/60] Update manifest.json to version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 83a74ca..ad3738c 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes", - "version": "1.8.16", + "version": "1.8.17", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul", From abfab4fc865f6c6509ab17751b142bbb5a138f8c Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:35:20 +0200 Subject: [PATCH 50/60] Remove console.log --- src/settings/FileExplorerSettings.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/settings/FileExplorerSettings.ts b/src/settings/FileExplorerSettings.ts index ee9297e..906dcfa 100644 --- a/src/settings/FileExplorerSettings.ts +++ b/src/settings/FileExplorerSettings.ts @@ -136,10 +136,8 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise { settingsTab.plugin.settings.hideCollapsingIcon = value; if (value) { - console.log(document.body.classList.contains('fn-hide-collapse-icon')); document.body.classList.add('fn-hide-collapse-icon'); } else { - console.log(document.body.classList.contains('fn-hide-collapse-icon')); document.body.classList.remove('fn-hide-collapse-icon'); } await settingsTab.plugin.saveSettings(); From e0613b7d27d3ad192b1b2ea83a701af5a3bea23f Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Wed, 20 Aug 2025 09:53:59 +0200 Subject: [PATCH 51/60] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8c49d3a..118a9a0 100644 --- a/README.md +++ b/README.md @@ -13,5 +13,7 @@ The plugin can be downloaded by clicking on https://obsidian.md/plugins?id=folde The easiest option is to install the [BRAT plugin](https://obsidian.md/plugins?id=obsidian42-brat) and then to follow the following guide: https://tfthacker.com/brat-quick-guide & use this link https://github.com/LostPaul/obsidian-folder-notes to install the beta version. +Join the Discord server to chat about the beta and to also get the beta user role. + ## Discord server [For regular updates on Folder Notes and my other plugins, join the Discord server to get notified and participate in discussions.](https://discord.gg/4UQEDfQmuH) From fdade5d96b53084d772bfa1185729c8319c778b0 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 29 Nov 2025 20:28:04 +0100 Subject: [PATCH 52/60] Add option to use markdown links in folder overview --- src/obsidian-folder-overview | 2 +- src/settings/SettingsTab.ts | 1 + src/suggesters/FileSuggester.ts | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 69f6e80..decf998 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 69f6e80b248e6317ca3d9587f27891ce86f55c79 +Subproject commit decf99839f5cd7fee5402066edd12043b3ffed1e diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index 2eeb650..3139136 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -137,6 +137,7 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = { fmtpIntegration: false, titleSize: 1, isInCallout: false, + useWikilinks: true, }, useSubmenus: true, syncMove: true, diff --git a/src/suggesters/FileSuggester.ts b/src/suggesters/FileSuggester.ts index 3828036..879034d 100644 --- a/src/suggesters/FileSuggester.ts +++ b/src/suggesters/FileSuggester.ts @@ -14,7 +14,6 @@ export class FileSuggest extends TextInputSuggest { super(inputEl, plugin); } - get_error_msg(mode: FileSuggestMode): string { switch (mode) { case FileSuggestMode.TemplateFiles: From 1d7a065b8a9303261ff5e059886feecdf964ba93 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Sat, 29 Nov 2025 21:00:38 +0100 Subject: [PATCH 53/60] Update obsidian-folder-overview --- src/obsidian-folder-overview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index decf998..c5986ef 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit decf99839f5cd7fee5402066edd12043b3ffed1e +Subproject commit c5986efabab87155c1ab652920deebbc75b883af From f4dbd3e93ca2e87532d0e4f449f31913fdda7f0b Mon Sep 17 00:00:00 2001 From: Sergey Kolesnik <1984175+stdword@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:58:14 +0300 Subject: [PATCH 54/60] fix: make.md plugin integration --- src/functions/utils.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/functions/utils.ts b/src/functions/utils.ts index ec9f2ff..cca1363 100644 --- a/src/functions/utils.ts +++ b/src/functions/utils.ts @@ -44,7 +44,17 @@ export function getFileExplorer( plugin: FolderNotesPlugin | FolderOverviewPlugin, ): FileExplorerWorkspaceLeaf { // eslint-disable-next-line max-len - const leaf = plugin.app.workspace.getLeavesOfType('file-explorer')[0] as unknown as FileExplorerWorkspaceLeaf; + let leaf = plugin.app.workspace.getLeavesOfType('file-explorer')[0] as unknown as FileExplorerWorkspaceLeaf; + + /* make.md plugin integration */ + if (leaf.containerEl.lastChild.dataset.type == 'mk-path-view') { + plugin.app.workspace.iterateAllLeaves((x) => { + if (x.tabHeaderEl.dataset.type == 'file-explorer') { + leaf = x; + } + }); + } + return leaf; } From ef59e9998e2c29d3cddb859dfb4421f834b52333 Mon Sep 17 00:00:00 2001 From: Sergey Kolesnik <1984175+stdword@users.noreply.github.com> Date: Thu, 18 Dec 2025 21:00:54 +0300 Subject: [PATCH 55/60] fix: make.md plugin integration --- styles.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/styles.css b/styles.css index 84eb68f..a8a114b 100644 --- a/styles.css +++ b/styles.css @@ -8,6 +8,12 @@ display: none; } +/* make.md plugin integration */ +.hide-folder-note .mk-tree-node > .mk-tree-wrapper > .dropzone > .mk-tree-item.is-folder-note { + opacity: 40%; + display: flex; +} + .pointer-cursor, .has-folder-note .nav-folder-title-content:hover, .has-folder-note.view-header-breadcrumb:hover, @@ -337,4 +343,4 @@ body.fn-hide-collapse-icon.only-has-folder-note:not(.is-collapsed):not(.show-fol .is-phone .fn-overview-folder-path .setting-item-control { display: block; } -} \ No newline at end of file +} From 4d30ea696e4affe6efce7040698bbaea2223243f Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Fri, 2 Jan 2026 12:55:40 +0100 Subject: [PATCH 56/60] Fix #300 --- src/main.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.ts b/src/main.ts index ad5bf0b..493726c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -30,7 +30,6 @@ import { refreshAllFolderStyles, setActiveFolder, removeActiveFolder, } from './functions/styleFunctions'; import { getExcludedFolder } from './ExcludeFolders/functions/folderFunctions'; -import type { FileExplorerView, InternalPlugin } from 'obsidian-typings'; import { FOLDER_OVERVIEW_VIEW, FolderOverviewView } from './obsidian-folder-overview/src/view'; import { registerOverviewCommands } from './obsidian-folder-overview/src/Commands'; import { updateOverviewView, updateViewDropdown } from './obsidian-folder-overview/src/main'; @@ -51,9 +50,6 @@ export default class FolderNotesPlugin extends Plugin { askModalCurrentlyOpen = false; fvIndexDB: FvIndexDB; - private fileExplorerPlugin!: InternalPlugin; - private fileExplorerView!: FileExplorerView; - async onload(): Promise { console.log('loading folder notes plugin'); await this.loadSettings(); @@ -175,6 +171,10 @@ export default class FolderNotesPlugin extends Plugin { return new FolderOverviewView(leaf, this); }); + this.app.workspace.on('layout-change', () => { + this.tabManager.updateTabs(); + }); + if (this.app.plugins.getPlugin('obsidian-front-matter-title-plugin')) { this.fmtpHandler = new FrontMatterTitlePluginHandler(this); } From 88cb9541f09054a6770bbd76ca38cc3d830f4970 Mon Sep 17 00:00:00 2001 From: mjkerrison <25721638+mjkerrison@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:35:49 +1100 Subject: [PATCH 57/60] fix: prevent excluded folders list from being reset The filter logic in deleteFolderNote() and hideFolderNoteInFileExplorer() was using && instead of ||, causing all excluded folders with showFolderNote=false (the default) to be removed instead of just the target entry. Fixes #293 Co-Authored-By: Claude Opus 4.5 --- src/functions/folderNoteFunctions.ts | 2 +- src/functions/styleFunctions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/functions/folderNoteFunctions.ts b/src/functions/folderNoteFunctions.ts index 69e1830..e91d750 100644 --- a/src/functions/folderNoteFunctions.ts +++ b/src/functions/folderNoteFunctions.ts @@ -410,7 +410,7 @@ export async function deleteFolderNote( if (!folder) return; plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter( - (excludedFolder) => (excludedFolder.path !== folder.path) && excludedFolder.showFolderNote); + (excludedFolder) => (excludedFolder.path !== folder.path) || !excludedFolder.showFolderNote); plugin.saveSettings(false); removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); diff --git a/src/functions/styleFunctions.ts b/src/functions/styleFunctions.ts index a99c6b8..7959702 100644 --- a/src/functions/styleFunctions.ts +++ b/src/functions/styleFunctions.ts @@ -226,7 +226,7 @@ export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPl export function hideFolderNoteInFileExplorer(folderPath: string, plugin: FolderNotesPlugin): void { plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter( - (folder) => (folder.path !== folderPath) && folder.showFolderNote); + (folder) => (folder.path !== folderPath) || !folder.showFolderNote); plugin.saveSettings(false); removeCSSClassFromFileExplorerEL(folderPath, 'show-folder-note-in-explorer', true, plugin); updateCSSClassesForFolder(folderPath, plugin); From a044e6c2eb19d7e08e9a4e6e6b28ac0f182f710e Mon Sep 17 00:00:00 2001 From: mjkerrison <25721638+mjkerrison@users.noreply.github.com> Date: Mon, 12 Jan 2026 12:22:28 +1100 Subject: [PATCH 58/60] fix: startup error and settings tab reset issues - Handle missing file explorer during early startup to prevent "Cannot read properties of undefined" error - Use renderSettingsPage instead of display when adding excluded/ whitelisted folders to stay on current settings tab Co-Authored-By: Claude Opus 4.5 --- .../modals/WhitelistedFoldersSettings.ts | 2 +- src/functions/styleFunctions.ts | 6 ++++-- src/functions/utils.ts | 17 ++++++++++------- src/settings/ExcludedFoldersSettings.ts | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts b/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts index b00342d..43d5cce 100644 --- a/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts +++ b/src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts @@ -40,7 +40,7 @@ export default class WhitelistedFoldersSettings extends Modal { this.plugin.settingsTab, contentEl, whitelistedFolder, ); addWhitelistedFolder(this.plugin, whitelistedFolder); - this.settingsTab.display(); + this.settingsTab.renderSettingsPage(this.settingsTab.plugin.settings.settingsTab); }); }); diff --git a/src/functions/styleFunctions.ts b/src/functions/styleFunctions.ts index 7959702..4e99d42 100644 --- a/src/functions/styleFunctions.ts +++ b/src/functions/styleFunctions.ts @@ -14,8 +14,10 @@ import type FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main'; * @description Refreshes the CSS classes for all folder notes in the file explorer. */ export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesPlugin): void { - if (plugin.activeFileExplorer === getFileExplorer(plugin) && !forceReload) { return; } - plugin.activeFileExplorer = getFileExplorer(plugin); + const fileExplorer = getFileExplorer(plugin); + if (!fileExplorer) { return; } + if (plugin.activeFileExplorer === fileExplorer && !forceReload) { return; } + plugin.activeFileExplorer = fileExplorer; plugin.app.vault.getAllLoadedFiles().forEach(async (file) => { if (file instanceof TFolder) { await updateCSSClassesForFolder(file.path, plugin); diff --git a/src/functions/utils.ts b/src/functions/utils.ts index cca1363..be765f2 100644 --- a/src/functions/utils.ts +++ b/src/functions/utils.ts @@ -42,15 +42,17 @@ export function getParentFolderPath(path: string): string { export function getFileExplorer( plugin: FolderNotesPlugin | FolderOverviewPlugin, -): FileExplorerWorkspaceLeaf { +): FileExplorerWorkspaceLeaf | undefined { // eslint-disable-next-line max-len let leaf = plugin.app.workspace.getLeavesOfType('file-explorer')[0] as unknown as FileExplorerWorkspaceLeaf; + if (!leaf) { return undefined; } + /* make.md plugin integration */ - if (leaf.containerEl.lastChild.dataset.type == 'mk-path-view') { + if ((leaf.containerEl?.lastChild as HTMLElement)?.dataset?.type == 'mk-path-view') { plugin.app.workspace.iterateAllLeaves((x) => { - if (x.tabHeaderEl.dataset.type == 'file-explorer') { - leaf = x; + if ((x as FileExplorerWorkspaceLeaf).tabHeaderEl?.dataset?.type == 'file-explorer') { + leaf = x as FileExplorerWorkspaceLeaf; } }); } @@ -58,12 +60,13 @@ export function getFileExplorer( return leaf; } -export function getFileExplorerView(plugin: FolderNotesPlugin): FileExplorerView { - return getFileExplorer(plugin).view; +export function getFileExplorerView(plugin: FolderNotesPlugin): FileExplorerView | undefined { + return getFileExplorer(plugin)?.view; } export function getFocusedItem(plugin: FolderNotesPlugin): TreeNode | null { - const fileExplorer = getFileExplorer(plugin) as unknown as FileExplorerLeaf; + const fileExplorer = getFileExplorer(plugin) as unknown as FileExplorerLeaf | undefined; + if (!fileExplorer) { return null; } const { focusedItem } = fileExplorer.view.tree; return focusedItem; } diff --git a/src/settings/ExcludedFoldersSettings.ts b/src/settings/ExcludedFoldersSettings.ts index b8b9345..55e5c7e 100644 --- a/src/settings/ExcludedFoldersSettings.ts +++ b/src/settings/ExcludedFoldersSettings.ts @@ -93,7 +93,7 @@ export async function renderExcludeFolders(settingsTab: SettingsTab): Promise Date: Wed, 28 Jan 2026 12:27:22 +0100 Subject: [PATCH 59/60] Fix #296 --- styles.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/styles.css b/styles.css index a8a114b..1466d0d 100644 --- a/styles.css +++ b/styles.css @@ -23,6 +23,9 @@ cursor: pointer !important; } +.hide-folder-note :not(.show-folder-note-in-explorer).only-has-folder-note .nav-folder-children { + display: none !important; +} /* ========================================================================== Tree Items From 7c41751c34b43b2863ae4358a8942b4114d2a382 Mon Sep 17 00:00:00 2001 From: Paul <70213368+LostPaul@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:47:00 +0100 Subject: [PATCH 60/60] Update manifest.json --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index ad3738c..94ead43 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "folder-notes", "name": "Folder notes", - "version": "1.8.17", + "version": "1.8.18", "minAppVersion": "0.15.0", "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", "author": "Lost Paul",