From f6316aecc70fc9ef1fda5dda9db6d1f99cdb3c31 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Wed, 26 Feb 2025 10:44:49 +0100 Subject: [PATCH 1/2] feat: use file-based smart playlists --- package-lock.json | 988 +++++++++++++-------- package.json | 6 +- src/App.d.ts | 10 +- src/data/FolderWatcher.ts | 2 +- src/data/M3UUtils.ts | 217 ----- src/data/PlaylistUtils.ts | 444 +++++++++ src/data/store.ts | 14 +- src/data/storeHelper.ts | 4 +- src/lib/library/CanvasLibrary.svelte | 14 +- src/lib/library/PlaylistHeader.svelte | 13 +- src/lib/library/SmartPlaylistHeader.svelte | 70 +- src/lib/library/TrackMenu.svelte | 4 +- src/lib/queue/QueueMenu.svelte | 4 +- src/lib/sidebar/Sidebar.svelte | 252 ++++-- src/lib/smart-query/Query.ts | 237 ++++- src/lib/smart-query/QueryPart.ts | 2 +- src/lib/views/CanvasLibraryView.svelte | 56 +- tsconfig.json | 1 + 18 files changed, 1556 insertions(+), 782 deletions(-) delete mode 100644 src/data/M3UUtils.ts create mode 100644 src/data/PlaylistUtils.ts diff --git a/package-lock.json b/package-lock.json index 4f501d83..612e3f64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,15 @@ { "name": "musicat", - "version": "0.11.0", + "version": "0.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "musicat", - "version": "0.11.0", + "version": "0.12.0", "dependencies": { + "@zokugun/dynopl": "^0.1.0", + "ajv": "^8.17.1", "chord-symbol": "^4.0.0", "is-dark-color": "^1.2.0", "mousetrap": "^1.6.5", @@ -16,7 +18,9 @@ "svelecte": "^4.5.1", "svelte-tiny-virtual-list": "^2.1.2", "svrollbar": "^0.12.0", - "wtf-plugin-html": "^1.0.0" + "uuid": "^11.1.0", + "wtf-plugin-html": "^1.0.0", + "yaml": "^2.7.0" }, "devDependencies": { "@alexanderolsen/libsamplerate-js": "^2.1.1", @@ -146,9 +150,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", "cpu": [ "ppc64" ], @@ -164,9 +168,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", "cpu": [ "arm" ], @@ -182,9 +186,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", "cpu": [ "arm64" ], @@ -200,9 +204,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", "cpu": [ "x64" ], @@ -218,9 +222,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "cpu": [ "arm64" ], @@ -236,9 +240,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", "cpu": [ "x64" ], @@ -254,9 +258,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", "cpu": [ "arm64" ], @@ -272,9 +276,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", "cpu": [ "x64" ], @@ -290,9 +294,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", "cpu": [ "arm" ], @@ -308,9 +312,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", "cpu": [ "arm64" ], @@ -326,9 +330,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", "cpu": [ "ia32" ], @@ -344,9 +348,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", "cpu": [ "loong64" ], @@ -362,9 +366,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", "cpu": [ "mips64el" ], @@ -380,9 +384,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", "cpu": [ "ppc64" ], @@ -398,9 +402,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", "cpu": [ "riscv64" ], @@ -416,9 +420,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", "cpu": [ "s390x" ], @@ -434,9 +438,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", "cpu": [ "x64" ], @@ -452,9 +456,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", "cpu": [ "arm64" ], @@ -470,9 +474,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", "cpu": [ "x64" ], @@ -488,9 +492,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", "cpu": [ "arm64" ], @@ -506,9 +510,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", "cpu": [ "x64" ], @@ -524,9 +528,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", "cpu": [ "x64" ], @@ -542,9 +546,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", "cpu": [ "arm64" ], @@ -560,9 +564,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", "cpu": [ "ia32" ], @@ -578,9 +582,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", "cpu": [ "x64" ], @@ -603,14 +607,14 @@ "license": "Apache-2.0" }, "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.2.tgz", - "integrity": "sha512-6sE5nyvDloULiyOMbOTJEEgWL32w+VHkZQs8S02Lnn8Y/O5aQhjOEXwWzvR7SsBE/exxlSpY2EsWZgqHbtLatg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.3.tgz", + "integrity": "sha512-pJT1OkhplSmvvr6i3CWTPvC/FGC06MbN5TNBfRO6Ox62AEz90eMq+dVvtX9Bl3jxCEkS0tATzDarRZuOLw7oFg==", "dev": true, "license": "MIT", "dependencies": { "@formatjs/fast-memoize": "2.2.6", - "@formatjs/intl-localematcher": "0.5.10", + "@formatjs/intl-localematcher": "0.6.0", "decimal.js": "10", "tslib": "2" } @@ -626,32 +630,32 @@ } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.9.8", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.8.tgz", - "integrity": "sha512-hZlLNI3+Lev8IAXuwehLoN7QTKqbx3XXwFW1jh0AdIA9XJdzn9Uzr+2LLBspPm/PX0+NLIfykj/8IKxQqHUcUQ==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.1.tgz", + "integrity": "sha512-o0AhSNaOfKoic0Sn1GkFCK4MxdRsw7mPJ5/rBpIqdvcC7MIuyUSW8WChUEvrK78HhNpYOgqCQbINxCTumJLzZA==", "dev": true, "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.2", - "@formatjs/icu-skeleton-parser": "1.8.12", + "@formatjs/ecma402-abstract": "2.3.3", + "@formatjs/icu-skeleton-parser": "1.8.13", "tslib": "2" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.12", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.12.tgz", - "integrity": "sha512-QRAY2jC1BomFQHYDMcZtClqHR55EEnB96V7Xbk/UiBodsuFc5kujybzt87+qj1KqmJozFhk6n4KiT1HKwAkcfg==", + "version": "1.8.13", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.13.tgz", + "integrity": "sha512-N/LIdTvVc1TpJmMt2jVg0Fr1F7Q1qJPdZSCs19unMskCmVQ/sa0H9L8PWt13vq+gLdLg1+pPsvBLydL1Apahjg==", "dev": true, "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.2", + "@formatjs/ecma402-abstract": "2.3.3", "tslib": "2" } }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz", - "integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.0.tgz", + "integrity": "sha512-4rB4g+3hESy1bHSBG3tDFaMY2CH67iT7yne1e+0CLTsGLDcmoEWWpJjjpWVaYgYfYuohIRuo0E+N536gd2ZHZA==", "dev": true, "license": "MIT", "dependencies": { @@ -707,9 +711,9 @@ } }, "node_modules/@modyfi/vite-plugin-yaml": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@modyfi/vite-plugin-yaml/-/vite-plugin-yaml-1.1.0.tgz", - "integrity": "sha512-L26xfzkSo1yamODCAtk/ipVlL6OEw2bcJ92zunyHu8zxi7+meV0zefA9xscRMDCsMY8xL3C3wi3DhMiPxcbxbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@modyfi/vite-plugin-yaml/-/vite-plugin-yaml-1.1.1.tgz", + "integrity": "sha512-rEbfFNlMGLKpAYs2RsfLAhxCHFa6M4QKHHk0A4EYcCJAUwFtFO6qiEdLjUGUTtnRUxAC7GxxCa+ZbeUILSDvqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -718,13 +722,13 @@ "tosource": "2.0.0-alpha.3" }, "peerDependencies": { - "vite": "^3.2.7 || ^4.0.5 || ^5.0.5" + "vite": ">=3.2.7" } }, "node_modules/@parcel/watcher": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", - "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -743,25 +747,25 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.0", - "@parcel/watcher-darwin-arm64": "2.5.0", - "@parcel/watcher-darwin-x64": "2.5.0", - "@parcel/watcher-freebsd-x64": "2.5.0", - "@parcel/watcher-linux-arm-glibc": "2.5.0", - "@parcel/watcher-linux-arm-musl": "2.5.0", - "@parcel/watcher-linux-arm64-glibc": "2.5.0", - "@parcel/watcher-linux-arm64-musl": "2.5.0", - "@parcel/watcher-linux-x64-glibc": "2.5.0", - "@parcel/watcher-linux-x64-musl": "2.5.0", - "@parcel/watcher-win32-arm64": "2.5.0", - "@parcel/watcher-win32-ia32": "2.5.0", - "@parcel/watcher-win32-x64": "2.5.0" + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", - "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", "cpu": [ "arm64" ], @@ -780,9 +784,9 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", - "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", "cpu": [ "arm64" ], @@ -801,9 +805,9 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", - "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", "cpu": [ "x64" ], @@ -822,9 +826,9 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", - "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", "cpu": [ "x64" ], @@ -843,9 +847,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", - "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", "cpu": [ "arm" ], @@ -864,9 +868,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", - "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", "cpu": [ "arm" ], @@ -885,9 +889,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", - "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", "cpu": [ "arm64" ], @@ -906,9 +910,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", - "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", "cpu": [ "arm64" ], @@ -927,9 +931,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", - "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", "cpu": [ "x64" ], @@ -948,9 +952,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", - "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", "cpu": [ "x64" ], @@ -969,9 +973,9 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", - "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", "cpu": [ "arm64" ], @@ -990,9 +994,9 @@ } }, "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", - "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", "cpu": [ "ia32" ], @@ -1011,9 +1015,9 @@ } }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", - "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", "cpu": [ "x64" ], @@ -1118,9 +1122,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.2.2.tgz", - "integrity": "sha512-5fVEdP4t4BT0ymvXZAM78kB0S/TaiRDLDoSRWGxVy1e7XCwuKyST5m6ybeyw/h/soK/91tbf+W3xXXy7XzkT4A==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.2.7.tgz", + "integrity": "sha512-ZnsS2B4BplwXP37celanNANiIy8TCYhvg5RT09n72uR/o+navFZtGpFSqljV8fy1Y4ixIPds8FrGSXJCN2BerA==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -1134,22 +1138,22 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.2.2", - "@tauri-apps/cli-darwin-x64": "2.2.2", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.2.2", - "@tauri-apps/cli-linux-arm64-gnu": "2.2.2", - "@tauri-apps/cli-linux-arm64-musl": "2.2.2", - "@tauri-apps/cli-linux-x64-gnu": "2.2.2", - "@tauri-apps/cli-linux-x64-musl": "2.2.2", - "@tauri-apps/cli-win32-arm64-msvc": "2.2.2", - "@tauri-apps/cli-win32-ia32-msvc": "2.2.2", - "@tauri-apps/cli-win32-x64-msvc": "2.2.2" + "@tauri-apps/cli-darwin-arm64": "2.2.7", + "@tauri-apps/cli-darwin-x64": "2.2.7", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.2.7", + "@tauri-apps/cli-linux-arm64-gnu": "2.2.7", + "@tauri-apps/cli-linux-arm64-musl": "2.2.7", + "@tauri-apps/cli-linux-x64-gnu": "2.2.7", + "@tauri-apps/cli-linux-x64-musl": "2.2.7", + "@tauri-apps/cli-win32-arm64-msvc": "2.2.7", + "@tauri-apps/cli-win32-ia32-msvc": "2.2.7", + "@tauri-apps/cli-win32-x64-msvc": "2.2.7" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.2.2.tgz", - "integrity": "sha512-JMXbX5hGrLOzJbjisd7gBe25PmHouXA1+f4yVWg5PRlgxW7pla7krOzhu2mchFlMVDr8aLwhMLgohvvx+raXag==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.2.7.tgz", + "integrity": "sha512-54kcpxZ3X1Rq+pPTzk3iIcjEVY4yv493uRx/80rLoAA95vAC0c//31Whz75UVddDjJfZvXlXZ3uSZ+bnCOnt0A==", "cpu": [ "arm64" ], @@ -1164,9 +1168,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.2.2.tgz", - "integrity": "sha512-i2gxKXev+Ed0UWeq0xSiyRjSkzzBbu9MCOhs/QVv1YuV+097K/fF89f+a4v5JJftCq2IOHhSKH23KyTJBLnNKQ==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.2.7.tgz", + "integrity": "sha512-Vgu2XtBWemLnarB+6LqQeLanDlRj7CeFN//H8bVVdjbNzxcSxsvbLYMBP8+3boa7eBnjDrqMImRySSgL6IrwTw==", "cpu": [ "x64" ], @@ -1181,9 +1185,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.2.2.tgz", - "integrity": "sha512-iC2HndsN5smmbvEDUQFyTHyYHSgx7OwJ6puyXLLpkAHnQDo4TGSPxIlPeZFSZmEoaJEmHLdG3j1LcFWOKrxWQg==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.2.7.tgz", + "integrity": "sha512-+Clha2iQAiK9zoY/KKW0KLHkR0k36O78YLx5Sl98tWkwI3OBZFg5H5WT1plH/4sbZIS2aLFN6dw58/JlY9Bu/g==", "cpu": [ "arm" ], @@ -1198,9 +1202,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.2.2.tgz", - "integrity": "sha512-YzK30tleUzWxfIp1davc5RhvmNZxiZQkUnQ4zajGJZ99zxNk8kwvv8nYSC3/J2R8sYpnuv+7CzNyIwA2s6yH+w==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.2.7.tgz", + "integrity": "sha512-Z/Lp4SQe6BUEOays9BQAEum2pvZF4w9igyXijP+WbkOejZx4cDvarFJ5qXrqSLmBh7vxrdZcLwoLk9U//+yQrg==", "cpu": [ "arm64" ], @@ -1215,9 +1219,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.2.tgz", - "integrity": "sha512-nOw2apBzOCTiH1fLOjL42ajHNhMzdp640CX5RrWkDYdyVO7YbGmWzrN26PPXohScScXVjSjtDDxdeQV1gHCxhg==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.7.tgz", + "integrity": "sha512-+8HZ+txff/Y3YjAh80XcLXcX8kpGXVdr1P8AfjLHxHdS6QD4Md+acSxGTTNbplmHuBaSHJvuTvZf9tU1eDCTDg==", "cpu": [ "arm64" ], @@ -1232,9 +1236,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.2.2.tgz", - "integrity": "sha512-Tmm4qVY8yxSugi8sCko1dyZxyPGK8m3tWm+b1J0DXwzxqaoMqNXuYGxkwtUdkznPXEfQSD8OGBfwjXNmVGE91Q==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.2.7.tgz", + "integrity": "sha512-ahlSnuCnUntblp9dG7/w5ZWZOdzRFi3zl0oScgt7GF4KNAOEa7duADsxPA4/FT2hLRa0SvpqtD4IYFvCxoVv3Q==", "cpu": [ "x64" ], @@ -1249,9 +1253,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.2.tgz", - "integrity": "sha512-AwAcaCUhmwzOFPvje80g2BAhnkoEpXdl1E0Uk+lvr9makHM0+aV++M5jibD97yxKnK5NrQ9YXPH5Sn6CdncgUg==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.7.tgz", + "integrity": "sha512-+qKAWnJRSX+pjjRbKAQgTdFY8ecdcu8UdJ69i7wn3ZcRn2nMMzOO2LOMOTQV42B7/Q64D1pIpmZj9yblTMvadA==", "cpu": [ "x64" ], @@ -1266,9 +1270,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.2.2.tgz", - "integrity": "sha512-u7TnwuUAN+eX6R2kDfSM8fsUFiBzNqq9PnAOsQ2qbwbHGbu7mHfGO1OFgnIzBt1C9FolFbENk2pzjiL4R9baXQ==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.2.7.tgz", + "integrity": "sha512-aa86nRnrwT04u9D9fhf5JVssuAZlUCCc8AjqQjqODQjMd4BMA2+d4K9qBMpEG/1kVh95vZaNsLogjEaqSTTw4A==", "cpu": [ "arm64" ], @@ -1283,9 +1287,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.2.2.tgz", - "integrity": "sha512-9KScbGKU6GfHThEYWdlO0+COW/8SDfIXbYgEvEcfZztE4VedHBbI0XfU+l+aS8nJN+fvYX+DtvY0tpDwyo0G4A==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.2.7.tgz", + "integrity": "sha512-EiJ5/25tLSQOSGvv+t6o3ZBfOTKB5S3vb+hHQuKbfmKdRF0XQu2YPdIi1CQw1DU97ZAE0Dq4frvnyYEKWgMzVQ==", "cpu": [ "ia32" ], @@ -1300,9 +1304,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.2.2.tgz", - "integrity": "sha512-ko8OWCLwFaHfdBaKbRX/C5btNt61v17qKOSQPksuc5PVvY0tAoci09612nEMlYiogZKEtn7VAqSdRAG6h0tz+g==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.2.7.tgz", + "integrity": "sha512-ZB8Kw90j8Ld+9tCWyD2fWCYfIrzbQohJ4DJSidNwbnehlZzP7wAz6Z3xjsvUdKtQ3ibtfoeTqVInzCCEpI+pWg==", "cpu": [ "x64" ], @@ -1317,9 +1321,9 @@ } }, "node_modules/@tauri-apps/plugin-clipboard-manager": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.2.0.tgz", - "integrity": "sha512-sIBrW/HioKq2vqomwwcU/Y8ygAv3DlS32yKPBX5XijCc0IyQKiDxYpGqmvE9DC5Y0lNJ/G53dfS961B31wjJ1g==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.2.1.tgz", + "integrity": "sha512-+7YDULB9Bk4fejxYrVNBQcxs3KsjPA3A3r53wwn7K8zOQvxjNBSYBRx/FW1OUBPGzm8BrreJFBkPVzQZSF2R4A==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { @@ -1357,9 +1361,9 @@ } }, "node_modules/@tauri-apps/plugin-http": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-http/-/plugin-http-2.2.0.tgz", - "integrity": "sha512-ZY6sIHhgu8hcu6BkkegoiOEbvOsQFSVcK8J7l+g9RNHrkhl5uzpNIytR4R/H50fj7gyG80DJvrXDx/LBo7Easw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-http/-/plugin-http-2.3.0.tgz", + "integrity": "sha512-pigTvz+zzAqbIhCzRiR1GE98Jw7A03j2V+Eiexr9thBI8VfMiwFQMcbgON51xlwnVaI72LdbYKNajU84im8tlg==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { @@ -1412,9 +1416,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.70", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz", - "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==", + "version": "18.19.76", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.76.tgz", + "integrity": "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==", "dev": true, "license": "MIT", "dependencies": { @@ -1471,6 +1475,12 @@ "simple-yenc": "^1.0.4" } }, + "node_modules/@zokugun/dynopl": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@zokugun/dynopl/-/dynopl-0.1.0.tgz", + "integrity": "sha512-pds0fueHd35zyg5xZSZmiIwOCHcdi4UX43Ww73F0IX1NP77CvvLT7Z/kNnqpZHKKBUhJsqLwfZWNzdWNddNulw==", + "license": "MIT" + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -1508,6 +1518,22 @@ "node": ">= 8.0.0" } }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-regex": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", @@ -1777,6 +1803,20 @@ "node": ">=8.0.0" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -1808,9 +1848,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001692", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", - "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", + "version": "1.0.30001701", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz", + "integrity": "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==", "dev": true, "funding": [ { @@ -1839,41 +1879,6 @@ } }, "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chokidar-cli": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-3.0.0.tgz", - "integrity": "sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "lodash.debounce": "^4.0.8", - "lodash.throttle": "^4.1.1", - "yargs": "^13.3.0" - }, - "bin": { - "chokidar": "index.js" - }, - "engines": { - "node": ">= 8.10.0" - } - }, - "node_modules/chokidar-cli/node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", @@ -1898,17 +1903,23 @@ "fsevents": "~2.3.2" } }, - "node_modules/chokidar-cli/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/chokidar-cli": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-3.0.0.tgz", + "integrity": "sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==", "dev": true, "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "chokidar": "^3.5.2", + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1", + "yargs": "^13.3.0" + }, + "bin": { + "chokidar": "index.js" }, "engines": { - "node": ">=8.10.0" + "node": ">= 8.10.0" } }, "node_modules/chord-mark": { @@ -2143,9 +2154,9 @@ } }, "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "dev": true, "license": "MIT" }, @@ -2194,9 +2205,9 @@ } }, "node_modules/dexie": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.10.tgz", - "integrity": "sha512-eM2RzuR3i+M046r2Q0Optl3pS31qTWf8aFuA7H9wnsHTwl8EPvroVLwvQene/6paAs39Tbk6fWZcn2aZaHkc/w==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.11.tgz", + "integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==", "dev": true, "license": "Apache-2.0" }, @@ -2205,24 +2216,40 @@ "resolved": "https://registry.npmjs.org/dexie-export-import/-/dexie-export-import-4.1.4.tgz", "integrity": "sha512-3bw171qUuOTWSYLXI7C/0M6p1X65Rho3tu1IvD9By8jn0+3t3dLSkDlZ1BC6MbABl3kRlhtGigzC+SF+qcS5Og==", "dev": true, + "license": "Apache-2.0", "peerDependencies": { "dexie": "^2.0.4 || ^3.0.0 || ^4.0.1" } }, "node_modules/dompurify": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", - "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", "dev": true, "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/electron-to-chromium": { - "version": "1.5.80", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", - "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==", + "version": "1.5.105", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.105.tgz", + "integrity": "sha512-ccp7LocdXx3yBhwiG0qTQ7XFrK48Ua2pxIxBdJO8cbddp/MvbBtPFzvnTchtyHQTsgqqczO8cdmAIbpMa0u2+g==", "dev": true, "license": "ISC" }, @@ -2242,6 +2269,55 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es5-ext": { "version": "0.10.64", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", @@ -2306,9 +2382,9 @@ } }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2320,31 +2396,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, "node_modules/escalade": { @@ -2446,6 +2522,28 @@ "type": "^2.7.2" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/file-type": { "version": "16.5.4", "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", @@ -2502,14 +2600,15 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -2626,6 +2725,45 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2675,6 +2813,19 @@ "dev": true, "license": "MIT" }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2691,6 +2842,35 @@ "node": ">=6" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2790,18 +2970,19 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, "license": "ISC" }, "node_modules/intl-messageformat": { - "version": "10.7.11", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.11.tgz", - "integrity": "sha512-IB2N1tmI24k2EFH3PWjU7ivJsnWyLwOWOva0jnXFa29WzB6fb0JZ5EMQGu+XN5lDtjHYFo0/UooP67zBwUg7rQ==", + "version": "10.7.15", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.15.tgz", + "integrity": "sha512-LRyExsEsefQSBjU2p47oAheoKz+EOJxSLDdjOaEjdriajfHsMXOmV/EhMvYSg9bAgCUHasuAC+mcUBe/95PfIg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@formatjs/ecma402-abstract": "2.3.2", + "@formatjs/ecma402-abstract": "2.3.3", "@formatjs/fast-memoize": "2.2.6", - "@formatjs/icu-messageformat-parser": "2.9.8", + "@formatjs/icu-messageformat-parser": "2.11.1", "tslib": "2" } }, @@ -2955,6 +3136,12 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -3092,6 +3279,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -3481,9 +3678,9 @@ } }, "node_modules/openai": { - "version": "4.78.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.78.0.tgz", - "integrity": "sha512-4rRsKkx++5m1zayxkryVH+K/z91cv1sRbaNJAhSQjZiSCQOR7eaM8KpfIssXrS9Hlpta7+VcuO/fi57pW8xGjA==", + "version": "4.85.4", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.85.4.tgz", + "integrity": "sha512-Nki51PBSu+Aryo7WKbdXvfm0X/iKkQS2fq3O0Uqb/O3b4exOZFid2te1BZ52bbO5UwxQZ5eeHJDCTqtrJLPw0w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3499,9 +3696,13 @@ "openai": "bin/cli" }, "peerDependencies": { + "ws": "^8.18.0", "zod": "^3.23.8" }, "peerDependenciesMeta": { + "ws": { + "optional": true + }, "zod": { "optional": true } @@ -3656,9 +3857,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -3676,7 +3877,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3692,9 +3893,9 @@ "license": "MIT" }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", + "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", "dev": true, "license": "MIT", "bin": { @@ -3797,12 +3998,12 @@ } }, "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", "license": "MIT", "dependencies": { - "readable-stream": "^3.6.0" + "readable-stream": "^4.7.0" }, "engines": { "node": ">=8" @@ -3812,32 +4013,17 @@ "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 14.16.0" + "dependencies": { + "picomatch": "^2.2.1" }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=8.10.0" } }, "node_modules/redent": { @@ -3881,6 +4067,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -4017,9 +4212,9 @@ } }, "node_modules/sass": { - "version": "1.83.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.83.1.tgz", - "integrity": "sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA==", + "version": "1.85.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.85.1.tgz", + "integrity": "sha512-Uk8WpxM5v+0cMR0XjX9KfRIacmSG86RH4DCCZjLU2rFh5tyutt9siAXJ7G+YfxQ99Q6wrRMbMlVl6KqUms71ag==", "dev": true, "license": "MIT", "dependencies": { @@ -4037,10 +4232,40 @@ "@parcel/watcher": "^2.4.1" } }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -4126,9 +4351,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", "license": "CC0-1.0" }, "node_modules/sprintf-js": { @@ -4263,44 +4488,6 @@ "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" } }, - "node_modules/svelte-check/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/svelte-check/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/svelte-check/node_modules/typescript": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", @@ -5678,11 +5865,18 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } }, "node_modules/validate-npm-package-license": { "version": "3.0.4", @@ -5710,9 +5904,9 @@ } }, "node_modules/vite": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.5.tgz", - "integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==", + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.9.tgz", + "integrity": "sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw==", "dev": true, "license": "MIT", "dependencies": { @@ -6285,9 +6479,9 @@ "license": "ISC" }, "node_modules/wtf_wikipedia": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/wtf_wikipedia/-/wtf_wikipedia-10.3.2.tgz", - "integrity": "sha512-8C1eUKDK6NaosrtocTEA4fz5Lm5nO6Hb92zLUqI7S1uVVjwEtI0mvSGSdGd/xR1nfSpDYm1ckBG1aLHEAF1pBg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/wtf_wikipedia/-/wtf_wikipedia-10.4.0.tgz", + "integrity": "sha512-yRxTiBURj2LW5HWAe+T7bCV2x45C/qTqcknUTmInKmB9cmLSxR6Nh44rB9K+nfNiydtjc3HLHwYWxMuHZtpVSQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -6323,6 +6517,18 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/package.json b/package.json index 7402e2aa..0e9be482 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,8 @@ "wtf_wikipedia": "^10.3.2" }, "dependencies": { + "@zokugun/dynopl": "^0.1.0", + "ajv": "^8.17.1", "chord-symbol": "^4.0.0", "is-dark-color": "^1.2.0", "mousetrap": "^1.6.5", @@ -82,6 +84,8 @@ "svelecte": "^4.5.1", "svelte-tiny-virtual-list": "^2.1.2", "svrollbar": "^0.12.0", - "wtf-plugin-html": "^1.0.0" + "uuid": "^11.1.0", + "wtf-plugin-html": "^1.0.0", + "yaml": "^2.7.0" } } diff --git a/src/App.d.ts b/src/App.d.ts index ec228cdd..701b150a 100644 --- a/src/App.d.ts +++ b/src/App.d.ts @@ -1,4 +1,5 @@ import type { UserQueryPart } from "./lib/smart-query/UserQueryPart"; +import type { Playlist as DynamicPlaylist } from "@zokugun/dynopl"; interface MetadataEntry { /** Musicat's cross-file tag identifier */ @@ -102,11 +103,18 @@ interface Playlist { tracks: string[]; } -interface PlaylistFile { +interface StaticPlaylistFile { path: string; title: string; // the filename } +interface DynamicPlaylistFile { + path: string; + title: string; // the filename + schema: DynamicPlaylist; + query?: SmartQuery; +} + /** * Represents a song/track that's in progress. */ diff --git a/src/data/FolderWatcher.ts b/src/data/FolderWatcher.ts index 14da6670..05706d89 100644 --- a/src/data/FolderWatcher.ts +++ b/src/data/FolderWatcher.ts @@ -17,7 +17,7 @@ import { loadArtistsFromSongbook, loadSongProjectsForArtist, } from "./ArtistsToolkitData"; -import { scanPlaylists } from "./M3UUtils"; +import { scanPlaylists } from "./PlaylistUtils"; // can also watch an array of paths export async function startWatchingLibraryFolders() { diff --git a/src/data/M3UUtils.ts b/src/data/M3UUtils.ts deleted file mode 100644 index 4254597f..00000000 --- a/src/data/M3UUtils.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { - exists, - mkdir, - readDir, - readTextFile, - rename, - writeTextFile, -} from "@tauri-apps/plugin-fs"; -import md5 from "md5"; -import { get } from "svelte/store"; -import type { PlaylistFile, Song } from "../App"; -import { db } from "./db"; -import { selectedPlaylistFile, userPlaylists, userSettings } from "./store"; -import { moveArrayElement } from "../utils/ArrayUtils"; -import { invoke } from "@tauri-apps/api/core"; -import { path } from "@tauri-apps/api"; - -interface M3UTrack { - duration: number; // Duration in seconds, -1 if unknown - title: string; // Track title and artist - path: string; // Path to the media file (local or URL) -} - -interface M3U { - tracks: M3UTrack[]; // List of tracks -} - -function parse(contents: string): M3U { - const lines = contents.split(/\r?\n/).filter((line) => line.trim() !== ""); - const tracks: M3UTrack[] = []; - let currentTrack: Partial = {}; - - for (const line of lines) { - if (line.trim().startsWith("#EXTINF:")) { - // Parse EXTINF line - const [, duration, title] = - line.match(/#EXTINF:([+-]?\d+(?:\.\d+)?),(.+)/) || []; - currentTrack = { - duration: parseInt(duration, 10), - title: title.trim(), - }; - } else if (!line.trim().startsWith("#")) { - // Parse media file path - if (currentTrack) { - currentTrack.path = line.trim(); - tracks.push(currentTrack as M3UTrack); - currentTrack = {}; // Reset for the next track - } - } - } - - return { tracks }; -} - -/** - * Converts an M3U object into an M3U playlist string. - * @param m3u The M3U object to convert. - * @returns The M3U playlist as a string. - */ -function write(m3u: M3U): string { - const lines: string[] = ["#EXTM3U"]; - - for (const track of m3u.tracks) { - lines.push(`#EXTINF:${track.duration},${track.title}`); - lines.push(track.path); - } - - return lines.join("\n"); -} - -export async function scanPlaylists() { - console.log("[M3U] Scanning playlists..."); - const playlistsLocation = get(userSettings).playlistsLocation; - // Read directory (or create if non existent) - const locationExists = await exists(playlistsLocation); - if (!locationExists) { - await mkdir(playlistsLocation); - } - const entries = await readDir(playlistsLocation); - const m3uFiles: PlaylistFile[] = []; - for (const entry of entries) { - if (entry.isFile && entry.name.endsWith(".m3u")) { - m3uFiles.push({ - title: entry.name.split(".m3u")[0], - path: await path.join(playlistsLocation, entry.name), - }); - } - } - userPlaylists.set( - m3uFiles.sort((a, b) => { - return a.title.localeCompare(b.title); - }), - ); - console.log("[M3U]: Playlists: ", m3uFiles); -} - -export async function parsePlaylist( - playlistFile: PlaylistFile, -): Promise { - const fileContents = await readTextFile(playlistFile.path); - let playlist: M3U; - try { - playlist = parse(fileContents); - } catch (e) { - console.error(e); - return []; - } - - console.log("M3U: parsed playlist", playlist); - - // Do a db query to get all those songs - const songIds = playlist.tracks.map((c) => md5(c.path)); - try { - const songs = await db.songs.bulkGet(songIds); - console.log("M3U: got songs", songs); - return songs; - } catch (e) { - console.error(e); - return []; - } -} - -export async function addSongsToPlaylist( - playlistFile: PlaylistFile, - songs: Song[], -) { - const existingSongs = await parsePlaylist(playlistFile); - await writePlaylist(playlistFile, [...existingSongs, ...songs]); -} - -export async function insertSongsToPlaylist( - playlistFile: PlaylistFile, - songs: Song[], - index: number, -) { - const playlist = await parsePlaylist(playlistFile); - const newPlaylist = [...playlist]; - newPlaylist.splice(index, 0, ...songs); - await writePlaylist(playlistFile, newPlaylist); -} - -export async function createNewPlaylistFile(title: string, songs: Song[] = []) { - await writePlaylist( - { - path: await path.join( - get(userSettings).playlistsLocation, - `${title}.m3u`, - ), - title, - }, - songs, - ); - await scanPlaylists(); -} - -export async function renamePlaylist( - playlistFile: PlaylistFile, - newTitle: string, -) { - console.log("renamePlaylist", playlistFile, newTitle); - const newPath = playlistFile.path.replace(playlistFile.title, newTitle); - console.log(`Renaming ${playlistFile.path} to ${newPath}`); - await rename(playlistFile.path, newPath); - await scanPlaylists(); - selectedPlaylistFile.set({ - path: newPath, - title: newTitle, - }); -} - -export async function deletePlaylistFile(playlistFile: PlaylistFile) { - await invoke("delete_files", { - event: { - files: [playlistFile.path], - }, - }); - await scanPlaylists(); -} - -export async function reorderSongsInPlaylist( - playlistFile: PlaylistFile, - fromIdx: number, - toIdx: number, -) { - let playlist = await parsePlaylist(playlistFile); - playlist = moveArrayElement(playlist, fromIdx, toIdx); - await writePlaylist(playlistFile, playlist); -} - -export async function deleteSongsFromPlaylist( - playlistFile: PlaylistFile, - songs: Song[], -): Promise { - const playlist = await parsePlaylist(playlistFile); - const newPlaylist = playlist.filter( - (ps) => !songs.find((s) => s.id === ps.id), - ); - await writePlaylist(playlistFile, newPlaylist); - return parsePlaylist(playlistFile); -} - -export async function writePlaylist(playlistFile: PlaylistFile, songs: Song[]) { - const playlistsLocation = get(userSettings).playlistsLocation; - - const playlist: M3U = { - tracks: songs.map((s) => ({ - title: `${s.artist} - ${s.title}`, - path: s.path, - duration: s.fileInfo.duration, - })), - }; - - const m3u = write(playlist); - - // Write to file using fs, overwrite - await writeTextFile(playlistFile.path, m3u); -} diff --git a/src/data/PlaylistUtils.ts b/src/data/PlaylistUtils.ts new file mode 100644 index 00000000..59e120e6 --- /dev/null +++ b/src/data/PlaylistUtils.ts @@ -0,0 +1,444 @@ +import { + exists, + mkdir, + readDir, + readTextFile, + rename, + writeTextFile, +} from "@tauri-apps/plugin-fs"; +import md5 from "md5"; +import { get } from "svelte/store"; +import type { StaticPlaylistFile, Song, DynamicPlaylistFile } from "../App"; +import { db } from "./db"; +import { + selectedPlaylistFile, + userDynamicPlaylists, + userStaticPlaylists, + userSettings, +} from "./store"; +import { moveArrayElement } from "../utils/ArrayUtils"; +import { invoke } from "@tauri-apps/api/core"; +import { path } from "@tauri-apps/api"; +import YAML from "yaml"; +import Ajv from "ajv"; +import dynoPLSchema from "@zokugun/dynopl/lib/dynopl.schema.json"; +import type { Playlist as DynamicPlaylist } from "@zokugun/dynopl"; +import SmartQuery from "../lib/smart-query/Query"; +import { kebabCase } from "lodash-es"; + +interface M3UTrack { + duration: number; // Duration in seconds, -1 if unknown + title: string; // Track title and artist + path: string; // Path to the media file (local or URL) +} + +interface M3U { + tracks: M3UTrack[]; // List of tracks +} + +const VALID_DYNOPL_OPERATORS = { + contains: ["genre", "tags", "title"], + inTheRange: ["year"], + is: ["artist", "albumArtist", "composer", "originCountry", "year"], + gt: ["duration", "year"], +}; + +const AJV = new Ajv({ + allErrors: true, + allowUnionTypes: true, + useDefaults: true, + removeAdditional: true, +}); +const validateDynoPLSchema = AJV.compile(dynoPLSchema); + +const NUMBER_FIELDS = ["duration", "trackNumber", "compilation", "year"]; + +export async function addSongsToStaticPlaylist( + playlistFile: StaticPlaylistFile, + songs: Song[], +) { + const existingSongs = await loadStaticPlaylist(playlistFile); + await writeStaticPlaylist(playlistFile, [...existingSongs, ...songs]); +} + +export function composeComparator( + key: string, + descending: boolean, + eqComparator, +) { + if (NUMBER_FIELDS.includes(key)) { + return (a, b) => { + const d = a[key] - b[key]; + + if (d === 0) { + return eqComparator(a, b); + } else if (descending) { + return -d; + } else { + return d; + } + }; + } else { + return (a, b) => { + const d = a[key].localeCompare(b[key]); + + if (d === 0) { + return eqComparator(a, b); + } else if (descending) { + return -d; + } else { + return d; + } + }; + } +} + +export async function createNewStaticPlaylistFile( + title: string, + songs: Song[] = [], +) { + await writeStaticPlaylist( + { + path: await path.join( + get(userSettings).playlistsLocation, + `${title}.m3u`, + ), + title, + }, + songs, + ); + await scanPlaylists(); +} + +export async function deletePlaylistFile(playlistFile: StaticPlaylistFile) { + await invoke("delete_files", { + event: { + files: [playlistFile.path], + }, + }); + await scanPlaylists(); +} + +export async function deleteSongsFromStaticPlaylist( + playlistFile: StaticPlaylistFile, + songs: Song[], +): Promise { + const playlist = await loadStaticPlaylist(playlistFile); + const newPlaylist = playlist.filter( + (ps) => !songs.find((s) => s.id === ps.id), + ); + await writeStaticPlaylist(playlistFile, newPlaylist); + return loadStaticPlaylist(playlistFile); +} + +export function getComparator(key: string, descending: boolean) { + if (NUMBER_FIELDS.includes(key)) { + if (descending) { + return (a, b) => b[key] - a[key]; + } else { + return (a, b) => a[key] - b[key]; + } + } else { + if (descending) { + return (a, b) => b[key].localeCompare(a[key]); + } else { + return (a, b) => a[key].localeCompare(b[key]); + } + } +} + +export async function insertSongsToStaticPlaylist( + playlistFile: StaticPlaylistFile, + songs: Song[], + index: number, +) { + const playlist = await loadStaticPlaylist(playlistFile); + const newPlaylist = [...playlist]; + newPlaylist.splice(index, 0, ...songs); + await writeStaticPlaylist(playlistFile, newPlaylist); +} + +export async function loadDynamicPlaylist( + filepath: string, +): Promise { + const ext = await path.extname(filepath); + + if (ext === "json" || ext === "jdp") { + const content = await readTextFile(filepath); + const parsed = JSON.parse(content); + + if (validateDynoPL(parsed)) { + let title = parsed.name; + if (typeof title !== "string" || title.length === 0) { + title = delExt(await path.basename(filepath)); + + if (ext === "json") { + title = delExt(title); + } + } + + return { + title, + path: filepath, + schema: parsed, + }; + } + } else if (ext === "yaml" || ext === "yml" || ext === "ydp") { + const content = await readTextFile(filepath); + const parsed = YAML.parse(content); + + if (validateDynoPL(parsed)) { + let title = parsed.name; + if (typeof title !== "string" || title.length === 0) { + title = delExt(await path.basename(filepath)); + + if (ext === "yaml" || ext === "yml") { + title = delExt(title); + } + } + + return { + title, + path: filepath, + schema: parsed, + }; + } + } + + return null; +} + +export async function loadStaticPlaylist( + playlistFile: StaticPlaylistFile, +): Promise { + const fileContents = await readTextFile(playlistFile.path); + let playlist: M3U; + try { + playlist = parseStaticPlaylist(fileContents); + } catch (e) { + console.error(e); + return []; + } + + console.log("M3U: parsed playlist", playlist); + + // Do a db query to get all those songs + const songIds = playlist.tracks.map((c) => md5(c.path)); + try { + const songs = await db.songs.bulkGet(songIds); + console.log("M3U: got songs", songs); + return songs; + } catch (e) { + console.error(e); + return []; + } +} + +export async function renameStaticPlaylist( + playlistFile: StaticPlaylistFile, + newTitle: string, +) { + console.log("renameStaticPlaylist", playlistFile, newTitle); + const newPath = playlistFile.path.replace(playlistFile.title, newTitle); + console.log(`Renaming ${playlistFile.path} to ${newPath}`); + await rename(playlistFile.path, newPath); + await scanPlaylists(); + selectedPlaylistFile.set({ + path: newPath, + title: newTitle, + }); +} + +export async function reorderSongsInStaticPlaylist( + playlistFile: StaticPlaylistFile, + fromIdx: number, + toIdx: number, +) { + let playlist = await loadStaticPlaylist(playlistFile); + playlist = moveArrayElement(playlist, fromIdx, toIdx); + await writeStaticPlaylist(playlistFile, playlist); +} + +export async function scanPlaylists() { + console.log("[M3U] Scanning playlists..."); + const playlistsLocation = get(userSettings).playlistsLocation; + // Read directory (or create if non existent) + const locationExists = await exists(playlistsLocation); + if (!locationExists) { + await mkdir(playlistsLocation); + } + + for (const smartQuery of await db.smartQueries.toArray()) { + const name = kebabCase(smartQuery.name); + const filepath = await path.join( + playlistsLocation, + `${name}.dynopl.yaml`, + ); + const schema = SmartQuery.toDynoPL(smartQuery); + const playlist: DynamicPlaylistFile = { + title: smartQuery.name, + path: filepath, + schema, + }; + + await writeDynamicPlaylist(playlist); + + await db.smartQueries.delete(smartQuery.id); + } + + const dynamicFiles: DynamicPlaylistFile[] = []; + const staticFiles: StaticPlaylistFile[] = []; + const entries = await readDir(playlistsLocation); + + for (const entry of entries) { + if (entry.isFile) { + if (entry.name.endsWith(".m3u")) { + staticFiles.push({ + title: delExt(entry.name), + path: await path.join(playlistsLocation, entry.name), + }); + } else if ( + entry.name.endsWith(".dynopl.yaml") || + entry.name.endsWith(".dynopl.yml") || + entry.name.endsWith(".dynopl.json") || + entry.name.endsWith(".ydp") || + entry.name.endsWith(".jdp") + ) { + const playlist = await loadDynamicPlaylist( + await path.join(playlistsLocation, entry.name), + ); + + if (playlist) { + dynamicFiles.push(playlist); + } + } + } + } + + userDynamicPlaylists.set( + dynamicFiles.sort((a, b) => a.title.localeCompare(b.title)), + ); + userStaticPlaylists.set( + staticFiles.sort((a, b) => a.title.localeCompare(b.title)), + ); + + console.log("[DynoPL]: Playlists: ", dynamicFiles); + console.log("[M3U]: Playlists: ", staticFiles); +} + +export async function writeDynamicPlaylist(playlistFile: DynamicPlaylistFile) { + playlistFile.schema.name = playlistFile.title; + + let content = null; + + const ext = await path.extname(playlistFile.path); + + if (ext === "json" || ext === "jdp") { + content = JSON.stringify(playlistFile.schema, null, "\t"); + } else if (ext === "yaml" || ext === "yml" || ext === "ydp") { + content = YAML.stringify(playlistFile.schema); + } + + if (content) { + // Write to file using fs, overwrite + await writeTextFile(playlistFile.path, content); + } +} + +export async function writeStaticPlaylist( + playlistFile: StaticPlaylistFile, + songs: Song[], +) { + const playlist: M3U = { + tracks: songs.map((s) => ({ + title: `${s.artist} - ${s.title}`, + path: s.path, + duration: s.fileInfo.duration, + })), + }; + + const m3u = toStaticPlaylist(playlist); + + // Write to file using fs, overwrite + await writeTextFile(playlistFile.path, m3u); +} + +function delExt(filename: string): string { + const index = filename.lastIndexOf("."); + if (index > 0) { + return filename.substring(0, index); + } else { + return filename; + } +} + +function parseStaticPlaylist(contents: string): M3U { + const lines = contents.split(/\r?\n/).filter((line) => line.trim() !== ""); + const tracks: M3UTrack[] = []; + let currentTrack: Partial = {}; + + for (const line of lines) { + if (line.trim().startsWith("#EXTINF:")) { + // Parse EXTINF line + const [, duration, title] = + line.match(/#EXTINF:([+-]?\d+(?:\.\d+)?),(.+)/) || []; + currentTrack = { + duration: parseInt(duration, 10), + title: title.trim(), + }; + } else if (!line.trim().startsWith("#")) { + // Parse media file path + if (currentTrack) { + currentTrack.path = line.trim(); + tracks.push(currentTrack as M3UTrack); + currentTrack = {}; // Reset for the next track + } + } + } + + return { tracks }; +} + +/** + * Converts an M3U object into an M3U playlist string. + * @param m3u The M3U object to convert. + * @returns The M3U playlist as a string. + */ +function toStaticPlaylist(m3u: M3U): string { + const lines: string[] = ["#EXTM3U"]; + + for (const track of m3u.tracks) { + lines.push(`#EXTINF:${track.duration},${track.title}`); + lines.push(track.path); + } + + return lines.join("\n"); +} + +function validateDynoPL(data): boolean { + // if (!validateDynoPLSchema(data)) { + // console.log(validateDynoPLSchema.errors); + // return false; + // } + + if (!data.all) { + return false; + } + + for (var rule of data.all) { + const operator = Object.keys(rule)[0]; + const keys = VALID_DYNOPL_OPERATORS[operator]; + + if (!keys) { + return false; + } + + for (const key of Object.keys(rule[operator])) { + if (!keys.includes(key)) { + return false; + } + } + } + + return true; +} diff --git a/src/data/store.ts b/src/data/store.ts index 01c0ac49..c7652d7c 100644 --- a/src/data/store.ts +++ b/src/data/store.ts @@ -22,7 +22,7 @@ import type { IAFile, IAItem, ImportStatus, - PlaylistFile, + StaticPlaylistFile, PopupType, UiView, Song, @@ -31,12 +31,13 @@ import type { UserSettings, WaveformPlayerState, PlayingSong, + DynamicPlaylistFile, } from "src/App"; import { derived, get, writable, type Writable } from "svelte/store"; import { locale } from "../i18n/i18n-svelte"; import { i18nString } from "../i18n/i18n-util"; import SmartQuery from "../lib/smart-query/Query"; -import { scanPlaylists } from "./M3UUtils"; +import { scanPlaylists } from "./PlaylistUtils"; import { liveQuery } from "dexie"; import { db } from "./db"; import { persistentWritable } from "./storeUtils"; @@ -159,7 +160,7 @@ export const playbackSpeed = writable(1.0); export const isFullScreenVisualiser = writable(false); // Playlists (populated from folder) -export const userPlaylists: Writable = writable([]); +export const userStaticPlaylists: Writable = writable([]); export const toDeletePlaylist = liveQuery(async () => { try { if (!db.hasBeenClosed()) { @@ -172,6 +173,9 @@ export const toDeletePlaylist = liveQuery(async () => { } return null; }); +export const userDynamicPlaylists: Writable = writable( + [], +); export const popupOpen: Writable = writable(null); export const uiView: Writable = writable("library"); @@ -232,7 +236,9 @@ export const isTagOrCondition = writable(false); // Playlists export type DragSource = "Library" | "Player" | "Queue" | "Sidebar"; export type SongOrigin = "Album" | "Playlist" | "SmartPlaylist"; -export const selectedPlaylistFile: Writable = writable(null); +export const selectedPlaylistFile: Writable< + StaticPlaylistFile | DynamicPlaylistFile +> = writable(null); export const draggedOrigin: Writable = writable(null); export const draggedSongs: Writable = writable([]); export const draggedSource: Writable = writable(null); diff --git a/src/data/storeHelper.ts b/src/data/storeHelper.ts index 997b11f7..fcf0ad16 100644 --- a/src/data/storeHelper.ts +++ b/src/data/storeHelper.ts @@ -1,5 +1,5 @@ import { remove } from "lodash-es"; -import type { Album, PlaylistFile, Song } from "src/App"; +import type { Album, StaticPlaylistFile, Song } from "src/App"; import type SmartQuery from "src/lib/smart-query/Query"; import { get } from "svelte/store"; import AudioPlayer from "../lib/player/AudioPlayer"; @@ -54,7 +54,7 @@ export function setDraggedAlbum( } export function setDraggedPlaylist( - playlist: PlaylistFile, + playlist: StaticPlaylistFile, songs: Song[], source: DragSource, ) { diff --git a/src/lib/library/CanvasLibrary.svelte b/src/lib/library/CanvasLibrary.svelte index 24233ff8..d422682c 100644 --- a/src/lib/library/CanvasLibrary.svelte +++ b/src/lib/library/CanvasLibrary.svelte @@ -27,10 +27,10 @@ import { Context } from "konva/lib/Context"; import toast from "svelte-french-toast"; import { - addSongsToPlaylist, - insertSongsToPlaylist, - reorderSongsInPlaylist, - } from "../../data/M3UUtils"; + addSongsToStaticPlaylist, + insertSongsToStaticPlaylist, + reorderSongsInStaticPlaylist, + } from "../../data/PlaylistUtils"; import { arrowFocus, current, @@ -1179,7 +1179,7 @@ resetDraggedSongs(); console.log("[Library] Adding to playlist: ", playlist); - await addSongsToPlaylist(playlist, songs); + await addSongsToStaticPlaylist(playlist, songs); toast.success( `${ songs.length > 1 ? songs.length + " songs" : songs[0].title @@ -1203,7 +1203,7 @@ resetDraggedSongs(); console.log("[Library] Insert to playlist: ", playlist); - await insertSongsToPlaylist(playlist, songs, idx); + await insertSongsToStaticPlaylist(playlist, songs, idx); toast.success( `${ songs.length > 1 ? songs.length + " songs" : songs[0].title @@ -1225,7 +1225,7 @@ toast.error($LL.library.orderDisabledHint()); } - await reorderSongsInPlaylist( + await reorderSongsInStaticPlaylist( $selectedPlaylistFile, draggingSongIdx, idx, diff --git a/src/lib/library/PlaylistHeader.svelte b/src/lib/library/PlaylistHeader.svelte index 6838d365..bd167c64 100644 --- a/src/lib/library/PlaylistHeader.svelte +++ b/src/lib/library/PlaylistHeader.svelte @@ -1,7 +1,10 @@ @@ -110,7 +148,9 @@ size={15} color={$uiView === "smart-query" ? "#45fffcf3" : "currentColor"} /> {selectedQuery?.name} + > {selectedQuery + ? selectedQuery.name + : $selectedPlaylistFile?.title} {/if} {#if durationText} @@ -122,7 +162,7 @@ {#if !$isSmartQueryBuilderOpen} - {#if $selectedSmartQuery?.startsWith("~usq:")} + {#if $selectedPlaylistFile} { - const queries = await db.smartQueries.toArray(); - return queries.sort((a, b) => a.name.localeCompare(b.name)); - }); - function onClickSmartPlaylist(e, smartQuery, reset) { $selectedPlaylistFile = null; $uiView = "smart-query"; @@ -656,12 +656,12 @@ $isSidebarFloating = false; } - function onClickSmartPlaylistOptions(e, query: SavedSmartQuery) { - menuX = e.clientX; - menuY = e.clientY; - smartPlaylistToEdit = query; - showSmartPlaylistMenu = !showSmartPlaylistMenu; - } + // function onClickSmartPlaylistOptions(e, query: SavedSmartQuery) { + // menuX = e.clientX; + // menuY = e.clientY; + // smartPlaylistToEdit = query; + // showSmartPlaylistMenu = !showSmartPlaylistMenu; + // } async function playSmartPlaylist(queryId: string) { const query = queryId.startsWith("~usq:") @@ -672,12 +672,12 @@ setQueue(songs, 0); } - async function onRenameSmartPlaylist(query: SavedSmartQuery) { - query.name = updatedSmartPlaylistName; - await db.smartQueries.put(query); - updatedSmartPlaylistName = ""; - isRenamingSmartPlaylist = false; - } + // async function onRenameSmartPlaylist(query: SavedSmartQuery) { + // query.name = updatedSmartPlaylistName; + // await db.smartQueries.put(query); + // updatedSmartPlaylistName = ""; + // isRenamingSmartPlaylist = false; + // } async function deleteSmartPlaylist() { if (!isConfirmingSmartPlaylistDelete) { @@ -731,6 +731,69 @@ activeSmartPlaylist = null; } + function onClickDynamicPlaylist(e, playlist: DynamicPlaylistFile) { + $uiView = "smart-query"; + // Opening a playlist will reset the query + $query = { + ...$query, + orderBy: "none", + reverse: false, + query: "", + }; + $selectedPlaylistFile = playlist; + $selectedSmartQuery = null; + $isSidebarFloating = false; + + activePlaylist = null; + } + + function onClickDynamicPlaylistOptions(e, playlist: DynamicPlaylistFile) { + menuX = e.clientX; + menuY = e.clientY; + playlistToEdit = playlist; + showPlaylistMenu = !showPlaylistMenu; + } + + function onMouseDownDynamicPlaylist(playlist: DynamicPlaylistFile) { + if (!$draggedSongs.length) { + activePlaylist = playlist; + } + } + + function onMouseEnterDynamicPlaylist(playlist: StaticPlaylistFile) { + hoveringOverPlaylistId = playlist?.path; + } + + async function onMouseLeaveDynamicPlaylist(e) { + draggingOverPlaylist = null; + hoveringOverPlaylistId = null; + + if (activePlaylist) { + // const songs = await loadStaticPlaylist(activePlaylist); + // setDraggedPlaylist(activePlaylist, songs, "Sidebar"); + // activePlaylist = null; + } + } + + async function onMouseUpDynamicPlaylist(playlist: DynamicPlaylistFile) { + if ($draggedOrigin === "Playlist" && $draggedTitle === playlist.title) { + resetDraggedSongs(); + } else if ($draggedSongs.length) { + resetDraggedSongs(); + } + + activePlaylist = null; + } + + async function onRenameDynamicPlaylist(playlist: DynamicPlaylistFile) { + updatedPlaylistName = ""; + isRenamingPlaylist = false; + } + + async function playDynamicPlaylist(playlist: DynamicPlaylistFile) { + console.log(playlist); + } + async function favouriteCurrentSong() { if (!$current.song) return; $current.song.isFavourite = !$current.song.isFavourite; @@ -1127,11 +1190,11 @@ class:selected={$uiView === "library" && !$selectedPlaylistFile} on:click={() => { + $uiView = "library"; $isSmartQueryBuilderOpen = false; $selectedPlaylistFile = null; $selectedSmartQuery = null; $query.orderBy = $query.libraryOrderBy; - $uiView = "library"; $isSidebarFloating = false; }} > @@ -1233,7 +1296,7 @@ {#if isPlaylistsExpanded}
- {#each $userPlaylists as playlist (playlist.path)} + {#each $userStaticPlaylists as playlist (playlist.path)}
- onClickPlaylistOptions(e, playlist)} + onClickStaticPlaylistOptions( + e, + playlist, + )} on:click={(e) => - onClickPlaylist(e, playlist)} + onClickStaticPlaylist(e, playlist)} on:dblclick={() => - playPlaylist(playlist)} + playStaticPlaylist(playlist)} on:mouseenter|preventDefault|stopPropagation={() => - onMouseEnterPlaylist(playlist)} - on:mouseleave|preventDefault|stopPropagation={onMouseLeavePlaylist} + onMouseEnterStaticPlaylist( + playlist, + )} + on:mouseleave|preventDefault|stopPropagation={onMouseLeaveStaticPlaylist} on:mousedown|preventDefault|stopPropagation={() => - onMouseDownPlaylist(playlist)} + onMouseDownStaticPlaylist(playlist)} on:mouseup|preventDefault|stopPropagation={() => - onMouseUpPlaylist(playlist)} + onMouseUpStaticPlaylist(playlist)} > {#if isRenamingPlaylist && playlistToEdit.title === playlist.title} { - onRenamePlaylist(playlist); + onRenameStaticPlaylist( + playlist, + ); }} fullWidth minimal @@ -1293,7 +1363,7 @@ color="#898989" size={14} onClick={(e) => - onClickPlaylistOptions( + onClickStaticPlaylistOptions( e, playlist, )} @@ -1306,7 +1376,7 @@ @@ -1371,47 +1441,44 @@

{smartQuery.name}

{/each} - {#each $savedSmartQueries as query (query.id)} + {#each $userDynamicPlaylists as playlist (playlist.path)}
- onClickSmartPlaylistOptions( + onClickDynamicPlaylistOptions( e, - query, + playlist, )} on:click={(e) => - onClickSmartPlaylist( - e, - `~usq:${query.id}`, - false, - )} + onClickDynamicPlaylist(e, playlist)} on:dblclick={() => - playSmartPlaylist( - `~usq:${query.id}`, - )} - on:mouseleave|preventDefault|stopPropagation={onMouseLeaveSmartPlaylist} + playDynamicPlaylist(playlist)} on:mouseenter|preventDefault|stopPropagation={() => - onMouseEnterSmartPlaylist(query.id)} + onMouseEnterDynamicPlaylist( + playlist, + )} + on:mouseleave|preventDefault|stopPropagation={onMouseLeaveDynamicPlaylist} on:mousedown|preventDefault|stopPropagation={() => - onMouseDownSmartPlaylist( - `~usq:${query.id}`, + onMouseDownDynamicPlaylist( + playlist, )} - on:mouseup|preventDefault|stopPropagation={onMouseUpSmartPlaylist} + on:mouseup|preventDefault|stopPropagation={() => + onMouseUpDynamicPlaylist(playlist)} > - {#if isRenamingSmartPlaylist && smartPlaylistToEdit.id === query.id} + {#if isRenamingPlaylist && playlistToEdit.title === playlist.title} { - onRenameSmartPlaylist( - query, + onRenameDynamicPlaylist( + playlist, ); }} fullWidth @@ -1419,46 +1486,37 @@ autoFocus /> {:else} -

{query.name}

+

{playlist.title}

{/if} - {#if isRenamingSmartPlaylist && smartPlaylistToEdit.id === query.id} + {#if isRenamingPlaylist && playlistToEdit.title === playlist.title} { e.stopPropagation(); - isRenamingSmartPlaylist = false; + isRenamingPlaylist = false; }} /> {:else}
- onClickSmartPlaylistOptions( + onClickDynamicPlaylistOptions( e, - query, + playlist, )} />
{/if}
{/each} - -
{/if} {#if $userSettings.isArtistsToolkitEnabled} @@ -1559,7 +1617,7 @@ > { - playPlaylist(playlistToEdit); + playStaticPlaylist(playlistToEdit); showPlaylistMenu = false; }} text="Play playlist" diff --git a/src/lib/smart-query/Query.ts b/src/lib/smart-query/Query.ts index 98acdc2d..9b4e4cbf 100644 --- a/src/lib/smart-query/Query.ts +++ b/src/lib/smart-query/Query.ts @@ -1,8 +1,16 @@ -import type { Song } from "src/App"; +import type { DynamicPlaylistFile, Song } from "src/App"; import { db } from "../../data/db"; import { isSmartQueryValid, smartQueryUpdater } from "../../data/store"; -import type { SavedSmartQuery } from "./QueryPart"; +import type { + FieldKey, + QueryPartStructWithValues, + SavedSmartQuery, +} from "./QueryPart"; import { UserQueryPart } from "./UserQueryPart"; +import { snakeCase, upperFirst } from "lodash-es"; +import type { Playlist as DynamicPlaylist } from "@zokugun/dynopl"; +import { v7 as uuidv7 } from "uuid"; + export default class SmartQuery { parts: UserQueryPart[] = []; @@ -10,6 +18,147 @@ export default class SmartQuery { name: string = null; userInput: string = ""; + static fromDynoPL(playlist: DynamicPlaylistFile): void { + const queryParts: QueryPartStructWithValues[] = []; + + for (const data of playlist.schema.all) { + if (data.contains) { + const key = Object.keys(data.gt)[0]; + const pathKey = + key === "title" + ? "titleContains" + : `contains${upperFirst(key)}`; + const valueKey = key === "title" ? "text" : key; + const part: QueryPartStructWithValues = { + dataType: "song", + fieldKey: key as FieldKey, + comparison: "contains", + description: `smartPlaylists.builder.parts.${pathKey}.title`, + example: `smartPlaylists.builder.parts.${pathKey}.example`, + prompt: + key === "title" + ? "title contains {text}" + : key === "genre" + ? "contains genre {genre}" + : "contains tag {tags}", + name: + key === "title" + ? "title-contains" + : key === "genre" + ? "contains-genre" + : "contains-tag", + inputRequired: { + [valueKey]: { + defaultVal: "", + isFieldKey: true, + isRequired: true, + type: "string", + }, + }, + values: { + [valueKey]: data.contains[key], + }, + }; + + queryParts.push(part); + } else if (data.inTheRange) { + const part: QueryPartStructWithValues = { + dataType: "song", + fieldKey: "year", + comparison: "is-between", + description: + "smartPlaylists.builder.parts.releasedBetween.title", + example: + "smartPlaylists.builder.parts.releasedBetween.example", + prompt: "released between {startYear} and {endYear}", + name: "released-between", + inputRequired: { + startYear: { + defaultVal: 1940, + isFieldKey: false, + isRequired: true, + type: "number", + }, + endYear: { + defaultVal: 1960, + isFieldKey: false, + isRequired: true, + type: "number", + }, + }, + values: { + startYear: data.inTheRange[0], + endYear: data.inTheRange[1], + }, + }; + + queryParts.push(part); + } else if (data.is) { + const key = Object.keys(data.is)[0]; + const ckey = key === "artistCountry" ? "originCountry" : key; + const byKey = `by${upperFirst(ckey)}`; + const part: QueryPartStructWithValues = { + comparison: "is-equal", + dataType: "song", + description: `smartPlaylists.builder.parts.${byKey}.title`, + example: `smartPlaylists.builder.parts.${byKey}.example`, + fieldKey: ckey as FieldKey, + inputRequired: { + [ckey]: { + defaultVal: "", + isFieldKey: true, + isRequired: true, + type: "string", + }, + }, + name: `by {${ckey}}`, + prompt: `by ${snakeCase(ckey).replaceAll("_", " ")} {${ckey}}`, + values: { + [ckey]: data.is[key], + }, + }; + + queryParts.push(part); + } else if (data.gt) { + const key = Object.keys(data.gt)[0]; + const pathKey = key === "year" ? "releasedAfter" : "longerThan"; + const valueKey = key === "year" ? "startYear" : "minutes"; + const part: QueryPartStructWithValues = { + dataType: "song", + fieldKey: key as FieldKey, + comparison: "is-greater-than", + description: `smartPlaylists.builder.parts.${pathKey}.title`, + example: `smartPlaylists.builder.parts.${pathKey}.example`, + prompt: + key === "year" + ? "released after {startYear}" + : "longer than {minutes}", + name: key === "year" ? "released-after" : "longer-than", + inputRequired: { + [valueKey]: { + defaultVal: key === "year" ? 1940 : 0, + isFieldKey: false, + isRequired: true, + type: "number", + }, + }, + values: { + [valueKey]: data.gt[key], + }, + }; + + queryParts.push(part); + } + } + + const query = new SmartQuery({ + name: playlist.title, + queryParts, + }); + + playlist.query = query; + } + static async loadWithUQI(queryId: `~usq:${string}`): Promise { // Run the query from the user-built blocks const queryName = Number(queryId.substring(5)); @@ -17,6 +166,12 @@ export default class SmartQuery { return new SmartQuery(savedQuery); } + static toDynoPL(savedQuery: SavedSmartQuery): DynamicPlaylist { + const query = new SmartQuery(savedQuery); + + return query.toDynoPL(); + } + constructor(savedQuery?: SavedSmartQuery) { if (savedQuery) { this.id = savedQuery.id; @@ -90,37 +245,55 @@ export default class SmartQuery { }, Promise.resolve(db.songs.toArray())); } - async save() { - if (this.id) { - await db.smartQueries.update(this.id, { - name: this.name, - queryParts: this.parts.map((p) => ({ - ...p.queryPart, - values: Object.entries(p.userInputs).reduce( - (obj, current) => { - obj[current[0]] = current[1].value; - return obj; - }, - {}, - ), - })), - }); + toDynoPL(playlist?: DynamicPlaylist): DynamicPlaylist { + if (!playlist) { + playlist = { + id: uuidv7(), + }; + } - return this.id; - } else { - return await db.smartQueries.put({ - name: this.name, - queryParts: this.parts.map((p) => ({ - ...p.queryPart, - values: Object.entries(p.userInputs).reduce( - (obj, current) => { - obj[current[0]] = current[1].value; - return obj; - }, - {}, - ), - })), - }); + playlist.all = []; + + for (const part of this.parts) { + const { fieldKey } = part.queryPart; + + if (part.queryPart.comparison === "contains") { + const inputKey = Object.keys(part.queryPart.inputRequired)[0]; + + playlist.all.push({ + contains: { + [fieldKey]: part.userInputs[inputKey].value, + }, + }); + } else if (part.queryPart.comparison === "is-between") { + playlist.all.push({ + inTheRange: { + [fieldKey]: [ + part.userInputs.startYear.value, + part.userInputs.endYear.value, + ], + }, + }); + } else if (part.queryPart.comparison === "is-equal") { + const operand = + fieldKey === "originCountry" ? "artistCountry" : fieldKey; + + playlist.all.push({ + is: { + [operand]: part.userInputs[fieldKey].value, + }, + }); + } else if (part.queryPart.comparison === "is-greater-than") { + const inputKey = Object.keys(part.queryPart.inputRequired)[0]; + + playlist.all.push({ + gt: { + [fieldKey]: part.userInputs[inputKey].value, + }, + }); + } } + + return playlist; } } diff --git a/src/lib/smart-query/QueryPart.ts b/src/lib/smart-query/QueryPart.ts index e5404730..59ceecd8 100644 --- a/src/lib/smart-query/QueryPart.ts +++ b/src/lib/smart-query/QueryPart.ts @@ -4,7 +4,7 @@ import type { Comparison, DataType } from "src/App"; * At the moment we just create query parts per field. * This can expand to more complex queries */ -type FieldKey = +export type FieldKey = | "title" | "artist" | "album" diff --git a/src/lib/views/CanvasLibraryView.svelte b/src/lib/views/CanvasLibraryView.svelte index 30978edb..77b377ca 100644 --- a/src/lib/views/CanvasLibraryView.svelte +++ b/src/lib/views/CanvasLibraryView.svelte @@ -1,9 +1,13 @@