From 876d76bc1f009c29169c259352948246836e9315 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Wed, 8 Nov 2023 20:38:40 +0000 Subject: [PATCH] Frontend created and rewritten a few times, with some backend fixes (#1) The software is fully operational. Reviewed-on: https://git.zi.fi/Vasanko/cista-storage/pulls/1 --- .gitignore | 1 + cista-front/.gitignore | 5 + cista-front/index.html | 23 +- cista-front/package-lock.json | 1458 ----------------- cista-front/package.json | 50 +- cista-front/src/App.vue | 180 +- cista-front/src/assets/base.css | 86 - cista-front/src/assets/logo.svg | 1 - cista-front/src/assets/main.css | 285 +++- cista-front/src/assets/svg/add-file.svg | 1 + cista-front/src/assets/svg/add-folder.svg | 1 + cista-front/src/assets/svg/arrow.svg | 1 + cista-front/src/assets/svg/arrows-h.svg | 1 + cista-front/src/assets/svg/arrows-v.svg | 1 + cista-front/src/assets/svg/check.svg | 1 + cista-front/src/assets/svg/code.svg | 1 + cista-front/src/assets/svg/cog.svg | 1 + cista-front/src/assets/svg/copy.svg | 1 + cista-front/src/assets/svg/create-file.svg | 1 + cista-front/src/assets/svg/create-folder.svg | 1 + cista-front/src/assets/svg/cross.svg | 1 + cista-front/src/assets/svg/disk.svg | 1 + cista-front/src/assets/svg/download.svg | 1 + cista-front/src/assets/svg/exclamation.svg | 1 + cista-front/src/assets/svg/eye.svg | 1 + cista-front/src/assets/svg/find.svg | 1 + cista-front/src/assets/svg/fullscreen.svg | 1 + cista-front/src/assets/svg/github.svg | 1 + cista-front/src/assets/svg/home.svg | 1 + cista-front/src/assets/svg/info.svg | 1 + cista-front/src/assets/svg/link.svg | 1 + cista-front/src/assets/svg/logo.svg | 1 + cista-front/src/assets/svg/loop.svg | 1 + cista-front/src/assets/svg/menu.svg | 1 + cista-front/src/assets/svg/next.svg | 1 + cista-front/src/assets/svg/open.svg | 1 + cista-front/src/assets/svg/paste.svg | 1 + cista-front/src/assets/svg/pause.svg | 1 + cista-front/src/assets/svg/pencil.svg | 1 + cista-front/src/assets/svg/play.svg | 1 + cista-front/src/assets/svg/plus.svg | 1 + cista-front/src/assets/svg/previous.svg | 1 + cista-front/src/assets/svg/reload.svg | 1 + cista-front/src/assets/svg/rename.svg | 1 + cista-front/src/assets/svg/scissors.svg | 1 + cista-front/src/assets/svg/shuffle.svg | 1 + cista-front/src/assets/svg/signin.svg | 1 + cista-front/src/assets/svg/signout.svg | 1 + cista-front/src/assets/svg/skip.svg | 1 + cista-front/src/assets/svg/spinner.svg | 1 + cista-front/src/assets/svg/stop.svg | 1 + cista-front/src/assets/svg/trash.svg | 1 + cista-front/src/assets/svg/triangle.svg | 1 + cista-front/src/assets/svg/unfullscreen.svg | 1 + cista-front/src/assets/svg/up-arrow.svg | 1 + cista-front/src/assets/svg/upload-cloud.svg | 1 + cista-front/src/assets/svg/user-cog.svg | 1 + cista-front/src/assets/svg/user.svg | 1 + cista-front/src/assets/svg/volume-high.svg | 1 + cista-front/src/assets/svg/volume-low.svg | 1 + cista-front/src/assets/svg/volume-medium.svg | 1 + cista-front/src/assets/svg/volume-mute.svg | 1 + cista-front/src/assets/svg/window-cross.svg | 1 + cista-front/src/assets/svg/window.svg | 1 + cista-front/src/assets/svg/wordwrap.svg | 1 + cista-front/src/assets/svg/zoomin.svg | 1 + cista-front/src/assets/svg/zoomout.svg | 1 + cista-front/src/components/BreadCrumb.vue | 154 ++ cista-front/src/components/FileExplorer.vue | 516 ++++++ .../src/components/FileRenameInput.vue | 59 + cista-front/src/components/FileViewer.vue | 52 + cista-front/src/components/HeaderMain.vue | 103 ++ cista-front/src/components/HeaderSelected.vue | 154 ++ cista-front/src/components/HelloWorld.vue | 41 - cista-front/src/components/LoginModal.vue | 101 ++ cista-front/src/components/ModalDialog.vue | 79 + .../src/components/NotificationLoading.vue | 27 + cista-front/src/components/SvgButton.vue | 42 + cista-front/src/components/TheWelcome.vue | 88 - cista-front/src/components/UploadButton.vue | 252 +++ cista-front/src/components/WelcomeItem.vue | 87 - .../src/components/icons/IconCommunity.vue | 7 - .../components/icons/IconDocumentation.vue | 7 - .../src/components/icons/IconEcosystem.vue | 7 - .../src/components/icons/IconSupport.vue | 7 - .../src/components/icons/IconTooling.vue | 19 - cista-front/src/main.ts | 15 +- cista-front/src/repositories/Client.ts | 35 + cista-front/src/repositories/Document.ts | 55 + cista-front/src/repositories/User.ts | 15 + cista-front/src/repositories/WS.ts | 133 ++ cista-front/src/router/index.ts | 20 +- cista-front/src/stores/counter.ts | 12 - cista-front/src/stores/documents.ts | 160 ++ cista-front/src/utils/index.ts | 96 ++ cista-front/src/views/AboutView.vue | 15 - cista-front/src/views/ExplorerView.vue | 58 + cista-front/src/views/HomeView.vue | 9 - cista-front/tsconfig.app.json | 2 + cista-front/tsconfig.json | 3 + cista-front/tsconfig.vitest.json | 8 + cista-front/vite.config.ts | 34 + cista/__init__.py | 0 cista/__main__.py | 6 +- cista/api.py | 42 +- cista/app.py | 195 ++- cista/auth.py | 16 +- cista/config.py | 3 +- cista/droppy.py | 6 +- cista/fileio.py | 4 +- cista/protocol.py | 37 +- cista/serve.py | 27 +- cista/session.py | 0 cista/util/__init__.py | 0 cista/util/apphelpers.py | 3 +- cista/util/asynclink.py | 3 +- cista/util/filename.py | 5 +- cista/util/lrucache.py | 2 +- cista/watching.py | 77 +- cista/wwwroot/assets/AboutView-4d995ba2.css | 1 - cista/wwwroot/assets/AboutView-ba1efa64.js | 1 - cista/wwwroot/assets/index-689b26c8.js | 9 - cista/wwwroot/assets/index-9f680dd7.css | 1 - cista/wwwroot/assets/logo-277e0e97.svg | 1 - cista/wwwroot/favicon.ico | Bin 4286 -> 0 bytes cista/wwwroot/index.html | 15 - cista/wwwroot/old-index.html | 241 --- pyproject.toml | 45 +- tests/test_control.py | 3 +- 129 files changed, 3027 insertions(+), 2335 deletions(-) delete mode 100644 cista-front/package-lock.json delete mode 100644 cista-front/src/assets/base.css delete mode 100644 cista-front/src/assets/logo.svg create mode 100644 cista-front/src/assets/svg/add-file.svg create mode 100644 cista-front/src/assets/svg/add-folder.svg create mode 100644 cista-front/src/assets/svg/arrow.svg create mode 100644 cista-front/src/assets/svg/arrows-h.svg create mode 100644 cista-front/src/assets/svg/arrows-v.svg create mode 100644 cista-front/src/assets/svg/check.svg create mode 100644 cista-front/src/assets/svg/code.svg create mode 100644 cista-front/src/assets/svg/cog.svg create mode 100644 cista-front/src/assets/svg/copy.svg create mode 100644 cista-front/src/assets/svg/create-file.svg create mode 100644 cista-front/src/assets/svg/create-folder.svg create mode 100644 cista-front/src/assets/svg/cross.svg create mode 100644 cista-front/src/assets/svg/disk.svg create mode 100644 cista-front/src/assets/svg/download.svg create mode 100644 cista-front/src/assets/svg/exclamation.svg create mode 100644 cista-front/src/assets/svg/eye.svg create mode 100644 cista-front/src/assets/svg/find.svg create mode 100644 cista-front/src/assets/svg/fullscreen.svg create mode 100644 cista-front/src/assets/svg/github.svg create mode 100644 cista-front/src/assets/svg/home.svg create mode 100644 cista-front/src/assets/svg/info.svg create mode 100644 cista-front/src/assets/svg/link.svg create mode 100644 cista-front/src/assets/svg/logo.svg create mode 100644 cista-front/src/assets/svg/loop.svg create mode 100644 cista-front/src/assets/svg/menu.svg create mode 100644 cista-front/src/assets/svg/next.svg create mode 100644 cista-front/src/assets/svg/open.svg create mode 100644 cista-front/src/assets/svg/paste.svg create mode 100644 cista-front/src/assets/svg/pause.svg create mode 100644 cista-front/src/assets/svg/pencil.svg create mode 100644 cista-front/src/assets/svg/play.svg create mode 100644 cista-front/src/assets/svg/plus.svg create mode 100644 cista-front/src/assets/svg/previous.svg create mode 100644 cista-front/src/assets/svg/reload.svg create mode 100644 cista-front/src/assets/svg/rename.svg create mode 100644 cista-front/src/assets/svg/scissors.svg create mode 100644 cista-front/src/assets/svg/shuffle.svg create mode 100644 cista-front/src/assets/svg/signin.svg create mode 100644 cista-front/src/assets/svg/signout.svg create mode 100644 cista-front/src/assets/svg/skip.svg create mode 100644 cista-front/src/assets/svg/spinner.svg create mode 100644 cista-front/src/assets/svg/stop.svg create mode 100644 cista-front/src/assets/svg/trash.svg create mode 100644 cista-front/src/assets/svg/triangle.svg create mode 100644 cista-front/src/assets/svg/unfullscreen.svg create mode 100644 cista-front/src/assets/svg/up-arrow.svg create mode 100644 cista-front/src/assets/svg/upload-cloud.svg create mode 100644 cista-front/src/assets/svg/user-cog.svg create mode 100644 cista-front/src/assets/svg/user.svg create mode 100644 cista-front/src/assets/svg/volume-high.svg create mode 100644 cista-front/src/assets/svg/volume-low.svg create mode 100644 cista-front/src/assets/svg/volume-medium.svg create mode 100644 cista-front/src/assets/svg/volume-mute.svg create mode 100644 cista-front/src/assets/svg/window-cross.svg create mode 100644 cista-front/src/assets/svg/window.svg create mode 100644 cista-front/src/assets/svg/wordwrap.svg create mode 100644 cista-front/src/assets/svg/zoomin.svg create mode 100644 cista-front/src/assets/svg/zoomout.svg create mode 100644 cista-front/src/components/BreadCrumb.vue create mode 100644 cista-front/src/components/FileExplorer.vue create mode 100644 cista-front/src/components/FileRenameInput.vue create mode 100644 cista-front/src/components/FileViewer.vue create mode 100644 cista-front/src/components/HeaderMain.vue create mode 100644 cista-front/src/components/HeaderSelected.vue delete mode 100644 cista-front/src/components/HelloWorld.vue create mode 100644 cista-front/src/components/LoginModal.vue create mode 100644 cista-front/src/components/ModalDialog.vue create mode 100644 cista-front/src/components/NotificationLoading.vue create mode 100644 cista-front/src/components/SvgButton.vue delete mode 100644 cista-front/src/components/TheWelcome.vue create mode 100644 cista-front/src/components/UploadButton.vue delete mode 100644 cista-front/src/components/WelcomeItem.vue delete mode 100644 cista-front/src/components/icons/IconCommunity.vue delete mode 100644 cista-front/src/components/icons/IconDocumentation.vue delete mode 100644 cista-front/src/components/icons/IconEcosystem.vue delete mode 100644 cista-front/src/components/icons/IconSupport.vue delete mode 100644 cista-front/src/components/icons/IconTooling.vue create mode 100644 cista-front/src/repositories/Client.ts create mode 100644 cista-front/src/repositories/Document.ts create mode 100644 cista-front/src/repositories/User.ts create mode 100644 cista-front/src/repositories/WS.ts delete mode 100644 cista-front/src/stores/counter.ts create mode 100644 cista-front/src/stores/documents.ts create mode 100644 cista-front/src/utils/index.ts delete mode 100644 cista-front/src/views/AboutView.vue create mode 100644 cista-front/src/views/ExplorerView.vue delete mode 100644 cista-front/src/views/HomeView.vue create mode 100644 cista-front/tsconfig.vitest.json mode change 100755 => 100644 cista/__init__.py mode change 100755 => 100644 cista/__main__.py mode change 100755 => 100644 cista/app.py mode change 100755 => 100644 cista/auth.py mode change 100755 => 100644 cista/config.py mode change 100755 => 100644 cista/droppy.py mode change 100755 => 100644 cista/fileio.py mode change 100755 => 100644 cista/protocol.py mode change 100755 => 100644 cista/serve.py mode change 100755 => 100644 cista/session.py create mode 100644 cista/util/__init__.py mode change 100755 => 100644 cista/util/asynclink.py mode change 100755 => 100644 cista/util/lrucache.py mode change 100755 => 100644 cista/watching.py delete mode 100644 cista/wwwroot/assets/AboutView-4d995ba2.css delete mode 100644 cista/wwwroot/assets/AboutView-ba1efa64.js delete mode 100644 cista/wwwroot/assets/index-689b26c8.js delete mode 100644 cista/wwwroot/assets/index-9f680dd7.css delete mode 100644 cista/wwwroot/assets/logo-277e0e97.svg delete mode 100644 cista/wwwroot/favicon.ico delete mode 100644 cista/wwwroot/index.html delete mode 100755 cista/wwwroot/old-index.html diff --git a/.gitignore b/.gitignore index 831d0ea..02868b0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ __pycache__/ *.egg-info/ /cista/_version.py +/cista/wwwroot/* /dist diff --git a/cista-front/.gitignore b/cista-front/.gitignore index 38adffa..ed637e9 100644 --- a/cista-front/.gitignore +++ b/cista-front/.gitignore @@ -7,12 +7,17 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* +# No locking +package-lock.json +yarn.lock + node_modules .DS_Store dist dist-ssr coverage *.local +components.d.ts /cypress/videos/ /cypress/screenshots/ diff --git a/cista-front/index.html b/cista-front/index.html index a888544..34a5c8d 100644 --- a/cista-front/index.html +++ b/cista-front/index.html @@ -1,13 +1,12 @@ - - - - - - Vite App - - -
- - - + + +Cista + + + + + + + +
diff --git a/cista-front/package-lock.json b/cista-front/package-lock.json deleted file mode 100644 index 7ebdb94..0000000 --- a/cista-front/package-lock.json +++ /dev/null @@ -1,1458 +0,0 @@ -{ - "name": "cista-front", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cista-front", - "version": "0.0.0", - "dependencies": { - "pinia": "^2.1.7", - "vue": "^3.3.4", - "vue-router": "^4.2.5" - }, - "devDependencies": { - "@tsconfig/node18": "^18.2.2", - "@types/node": "^18.18.5", - "@vitejs/plugin-vue": "^4.4.0", - "@vue/tsconfig": "^0.4.0", - "npm-run-all2": "^6.1.1", - "typescript": "~5.2.0", - "vite": "^4.4.11", - "vue-tsc": "^1.8.19" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@tsconfig/node18": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.2.tgz", - "integrity": "sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.18.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.6.tgz", - "integrity": "sha512-wf3Vz+jCmOQ2HV1YUJuCWdL64adYxumkrxtc+H1VUQlnQI04+5HtH+qZCOE21lBE7gIrt+CwX2Wv8Acrw5Ak6w==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz", - "integrity": "sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==", - "dev": true - }, - "node_modules/@vitejs/plugin-vue": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz", - "integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==", - "dev": true, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.0.0", - "vue": "^3.2.25" - } - }, - "node_modules/@volar/language-core": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.4.tgz", - "integrity": "sha512-Na69qA6uwVIdA0rHuOc2W3pHtVQQO8hCNim7FOaKNpRJh0oAFnu5r9i7Oopo5C4cnELZkPNjTrbmpcCTiW+CMQ==", - "dev": true, - "dependencies": { - "@volar/source-map": "1.10.4" - } - }, - "node_modules/@volar/source-map": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.4.tgz", - "integrity": "sha512-RxZdUEL+pV8p+SMqnhVjzy5zpb1QRZTlcwSk4bdcBO7yOu4rtEWqDGahVCEj4CcXour+0yJUMrMczfSCpP9Uxg==", - "dev": true, - "dependencies": { - "muggle-string": "^0.3.1" - } - }, - "node_modules/@volar/typescript": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.4.tgz", - "integrity": "sha512-BCCUEBASBEMCrz7qmNSi2hBEWYsXD0doaktRKpmmhvb6XntM2sAWYu6gbyK/MluLDgluGLFiFRpWgobgzUqolg==", - "dev": true, - "dependencies": { - "@volar/language-core": "1.10.4" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.6.tgz", - "integrity": "sha512-2JNjemwaNwf+MkkatATVZi7oAH1Hx0B04DdPH3ZoZ8vKC1xZVP7nl4HIsk8XYd3r+/52sqqoz9TWzYc3yE9dqA==", - "dependencies": { - "@babel/parser": "^7.23.0", - "@vue/shared": "3.3.6", - "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.6.tgz", - "integrity": "sha512-1MxXcJYMHiTPexjLAJUkNs/Tw2eDf2tY3a0rL+LfuWyiKN2s6jvSwywH3PWD8bKICjfebX3GWx2Os8jkRDq3Ng==", - "dependencies": { - "@vue/compiler-core": "3.3.6", - "@vue/shared": "3.3.6" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.6.tgz", - "integrity": "sha512-/Kms6du2h1VrXFreuZmlvQej8B1zenBqIohP0690IUBkJjsFvJxY0crcvVRJ0UhMgSR9dewB+khdR1DfbpArJA==", - "dependencies": { - "@babel/parser": "^7.23.0", - "@vue/compiler-core": "3.3.6", - "@vue/compiler-dom": "3.3.6", - "@vue/compiler-ssr": "3.3.6", - "@vue/reactivity-transform": "3.3.6", - "@vue/shared": "3.3.6", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.5", - "postcss": "^8.4.31", - "source-map-js": "^1.0.2" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.6.tgz", - "integrity": "sha512-QTIHAfDCHhjXlYGkUg5KH7YwYtdUM1vcFl/FxFDlD6d0nXAmnjizka3HITp8DGudzHndv2PjKVS44vqqy0vP4w==", - "dependencies": { - "@vue/compiler-dom": "3.3.6", - "@vue/shared": "3.3.6" - } - }, - "node_modules/@vue/devtools-api": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz", - "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==" - }, - "node_modules/@vue/language-core": { - "version": "1.8.19", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.19.tgz", - "integrity": "sha512-nt3dodGs97UM6fnxeQBazO50yYCKBK53waFWB3qMbLmR6eL3aUryZgQtZoBe1pye17Wl8fs9HysV3si6xMgndQ==", - "dev": true, - "dependencies": { - "@volar/language-core": "~1.10.4", - "@volar/source-map": "~1.10.4", - "@vue/compiler-dom": "^3.3.0", - "@vue/reactivity": "^3.3.0", - "@vue/shared": "^3.3.0", - "minimatch": "^9.0.3", - "muggle-string": "^0.3.1", - "vue-template-compiler": "^2.7.14" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@vue/reactivity": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.6.tgz", - "integrity": "sha512-gtChAumfQz5lSy5jZXfyXbKrIYPf9XEOrIr6rxwVyeWVjFhJwmwPLtV6Yis+M9onzX++I5AVE9j+iPH60U+B8Q==", - "dependencies": { - "@vue/shared": "3.3.6" - } - }, - "node_modules/@vue/reactivity-transform": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.6.tgz", - "integrity": "sha512-RlJl4dHfeO7EuzU1iJOsrlqWyJfHTkJbvYz/IOJWqu8dlCNWtxWX377WI0VsbAgBizjwD+3ZjdnvSyyFW1YVng==", - "dependencies": { - "@babel/parser": "^7.23.0", - "@vue/compiler-core": "3.3.6", - "@vue/shared": "3.3.6", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.5" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.6.tgz", - "integrity": "sha512-qp7HTP1iw1UW2ZGJ8L3zpqlngrBKvLsDAcq5lA6JvEXHmpoEmjKju7ahM9W2p/h51h0OT5F2fGlP/gMhHOmbUA==", - "dependencies": { - "@vue/reactivity": "3.3.6", - "@vue/shared": "3.3.6" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.6.tgz", - "integrity": "sha512-AoX3Cp8NqMXjLbIG9YR6n/pPLWE9TiDdk6wTJHFnl2GpHzDFH1HLBC9wlqqQ7RlnvN3bVLpzPGAAH00SAtOxHg==", - "dependencies": { - "@vue/runtime-core": "3.3.6", - "@vue/shared": "3.3.6", - "csstype": "^3.1.2" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.6.tgz", - "integrity": "sha512-kgLoN43W4ERdZ6dpyy+gnk2ZHtcOaIr5Uc/WUP5DRwutgvluzu2pudsZGoD2b7AEJHByUVMa9k6Sho5lLRCykw==", - "dependencies": { - "@vue/compiler-ssr": "3.3.6", - "@vue/shared": "3.3.6" - }, - "peerDependencies": { - "vue": "3.3.6" - } - }, - "node_modules/@vue/shared": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.6.tgz", - "integrity": "sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==" - }, - "node_modules/@vue/tsconfig": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.4.0.tgz", - "integrity": "sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==", - "dev": true - }, - "node_modules/@vue/typescript": { - "version": "1.8.19", - "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.19.tgz", - "integrity": "sha512-k/SHeeQROUgqsxyHQ8Cs3Zz5TnX57p7BcBDVYR2E0c61QL2DJ2G8CsaBremmNGuGE6o1R5D50IHIxFmroMz8iw==", - "dev": true, - "dependencies": { - "@volar/typescript": "~1.10.4", - "@vue/language-core": "1.8.19" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" - }, - "node_modules/de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/hosted-git-info": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", - "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/muggle-string": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", - "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/normalize-package-data": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-run-all2": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.1.1.tgz", - "integrity": "sha512-lWLbkPZ5BSdXtN8lR+0rc8caKoPdymycpZksyDEC9MOBvfdwTXZ0uVhb7bMcGeXv2/BKtfQuo6Zn3zfc8rxNXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "cross-spawn": "^7.0.3", - "memorystream": "^0.3.1", - "minimatch": "^9.0.0", - "pidtree": "^0.6.0", - "read-pkg": "^8.0.0", - "shell-quote": "^1.7.3" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "npm-run-all2": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0", - "npm": ">= 8" - } - }, - "node_modules/parse-json": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.0.tgz", - "integrity": "sha512-ihtdrgbqdONYD156Ap6qTcaGcGdkdAxodO1wLqQ/j7HP1u2sFYppINiq4jyC8F+Nm+4fVufylCV00QmkTHkSUg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.21.4", - "error-ex": "^1.3.2", - "json-parse-even-better-errors": "^3.0.0", - "lines-and-columns": "^2.0.3", - "type-fest": "^3.8.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-json/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pinia": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", - "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", - "dependencies": { - "@vue/devtools-api": "^6.5.0", - "vue-demi": ">=0.14.5" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "@vue/composition-api": "^1.4.0", - "typescript": ">=4.4.4", - "vue": "^2.6.14 || ^3.3.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/pinia/node_modules/vue-demi": { - "version": "0.14.6", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", - "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/read-pkg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", - "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.1", - "normalize-package-data": "^6.0.0", - "parse-json": "^7.0.0", - "type-fest": "^4.2.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.5.0.tgz", - "integrity": "sha512-diLQivFzddJl4ylL3jxSkEc39Tpw7o1QeEHIPxVwryDK2lpB7Nqhzhuo6v5/Ls08Z0yPSAhsyAWlv1/H0ciNmw==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "devOptional": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vite": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", - "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", - "dev": true, - "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "@types/node": ">= 14", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vue": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.6.tgz", - "integrity": "sha512-jJIDETeWJnoY+gfn4ZtMPMS5KtbP4ax+CT4dcQFhTnWEk8xMupFyQ0JxL28nvT/M4+p4a0ptxaV2WY0LiIxvRg==", - "dependencies": { - "@vue/compiler-dom": "3.3.6", - "@vue/compiler-sfc": "3.3.6", - "@vue/runtime-dom": "3.3.6", - "@vue/server-renderer": "3.3.6", - "@vue/shared": "3.3.6" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/vue-router": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz", - "integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==", - "dependencies": { - "@vue/devtools-api": "^6.5.0" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "vue": "^3.2.0" - } - }, - "node_modules/vue-template-compiler": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", - "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==", - "dev": true, - "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.2.0" - } - }, - "node_modules/vue-tsc": { - "version": "1.8.19", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.19.tgz", - "integrity": "sha512-tacMQLQ0CXAfbhRycCL5sWIy1qujXaIEtP1hIQpzHWOUuICbtTj9gJyFf91PvzG5KCNIkA5Eg7k2Fmgt28l5DQ==", - "dev": true, - "dependencies": { - "@vue/language-core": "1.8.19", - "@vue/typescript": "1.8.19", - "semver": "^7.5.4" - }, - "bin": { - "vue-tsc": "bin/vue-tsc.js" - }, - "peerDependencies": { - "typescript": "*" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } -} diff --git a/cista-front/package.json b/cista-front/package.json index b70c744..7d7c3fa 100644 --- a/cista-front/package.json +++ b/cista-front/package.json @@ -1,27 +1,59 @@ { - "name": "cista-front", + "name": "front", "version": "0.0.0", "private": true, "scripts": { "dev": "vite", "build": "run-p type-check \"build-only {@}\" --", "preview": "vite preview", + "test:unit": "vitest", "build-only": "vite build", - "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false" + "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "format": "prettier --write src/" }, "dependencies": { - "pinia": "^2.1.7", + "@imengyu/vue3-context-menu": "^1.3.3", + "@vueuse/core": "^10.4.1", + "esbuild": "^0.19.5", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "pinia": "^2.1.6", + "pinia-plugin-persistedstate": "^3.2.0", + "unplugin-vue-components": "^0.25.2", + "vite-plugin-rewrite-all": "^1.0.1", + "vite-svg-loader": "^4.0.0", "vue": "^3.3.4", - "vue-router": "^4.2.5" + "vue-router": "^4.2.4" }, "devDependencies": { + "@rushstack/eslint-patch": "^1.3.3", "@tsconfig/node18": "^18.2.2", - "@types/node": "^18.18.5", - "@vitejs/plugin-vue": "^4.4.0", + "@types/jsdom": "^21.1.3", + "@types/lodash-es": "^4.17.10", + "@types/node": "^18.17.17", + "@vitejs/plugin-vue": "^4.3.4", + "@vue/eslint-config-prettier": "^8.0.0", + "@vue/eslint-config-typescript": "^12.0.0", + "@vue/test-utils": "^2.4.1", "@vue/tsconfig": "^0.4.0", - "npm-run-all2": "^6.1.1", + "babel-eslint": "^10.1.0", + "eslint": "^8.52.0", + "eslint-plugin-vue": "^9.18.1", + "jsdom": "^22.1.0", + "npm-run-all2": "^6.0.6", + "prettier": "^3.0.3", "typescript": "~5.2.0", - "vite": "^4.4.11", - "vue-tsc": "^1.8.19" + "vite": "^4.4.9", + "vitest": "^0.34.4", + "vue-tsc": "^1.8.11" + }, + "prettier": { + "semi": false, + "singleQuote": true, + "trailingComma": "none", + "arrowParens": "avoid", + "endOfLine": "lf", + "printWidth": 88 } } diff --git a/cista-front/src/App.vue b/cista-front/src/App.vue index 7905b05..dbb7e16 100644 --- a/cista-front/src/App.vue +++ b/cista-front/src/App.vue @@ -1,85 +1,121 @@ - - - +onMounted(() => { + window.addEventListener('keydown', globalShortcutHandler) + window.addEventListener('keyup', globalShortcutHandler) +}) +onUnmounted(() => { + window.removeEventListener('keydown', globalShortcutHandler) + window.removeEventListener('keyup', globalShortcutHandler) +}) +export type { Path } + diff --git a/cista-front/src/assets/base.css b/cista-front/src/assets/base.css deleted file mode 100644 index 8816868..0000000 --- a/cista-front/src/assets/base.css +++ /dev/null @@ -1,86 +0,0 @@ -/* color palette from */ -:root { - --vt-c-white: #ffffff; - --vt-c-white-soft: #f8f8f8; - --vt-c-white-mute: #f2f2f2; - - --vt-c-black: #181818; - --vt-c-black-soft: #222222; - --vt-c-black-mute: #282828; - - --vt-c-indigo: #2c3e50; - - --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); - --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); - --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); - --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); - - --vt-c-text-light-1: var(--vt-c-indigo); - --vt-c-text-light-2: rgba(60, 60, 60, 0.66); - --vt-c-text-dark-1: var(--vt-c-white); - --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); -} - -/* semantic color variables for this project */ -:root { - --color-background: var(--vt-c-white); - --color-background-soft: var(--vt-c-white-soft); - --color-background-mute: var(--vt-c-white-mute); - - --color-border: var(--vt-c-divider-light-2); - --color-border-hover: var(--vt-c-divider-light-1); - - --color-heading: var(--vt-c-text-light-1); - --color-text: var(--vt-c-text-light-1); - - --section-gap: 160px; -} - -@media (prefers-color-scheme: dark) { - :root { - --color-background: var(--vt-c-black); - --color-background-soft: var(--vt-c-black-soft); - --color-background-mute: var(--vt-c-black-mute); - - --color-border: var(--vt-c-divider-dark-2); - --color-border-hover: var(--vt-c-divider-dark-1); - - --color-heading: var(--vt-c-text-dark-1); - --color-text: var(--vt-c-text-dark-2); - } -} - -*, -*::before, -*::after { - box-sizing: border-box; - margin: 0; - font-weight: normal; -} - -body { - min-height: 100vh; - color: var(--color-text); - background: var(--color-background); - transition: - color 0.5s, - background-color 0.5s; - line-height: 1.6; - font-family: - Inter, - -apple-system, - BlinkMacSystemFont, - 'Segoe UI', - Roboto, - Oxygen, - Ubuntu, - Cantarell, - 'Fira Sans', - 'Droid Sans', - 'Helvetica Neue', - sans-serif; - font-size: 15px; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/cista-front/src/assets/logo.svg b/cista-front/src/assets/logo.svg deleted file mode 100644 index 7565660..0000000 --- a/cista-front/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cista-front/src/assets/main.css b/cista-front/src/assets/main.css index e8667cd..2301f1e 100644 --- a/cista-front/src/assets/main.css +++ b/cista-front/src/assets/main.css @@ -1,35 +1,268 @@ -@import './base.css'; +@charset "UTF-8"; -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - - font-weight: normal; +:root { + --primary-color: #000; + --primary-background: #ddd; + --header-background: var(--soft-color); + --header-color: #ccc; + --input-background: #fff; + --input-color: #000; + --primary-color: #000; + --soft-color: #146; + --accent-color: #f80; + --transition-time: 0.2s; + /* The following are overridden by responsive layouts */ + --root-font-size: 1rem; + --header-font-size: 1rem; + --header-height: calc(6.5 * var(--header-font-size)); } - -a, -.green { - text-decoration: none; - color: hsla(160, 100%, 37%, 1); - transition: 0.4s; +@media (prefers-color-scheme: dark) { + :root { + --primary-color: #ddd; + --primary-background: var(--soft-color); + --header-background: #000; + --header-color: #ccc; + --input-background: var(--soft-color); + --input-color: #ddd; + } } - -@media (hover: hover) { - a:hover { - background-color: hsla(160, 100%, 37%, 0.2); +@media screen and (max-width: 600px) { + .size, + .modified, + .summary { + display: none; } } - -@media (min-width: 1024px) { - body { +@media screen and (min-width: 1000px) { + :root { + --root-font-size: calc(8px + 8 * 100vw / 1000); + } + header .buttons:has(input[type='search']) > div { + display: none; + } + header .buttons > div:has(input[type='search']) { + display: inherit; + } +} +@media screen and (min-width: 2000px) { + :root { + --root-font-size: 1.5rem; + } +} +/* Low (landscape) screens: smaller header */ +@media screen and (max-height: 600px) { + :root { + --header-font-size: calc(10px + 10 * 100vh / 600); /* 20px at 600px height */ + --root-font-size: 0.8rem; + } + header .breadcrumb > * { + padding-top: calc(8 + 8 * 100vh / 600) !important; + padding-bottom: calc(8 + 8 * 100vh / 600) !important; + } +} +@media screen and (max-height: 300px) { + :root { + --header-font-size: 15px; /* Don't go smaller than this, no benefit */ + --header-height: calc(1.75 * 16px); + --root-font-size: 0.6rem; + } + header .breadcrumb > * { + padding-top: 14px !important; + padding-bottom: 14px !important; + } +} +@media screen and (orientation: landscape) and (min-width: 700px) { + /* Breadcrumbs and buttons side by side */ + :root { + --header-font-size: calc(8px + 8 * 100vh / 600); /* 16px (1rem nominal) at 600px height */ + } + header { display: flex; - place-items: center; + flex-direction: row-reverse; + justify-content: space-between; + align-items: end; } - - #app { - display: grid; - grid-template-columns: 1fr 1fr; - padding: 0 2rem; + header .breadcrumb { + flex-shrink: 1; + } + header .breadcrumb > * { + flex-shrink: 1; + padding-top: 1rem !important; + padding-bottom: 1rem !important; } } +@media print { + :root { + --primary-color: black; + --primary-background: none; + --header-background: none; + --header-color: black; + } + nav, + .menu, + .rename-button { + display: none; + } + .breadcrumb > a { + color: black !important; + background: none !important; + padding: 0 !important; + margin: 0 !important; + clip-path: none !important; + max-width: none !important; + } + .breadcrumb > a::after { + content: '/'; + } + .breadcrumb svg { + fill: black !important; + } + main { + height: auto !important; + padding-bottom: 0 !important; + } + thead tr { + position: static !important; + background: none !important; + border-bottom: 1pt solid black !important; + } + .selection { + min-width: 0 !important; + padding: 0 !important; + } + .selection input { + display: none; + } + .selection input:checked { + display: inherit; + } + tbody .selection input:checked { + opacity: 1 !important; + transform: scale(0.5); + left: 0; + } +} +html { + font-size: var(--root-font-size); + overflow: hidden; +} +/* Hide scrollbar for all browsers */ +main::-webkit-scrollbar { + display: none; +} +main { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} +body { + background-color: var(--primary-background); + font-size: 1rem; + font-family: 'Roboto'; + color: var(--primary-color); + margin: 0; +} +tbody .size, +tbody .modified { + font-family: 'Roboto Mono'; +} +header { + background-color: var(--header-background); + color: var(--header-color); + font-size: var(--header-font-size); +} +main { + height: 100%; +} +::selection { + color: #000; + background: yellow !important; +} +button { + font: inherit; + color: inherit; + margin: 0; + border: 0; + padding: 0; + background: none; + cursor: pointer; + min-width: 1rem; + min-height: 1rem; +} +input { + margin: 0; +} +:focus { + outline: none; +} +a:link, +a:visited, +a:active, +a:hover { + color: var(--primary-color); + text-decoration: none; +} +table { + border-collapse: collapse; + border-spacing: 0; + border: 0; + gap: 0; +} +#app { + height: 100%; + display: flex; + flex-direction: column; +} +header nav.headermain { + /* Position so that tooltips can appear on top of other positioned elements */ + position: relative; + z-index: 100; +} +main { + height: calc(100svh - var(--header-height)); + padding-bottom: 3em; /* convenience space on the bottom */ + overflow-y: scroll; +} +.spacer { flex-grow: 1 } +.smallgap { flex-shrink: 1; width: 2em } + +[data-tooltip]:hover:after { + z-index: 101; + content: attr(data-tooltip); + position: absolute; + font-size: 1rem; + text-align: center; + padding: .5rem 1rem; + border-radius: 3rem 0 3rem 0; + box-shadow: 0 0 1rem var(--accent-color); + transform: translate(calc(1rem + -50%), 150%); + background-color: var(--accent-color); + color: var(--primary-color); + white-space: pre; + animation: appearbriefly calc(10 * var(--transition-time)) linear forwards; +} +.modified [data-tooltip]:hover:after { + transform: translate(calc(1rem + 1ex + -100%), calc(-1.5rem + 100%)); +} +@keyframes appearbriefly { + from { + opacity: 0; + } + 30% { + opacity: 0; + } + 40% { + opacity: 1; + } + 90% { + opacity: 1; + } + to { + opacity: 0; + } +} +.error-message { + padding: .5em; + font-weight: bold; + background: var(--accent-color); + color: #000; +} diff --git a/cista-front/src/assets/svg/add-file.svg b/cista-front/src/assets/svg/add-file.svg new file mode 100644 index 0000000..6a219d4 --- /dev/null +++ b/cista-front/src/assets/svg/add-file.svg @@ -0,0 +1 @@ + diff --git a/cista-front/src/assets/svg/add-folder.svg b/cista-front/src/assets/svg/add-folder.svg new file mode 100644 index 0000000..e9ea9ea --- /dev/null +++ b/cista-front/src/assets/svg/add-folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/arrow.svg b/cista-front/src/assets/svg/arrow.svg new file mode 100644 index 0000000..c28cd74 --- /dev/null +++ b/cista-front/src/assets/svg/arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/arrows-h.svg b/cista-front/src/assets/svg/arrows-h.svg new file mode 100644 index 0000000..08fb7c8 --- /dev/null +++ b/cista-front/src/assets/svg/arrows-h.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/arrows-v.svg b/cista-front/src/assets/svg/arrows-v.svg new file mode 100644 index 0000000..0cb0981 --- /dev/null +++ b/cista-front/src/assets/svg/arrows-v.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/check.svg b/cista-front/src/assets/svg/check.svg new file mode 100644 index 0000000..fcb7b4f --- /dev/null +++ b/cista-front/src/assets/svg/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/code.svg b/cista-front/src/assets/svg/code.svg new file mode 100644 index 0000000..ecb7d23 --- /dev/null +++ b/cista-front/src/assets/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/cog.svg b/cista-front/src/assets/svg/cog.svg new file mode 100644 index 0000000..164d7e2 --- /dev/null +++ b/cista-front/src/assets/svg/cog.svg @@ -0,0 +1 @@ + diff --git a/cista-front/src/assets/svg/copy.svg b/cista-front/src/assets/svg/copy.svg new file mode 100644 index 0000000..da381f3 --- /dev/null +++ b/cista-front/src/assets/svg/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/create-file.svg b/cista-front/src/assets/svg/create-file.svg new file mode 100644 index 0000000..3486c99 --- /dev/null +++ b/cista-front/src/assets/svg/create-file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/create-folder.svg b/cista-front/src/assets/svg/create-folder.svg new file mode 100644 index 0000000..f23c529 --- /dev/null +++ b/cista-front/src/assets/svg/create-folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/cross.svg b/cista-front/src/assets/svg/cross.svg new file mode 100644 index 0000000..c00f247 --- /dev/null +++ b/cista-front/src/assets/svg/cross.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/disk.svg b/cista-front/src/assets/svg/disk.svg new file mode 100644 index 0000000..4da18e9 --- /dev/null +++ b/cista-front/src/assets/svg/disk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/download.svg b/cista-front/src/assets/svg/download.svg new file mode 100644 index 0000000..d4ec5ed --- /dev/null +++ b/cista-front/src/assets/svg/download.svg @@ -0,0 +1 @@ + diff --git a/cista-front/src/assets/svg/exclamation.svg b/cista-front/src/assets/svg/exclamation.svg new file mode 100644 index 0000000..b1350d4 --- /dev/null +++ b/cista-front/src/assets/svg/exclamation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/eye.svg b/cista-front/src/assets/svg/eye.svg new file mode 100644 index 0000000..025fdd7 --- /dev/null +++ b/cista-front/src/assets/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/find.svg b/cista-front/src/assets/svg/find.svg new file mode 100644 index 0000000..b16111e --- /dev/null +++ b/cista-front/src/assets/svg/find.svg @@ -0,0 +1 @@ + diff --git a/cista-front/src/assets/svg/fullscreen.svg b/cista-front/src/assets/svg/fullscreen.svg new file mode 100644 index 0000000..12c6acc --- /dev/null +++ b/cista-front/src/assets/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/github.svg b/cista-front/src/assets/svg/github.svg new file mode 100644 index 0000000..c45ead6 --- /dev/null +++ b/cista-front/src/assets/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/home.svg b/cista-front/src/assets/svg/home.svg new file mode 100644 index 0000000..6c75e4d --- /dev/null +++ b/cista-front/src/assets/svg/home.svg @@ -0,0 +1 @@ + diff --git a/cista-front/src/assets/svg/info.svg b/cista-front/src/assets/svg/info.svg new file mode 100644 index 0000000..b7207d9 --- /dev/null +++ b/cista-front/src/assets/svg/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/link.svg b/cista-front/src/assets/svg/link.svg new file mode 100644 index 0000000..f30d2dd --- /dev/null +++ b/cista-front/src/assets/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/logo.svg b/cista-front/src/assets/svg/logo.svg new file mode 100644 index 0000000..ec92f76 --- /dev/null +++ b/cista-front/src/assets/svg/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/loop.svg b/cista-front/src/assets/svg/loop.svg new file mode 100644 index 0000000..8bba5f9 --- /dev/null +++ b/cista-front/src/assets/svg/loop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/menu.svg b/cista-front/src/assets/svg/menu.svg new file mode 100644 index 0000000..decc2ad --- /dev/null +++ b/cista-front/src/assets/svg/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/next.svg b/cista-front/src/assets/svg/next.svg new file mode 100644 index 0000000..8ae7797 --- /dev/null +++ b/cista-front/src/assets/svg/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/open.svg b/cista-front/src/assets/svg/open.svg new file mode 100644 index 0000000..b8f3710 --- /dev/null +++ b/cista-front/src/assets/svg/open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/paste.svg b/cista-front/src/assets/svg/paste.svg new file mode 100644 index 0000000..36bdeba --- /dev/null +++ b/cista-front/src/assets/svg/paste.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/pause.svg b/cista-front/src/assets/svg/pause.svg new file mode 100644 index 0000000..3950b22 --- /dev/null +++ b/cista-front/src/assets/svg/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/pencil.svg b/cista-front/src/assets/svg/pencil.svg new file mode 100644 index 0000000..aed85f6 --- /dev/null +++ b/cista-front/src/assets/svg/pencil.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/play.svg b/cista-front/src/assets/svg/play.svg new file mode 100644 index 0000000..f2f1421 --- /dev/null +++ b/cista-front/src/assets/svg/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/plus.svg b/cista-front/src/assets/svg/plus.svg new file mode 100644 index 0000000..3b4606d --- /dev/null +++ b/cista-front/src/assets/svg/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/previous.svg b/cista-front/src/assets/svg/previous.svg new file mode 100644 index 0000000..c6627c2 --- /dev/null +++ b/cista-front/src/assets/svg/previous.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/reload.svg b/cista-front/src/assets/svg/reload.svg new file mode 100644 index 0000000..7a3f49c --- /dev/null +++ b/cista-front/src/assets/svg/reload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/rename.svg b/cista-front/src/assets/svg/rename.svg new file mode 100644 index 0000000..c7cca59 --- /dev/null +++ b/cista-front/src/assets/svg/rename.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/scissors.svg b/cista-front/src/assets/svg/scissors.svg new file mode 100644 index 0000000..03c87de --- /dev/null +++ b/cista-front/src/assets/svg/scissors.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/shuffle.svg b/cista-front/src/assets/svg/shuffle.svg new file mode 100644 index 0000000..708bb89 --- /dev/null +++ b/cista-front/src/assets/svg/shuffle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/signin.svg b/cista-front/src/assets/svg/signin.svg new file mode 100644 index 0000000..6abf1fe --- /dev/null +++ b/cista-front/src/assets/svg/signin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/signout.svg b/cista-front/src/assets/svg/signout.svg new file mode 100644 index 0000000..8cccb6b --- /dev/null +++ b/cista-front/src/assets/svg/signout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/skip.svg b/cista-front/src/assets/svg/skip.svg new file mode 100644 index 0000000..8c318d0 --- /dev/null +++ b/cista-front/src/assets/svg/skip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/spinner.svg b/cista-front/src/assets/svg/spinner.svg new file mode 100644 index 0000000..1bfa9c8 --- /dev/null +++ b/cista-front/src/assets/svg/spinner.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/stop.svg b/cista-front/src/assets/svg/stop.svg new file mode 100644 index 0000000..9e6a47c --- /dev/null +++ b/cista-front/src/assets/svg/stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/trash.svg b/cista-front/src/assets/svg/trash.svg new file mode 100644 index 0000000..840f1f0 --- /dev/null +++ b/cista-front/src/assets/svg/trash.svg @@ -0,0 +1 @@ + diff --git a/cista-front/src/assets/svg/triangle.svg b/cista-front/src/assets/svg/triangle.svg new file mode 100644 index 0000000..bd15973 --- /dev/null +++ b/cista-front/src/assets/svg/triangle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/unfullscreen.svg b/cista-front/src/assets/svg/unfullscreen.svg new file mode 100644 index 0000000..c0b2dd2 --- /dev/null +++ b/cista-front/src/assets/svg/unfullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/up-arrow.svg b/cista-front/src/assets/svg/up-arrow.svg new file mode 100644 index 0000000..d199ad7 --- /dev/null +++ b/cista-front/src/assets/svg/up-arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/upload-cloud.svg b/cista-front/src/assets/svg/upload-cloud.svg new file mode 100644 index 0000000..421a513 --- /dev/null +++ b/cista-front/src/assets/svg/upload-cloud.svg @@ -0,0 +1 @@ + diff --git a/cista-front/src/assets/svg/user-cog.svg b/cista-front/src/assets/svg/user-cog.svg new file mode 100644 index 0000000..6c84dfe --- /dev/null +++ b/cista-front/src/assets/svg/user-cog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/user.svg b/cista-front/src/assets/svg/user.svg new file mode 100644 index 0000000..d669fdb --- /dev/null +++ b/cista-front/src/assets/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/volume-high.svg b/cista-front/src/assets/svg/volume-high.svg new file mode 100644 index 0000000..093950b --- /dev/null +++ b/cista-front/src/assets/svg/volume-high.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/volume-low.svg b/cista-front/src/assets/svg/volume-low.svg new file mode 100644 index 0000000..8c50d96 --- /dev/null +++ b/cista-front/src/assets/svg/volume-low.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/volume-medium.svg b/cista-front/src/assets/svg/volume-medium.svg new file mode 100644 index 0000000..eba0ea0 --- /dev/null +++ b/cista-front/src/assets/svg/volume-medium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/volume-mute.svg b/cista-front/src/assets/svg/volume-mute.svg new file mode 100644 index 0000000..a3893cc --- /dev/null +++ b/cista-front/src/assets/svg/volume-mute.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/window-cross.svg b/cista-front/src/assets/svg/window-cross.svg new file mode 100644 index 0000000..b27af7d --- /dev/null +++ b/cista-front/src/assets/svg/window-cross.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/window.svg b/cista-front/src/assets/svg/window.svg new file mode 100644 index 0000000..0b3ccd3 --- /dev/null +++ b/cista-front/src/assets/svg/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/wordwrap.svg b/cista-front/src/assets/svg/wordwrap.svg new file mode 100644 index 0000000..ef3124d --- /dev/null +++ b/cista-front/src/assets/svg/wordwrap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/zoomin.svg b/cista-front/src/assets/svg/zoomin.svg new file mode 100644 index 0000000..24e626a --- /dev/null +++ b/cista-front/src/assets/svg/zoomin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/assets/svg/zoomout.svg b/cista-front/src/assets/svg/zoomout.svg new file mode 100644 index 0000000..79e4886 --- /dev/null +++ b/cista-front/src/assets/svg/zoomout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cista-front/src/components/BreadCrumb.vue b/cista-front/src/components/BreadCrumb.vue new file mode 100644 index 0000000..addac26 --- /dev/null +++ b/cista-front/src/components/BreadCrumb.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/cista-front/src/components/FileExplorer.vue b/cista-front/src/components/FileExplorer.vue new file mode 100644 index 0000000..a784ce6 --- /dev/null +++ b/cista-front/src/components/FileExplorer.vue @@ -0,0 +1,516 @@ + + + + + diff --git a/cista-front/src/components/FileRenameInput.vue b/cista-front/src/components/FileRenameInput.vue new file mode 100644 index 0000000..aaa6977 --- /dev/null +++ b/cista-front/src/components/FileRenameInput.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/cista-front/src/components/FileViewer.vue b/cista-front/src/components/FileViewer.vue new file mode 100644 index 0000000..4e45e02 --- /dev/null +++ b/cista-front/src/components/FileViewer.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/cista-front/src/components/HeaderMain.vue b/cista-front/src/components/HeaderMain.vue new file mode 100644 index 0000000..a9826b1 --- /dev/null +++ b/cista-front/src/components/HeaderMain.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/cista-front/src/components/HeaderSelected.vue b/cista-front/src/components/HeaderSelected.vue new file mode 100644 index 0000000..cdfd0d7 --- /dev/null +++ b/cista-front/src/components/HeaderSelected.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/cista-front/src/components/HelloWorld.vue b/cista-front/src/components/HelloWorld.vue deleted file mode 100644 index 38d821e..0000000 --- a/cista-front/src/components/HelloWorld.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - diff --git a/cista-front/src/components/LoginModal.vue b/cista-front/src/components/LoginModal.vue new file mode 100644 index 0000000..3d811e8 --- /dev/null +++ b/cista-front/src/components/LoginModal.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/cista-front/src/components/ModalDialog.vue b/cista-front/src/components/ModalDialog.vue new file mode 100644 index 0000000..b024a7c --- /dev/null +++ b/cista-front/src/components/ModalDialog.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/cista-front/src/components/NotificationLoading.vue b/cista-front/src/components/NotificationLoading.vue new file mode 100644 index 0000000..81e9cde --- /dev/null +++ b/cista-front/src/components/NotificationLoading.vue @@ -0,0 +1,27 @@ + + + + diff --git a/cista-front/src/components/SvgButton.vue b/cista-front/src/components/SvgButton.vue new file mode 100644 index 0000000..5f5c162 --- /dev/null +++ b/cista-front/src/components/SvgButton.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/cista-front/src/components/TheWelcome.vue b/cista-front/src/components/TheWelcome.vue deleted file mode 100644 index 49d8f73..0000000 --- a/cista-front/src/components/TheWelcome.vue +++ /dev/null @@ -1,88 +0,0 @@ - - - diff --git a/cista-front/src/components/UploadButton.vue b/cista-front/src/components/UploadButton.vue new file mode 100644 index 0000000..b2b69d9 --- /dev/null +++ b/cista-front/src/components/UploadButton.vue @@ -0,0 +1,252 @@ + + + + diff --git a/cista-front/src/components/WelcomeItem.vue b/cista-front/src/components/WelcomeItem.vue deleted file mode 100644 index 6d7086a..0000000 --- a/cista-front/src/components/WelcomeItem.vue +++ /dev/null @@ -1,87 +0,0 @@ - - - diff --git a/cista-front/src/components/icons/IconCommunity.vue b/cista-front/src/components/icons/IconCommunity.vue deleted file mode 100644 index 2dc8b05..0000000 --- a/cista-front/src/components/icons/IconCommunity.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/cista-front/src/components/icons/IconDocumentation.vue b/cista-front/src/components/icons/IconDocumentation.vue deleted file mode 100644 index 6d4791c..0000000 --- a/cista-front/src/components/icons/IconDocumentation.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/cista-front/src/components/icons/IconEcosystem.vue b/cista-front/src/components/icons/IconEcosystem.vue deleted file mode 100644 index c3a4f07..0000000 --- a/cista-front/src/components/icons/IconEcosystem.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/cista-front/src/components/icons/IconSupport.vue b/cista-front/src/components/icons/IconSupport.vue deleted file mode 100644 index 7452834..0000000 --- a/cista-front/src/components/icons/IconSupport.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/cista-front/src/components/icons/IconTooling.vue b/cista-front/src/components/icons/IconTooling.vue deleted file mode 100644 index 660598d..0000000 --- a/cista-front/src/components/icons/IconTooling.vue +++ /dev/null @@ -1,19 +0,0 @@ - - diff --git a/cista-front/src/main.ts b/cista-front/src/main.ts index 5dcad83..ac91353 100644 --- a/cista-front/src/main.ts +++ b/cista-front/src/main.ts @@ -6,9 +6,20 @@ import { createPinia } from 'pinia' import App from './App.vue' import router from './router' +import piniaPluginPersistedState from 'pinia-plugin-persistedstate' + +import ContextMenu from '@imengyu/vue3-context-menu' +import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css' + const app = createApp(App) +app.config.errorHandler = err => { + /* handle error */ + console.log(err) +} -app.use(createPinia()) +const pinia = createPinia() +pinia.use(piniaPluginPersistedState) +app.use(pinia) app.use(router) - +app.use(ContextMenu) app.mount('#app') diff --git a/cista-front/src/repositories/Client.ts b/cista-front/src/repositories/Client.ts new file mode 100644 index 0000000..5236dcc --- /dev/null +++ b/cista-front/src/repositories/Client.ts @@ -0,0 +1,35 @@ +class ClientClass { + async post(url: string, data?: Record): Promise { + const res = await fetch(url, { + method: 'POST', + headers: { + accept: 'application/json', + 'content-type': 'application/json' + }, + body: data !== undefined ? JSON.stringify(data) : undefined + }) + let msg + try { + msg = await res.json() + } catch (e) { + throw new SimpleError(res.status, `🛑 ${res.status} ${res.statusText}`) + } + if ('error' in msg) throw new SimpleError(msg.error.code, msg.error.message) + return msg + } +} + +export const Client = new ClientClass() +export interface ISimpleError extends Error { + code: number +} + +class SimpleError extends Error implements ISimpleError { + code: number + constructor(code: number, message: string) { + super(message) + this.code = code + } +} + +export default Client diff --git a/cista-front/src/repositories/Document.ts b/cista-front/src/repositories/Document.ts new file mode 100644 index 0000000..57355b4 --- /dev/null +++ b/cista-front/src/repositories/Document.ts @@ -0,0 +1,55 @@ +export type FUID = string + +export type Document = { + loc: string + name: string + key: FUID + size: number + sizedisp: string + mtime: number + modified: string + haystack: string + dir: boolean +} + +export type errorEvent = { + error: { + code: number + message: string + redirect: string + } +} + +// Raw types the backend /api/watch sends us + +export type FileEntry = { + key: FUID + size: number + mtime: number +} + +export type DirEntry = { + key: FUID + size: number + mtime: number + dir: DirList +} + +export type DirList = Record + +export type UpdateEntry = { + name: string + deleted?: boolean + key?: FUID + size?: number + mtime?: number + dir?: DirList +} + +// Helper structure for selections +export interface SelectedItems { + keys: FUID[] + docs: Record + recursive: Array<[string, string, Document]> + missing: Set +} diff --git a/cista-front/src/repositories/User.ts b/cista-front/src/repositories/User.ts new file mode 100644 index 0000000..f39dd31 --- /dev/null +++ b/cista-front/src/repositories/User.ts @@ -0,0 +1,15 @@ +import Client from '@/repositories/Client' +export const url_login = '/login' +export const url_logout = '/logout ' + +export async function loginUser(username: string, password: string) { + const user = await Client.post(url_login, { + username, + password + }) + return user +} +export async function logoutUser() { + const data = await Client.post(url_logout) + return data +} diff --git a/cista-front/src/repositories/WS.ts b/cista-front/src/repositories/WS.ts new file mode 100644 index 0000000..b760e17 --- /dev/null +++ b/cista-front/src/repositories/WS.ts @@ -0,0 +1,133 @@ +import { useDocumentStore } from "@/stores/documents" +import type { DirEntry, UpdateEntry, errorEvent } from "./Document" + +export const controlUrl = '/api/control' +export const uploadUrl = '/api/upload' +export const watchUrl = '/api/watch' + +let tree = null as DirEntry | null +let reconnectDuration = 500 +let wsWatch = null as WebSocket | null + +export const connect = (path: string, handlers: Partial>) => { + const webSocket = new WebSocket(new URL(path, location.origin.replace(/^http/, 'ws'))) + for (const [event, handler] of Object.entries(handlers)) webSocket.addEventListener(event, handler) + return webSocket +} + +export const watchConnect = () => { + if (watchTimeout !== null) { + clearTimeout(watchTimeout) + watchTimeout = null + } + const store = useDocumentStore() + if (store.error !== 'Reconnecting...') store.error = 'Connecting...' + console.log(store.error) + + wsWatch = connect(watchUrl, { + message: handleWatchMessage, + close: watchReconnect, + }) + wsWatch.addEventListener("message", event => { + if (store.connected) return + const msg = JSON.parse(event.data) + if ('error' in msg) { + if (msg.error.code === 401) { + store.user.isLoggedIn = false + store.user.isOpenLoginModal = true + } else { + store.error = msg.error.message + } + return + } + if ("server" in msg) { + console.log('Connected to backend', msg) + store.connected = true + reconnectDuration = 500 + store.error = '' + if (msg.user) store.login(msg.user.username, msg.user.privileged) + else if (store.isUserLogged) store.logout() + if (!msg.server.public && !msg.user) store.user.isOpenLoginModal = true + } + }) +} + +export const watchDisconnect = () => { + if (!wsWatch) return + wsWatch.close() + wsWatch = null +} + +let watchTimeout: any = null + +const watchReconnect = (event: MessageEvent) => { + const store = useDocumentStore() + if (store.connected) { + console.warn("Disconnected from server", event) + store.connected = false + store.error = 'Reconnecting...' + } + reconnectDuration = Math.min(5000, reconnectDuration + 500) + // The server closes the websocket after errors, so we need to reopen it + if (watchTimeout !== null) clearTimeout(watchTimeout) + watchTimeout = setTimeout(watchConnect, reconnectDuration) +} + + +const handleWatchMessage = (event: MessageEvent) => { + const msg = JSON.parse(event.data) + switch (true) { + case !!msg.root: + handleRootMessage(msg) + break + case !!msg.update: + handleUpdateMessage(msg) + break + case !!msg.space: + console.log('Watch space', msg.space) + break + case !!msg.error: + handleError(msg) + break + default: + } +} + +function handleRootMessage({ root }: { root: DirEntry }) { + const store = useDocumentStore() + console.log('Watch root', root) + store.updateRoot(root) + tree = root +} + +function handleUpdateMessage(updateData: { update: UpdateEntry[] }) { + const store = useDocumentStore() + console.log('Watch update', updateData.update) + if (!tree) return console.error('Watch update before root') + let node: DirEntry = tree + for (const elem of updateData.update) { + if (elem.deleted) { + delete node.dir[elem.name] + break // Deleted elements can't have further children + } + if (elem.name) { + // @ts-ignore + console.log(node, elem.name) + node = node.dir[elem.name] ||= {} + } + if (elem.key !== undefined) node.key = elem.key + if (elem.size !== undefined) node.size = elem.size + if (elem.mtime !== undefined) node.mtime = elem.mtime + if (elem.dir !== undefined) node.dir = elem.dir + } + store.updateRoot(tree) +} + +function handleError(msg: errorEvent) { + const store = useDocumentStore() + if (msg.error.code === 401) { + store.user.isOpenLoginModal = true + store.user.isLoggedIn = false + return + } +} diff --git a/cista-front/src/router/index.ts b/cista-front/src/router/index.ts index a49ae50..08a6aca 100644 --- a/cista-front/src/router/index.ts +++ b/cista-front/src/router/index.ts @@ -1,21 +1,13 @@ -import { createRouter, createWebHistory } from 'vue-router' -import HomeView from '../views/HomeView.vue' +import { createRouter, createWebHashHistory } from 'vue-router' +import ExplorerView from '@/views/ExplorerView.vue' const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), + history: createWebHashHistory(import.meta.env.BASE_URL), routes: [ { - path: '/', - name: 'home', - component: HomeView - }, - { - path: '/about', - name: 'about', - // route level code-splitting - // this generates a separate chunk (About.[hash].js) for this route - // which is lazy-loaded when the route is visited. - component: () => import('../views/AboutView.vue') + path: '/:pathMatch(.*)*', + name: 'explorer', + component: ExplorerView } ] }) diff --git a/cista-front/src/stores/counter.ts b/cista-front/src/stores/counter.ts deleted file mode 100644 index b6757ba..0000000 --- a/cista-front/src/stores/counter.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ref, computed } from 'vue' -import { defineStore } from 'pinia' - -export const useCounterStore = defineStore('counter', () => { - const count = ref(0) - const doubleCount = computed(() => count.value * 2) - function increment() { - count.value++ - } - - return { count, doubleCount, increment } -}) diff --git a/cista-front/src/stores/documents.ts b/cista-front/src/stores/documents.ts new file mode 100644 index 0000000..59662d0 --- /dev/null +++ b/cista-front/src/stores/documents.ts @@ -0,0 +1,160 @@ +import type { + Document, + DirEntry, + FileEntry, + FUID, + SelectedItems +} from '@/repositories/Document' +import { formatSize, formatUnixDate, haystackFormat } from '@/utils' +import { defineStore } from 'pinia' +import { collator } from '@/utils' +import { logoutUser } from '@/repositories/User' +import { watchConnect } from '@/repositories/WS' + +type FileData = { id: string; mtime: number; size: number; dir: DirectoryData } +type DirectoryData = { + [filename: string]: FileData +} +type User = { + username: string + privileged: boolean + isOpenLoginModal: boolean + isLoggedIn: boolean +} + +export const useDocumentStore = defineStore({ + id: 'documents', + state: () => ({ + document: [] as Document[], + search: "" as string, + selected: new Set(), + uploadingDocuments: [], + uploadCount: 0 as number, + fileExplorer: null, + error: '' as string, + connected: false, + user: { + username: '', + privileged: false, + isLoggedIn: false, + isOpenLoginModal: false + } as User + }), + persist: { + storage: sessionStorage, + paths: ['document'], + }, + actions: { + updateRoot(root: DirEntry | null = null) { + if (!root) { + this.document = [] + return + } + // Transform tree data to flat documents array + let loc = "" + const mapper = ([name, attr]: [string, FileEntry | DirEntry]) => ({ + ...attr, + loc, + name, + sizedisp: formatSize(attr.size), + modified: formatUnixDate(attr.mtime), + haystack: haystackFormat(name), + }) + const queue = [...Object.entries(root.dir ?? {}).map(mapper)] + const docs = [] + for (let doc; (doc = queue.shift()) !== undefined;) { + docs.push(doc) + if ("dir" in doc) { + // Recurse but replace recursive structure with boolean + loc = doc.loc ? `${doc.loc}/${doc.name}` : doc.name + queue.push(...Object.entries(doc.dir).map(mapper)) + // @ts-ignore + doc.dir = true + } + // @ts-ignore + else doc.dir = false + } + // Pre sort directory entries folders first then files, names in natural ordering + docs.sort((a, b) => + // @ts-ignore + b.dir - a.dir || + collator.compare(a.name, b.name) + ) + this.document = docs as Document[] + }, + login(username: string, privileged: boolean) { + this.user.username = username + this.user.privileged = privileged + this.user.isLoggedIn = true + this.user.isOpenLoginModal = false + if (!this.connected) watchConnect() + }, + loginDialog() { + this.user.isOpenLoginModal = true + }, + async logout() { + console.log("Logout") + await logoutUser() + this.$reset() + history.go() // Reload page + } + }, + getters: { + isUserLogged(): boolean { + return this.user.isLoggedIn + }, + recentDocuments(): Document[] { + const ret = [...this.document] + ret.sort((a, b) => b.mtime - a.mtime) + return ret + }, + largeDocuments(): Document[] { + const ret = [...this.document] + ret.sort((a, b) => b.size - a.size) + return ret + }, + selectedFiles(): SelectedItems { + const selected = this.selected + const found = new Set() + const ret: SelectedItems = { + missing: new Set(), + docs: {}, + keys: [], + recursive: [], + } + for (const doc of this.document) { + if (selected.has(doc.key)) { + found.add(doc.key) + ret.keys.push(doc.key) + ret.docs[doc.key] = doc + } + } + // What did we not select? + for (const key of selected) if (!found.has(key)) ret.missing.add(key) + // Build a flat list including contents recursively + const relnames = new Set() + function add(rel: string, full: string, doc: Document) { + if (!doc.dir && relnames.has(rel)) throw Error(`Multiple selections conflict for: ${rel}`) + relnames.add(rel) + ret.recursive.push([rel, full, doc]) + } + for (const key of ret.keys) { + const base = ret.docs[key] + const basepath = base.loc ? `${base.loc}/${base.name}` : base.name + const nremove = base.loc.length + add(base.name, basepath, base) + for (const doc of this.document) { + if (doc.loc === basepath || doc.loc.startsWith(basepath) && doc.loc[basepath.length] === '/') { + const full = doc.loc ? `${doc.loc}/${doc.name}` : doc.name + const rel = full.slice(nremove) + add(rel, full, doc) + } + } + } + // Sort by rel (name stored as on download) + ret.recursive.sort((a, b) => collator.compare(a[0], b[0])) + + return ret + } + } +}) diff --git a/cista-front/src/utils/index.ts b/cista-front/src/utils/index.ts new file mode 100644 index 0000000..e8cf947 --- /dev/null +++ b/cista-front/src/utils/index.ts @@ -0,0 +1,96 @@ +export function determineFileType(inputString: string): 'file' | 'folder' { + if (inputString.includes('.') && !inputString.endsWith('.')) { + return 'file' + } else { + return 'folder' + } +} + +export function formatSize(size: number) { + if (size === 0) return 'empty' + for (const unit of [null, 'kB', 'MB', 'GB', 'TB', 'PB', 'EB']) { + if (size < 1e4) + return ( + size.toLocaleString().replace(',', '\u202F') + (unit ? `\u202F${unit}` : '') + ) + size = Math.round(size / 1000) + } + return 'huge' +} + +export function formatUnixDate(t: number) { + const date = new Date(t * 1000) + const now = new Date() + const diff = date.getTime() - now.getTime() + const adiff = Math.abs(diff) + const formatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }) + if (adiff <= 5000) return 'now' + if (adiff <= 60000) { + return formatter.format(Math.round(diff / 1000), 'second').replace(' ago', '').replaceAll(' ', '\u202F') + } + if (adiff <= 3600000) { + return formatter.format(Math.round(diff / 60000), 'minute').replace('utes', '').replace('ute', '').replaceAll(' ', '\u202F') + } + if (adiff <= 86400000) { + return formatter.format(Math.round(diff / 3600000), 'hour').replaceAll(' ', '\u202F') + } + if (adiff <= 604800000) { + return formatter.format(Math.round(diff / 86400000), 'day').replaceAll(' ', '\u202F') + } + let d = date.toLocaleDateString('en-ie', { + weekday: 'short', + year: 'numeric', + month: 'short', + day: 'numeric' + }).replace("Sept", "Sep") + if (d.length === 14) d = d.replace(' ', ' \u2007') // dom < 10 alignment (add figure space) + d = d.replaceAll(' ', '\u202F').replace('\u202F', '\u00A0') // nobr spaces, thin w/ date but not weekday + d = d.slice(0, -4) + d.slice(-2) // Two digit year is enough + return d +} + +export function getFileExtension(filename: string) { + const parts = filename.split('.') + if (parts.length > 1) { + return parts[parts.length - 1] + } else { + return '' // No hay extensión + } +} +interface FileTypes { + [key: string]: string[] +} + +const filetypes: FileTypes = { + video: ['avi', 'mkv', 'mov', 'mp4', 'webm'], + image: ['avif', 'gif', 'jpg', 'jpeg', 'png', 'webp', 'svg'], + pdf: ['pdf'], +} + +export function getFileType(name: string): string { + const ext = name.split('.').pop()?.toLowerCase() + if (!ext || ext.length === name.length) return 'unknown' + return Object.keys(filetypes).find(type => filetypes[type].includes(ext)) || 'unknown' +} + +// Prebuilt for fast & consistent sorting +export const collator = new Intl.Collator('en', { sensitivity: 'base', numeric: true, usage: 'search' }) + +// Preformat document names for faster search +export function haystackFormat(str: string) { + const based = str.normalize('NFKD').replace(/[\u0300-\u036f]/g, '').toLowerCase() + return '^' + based + '$' +} + + +// Preformat search string for faster search +export function needleFormat(query: string) { + const based = query.normalize('NFKD').replace(/[\u0300-\u036f]/g, '').toLowerCase() + return {based, words: based.split(/\W+/)} +} + +// Test if haystack includes needle +export function localeIncludes(haystack: string, filter: { based: string, words: string[] }) { + const {based, words} = filter + return haystack.includes(based) || words && words.every(word => haystack.includes(word)) +} diff --git a/cista-front/src/views/AboutView.vue b/cista-front/src/views/AboutView.vue deleted file mode 100644 index 756ad2a..0000000 --- a/cista-front/src/views/AboutView.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/cista-front/src/views/ExplorerView.vue b/cista-front/src/views/ExplorerView.vue new file mode 100644 index 0000000..b163caa --- /dev/null +++ b/cista-front/src/views/ExplorerView.vue @@ -0,0 +1,58 @@ + + + diff --git a/cista-front/src/views/HomeView.vue b/cista-front/src/views/HomeView.vue deleted file mode 100644 index d5c0217..0000000 --- a/cista-front/src/views/HomeView.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/cista-front/tsconfig.app.json b/cista-front/tsconfig.app.json index 3e5b621..5fc923a 100644 --- a/cista-front/tsconfig.app.json +++ b/cista-front/tsconfig.app.json @@ -3,6 +3,8 @@ "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], "exclude": ["src/**/__tests__/*"], "compilerOptions": { + "lib": ["es2021", "DOM"], + "target": "es2021", "composite": true, "baseUrl": ".", "paths": { diff --git a/cista-front/tsconfig.json b/cista-front/tsconfig.json index 66b5e57..100cf6a 100644 --- a/cista-front/tsconfig.json +++ b/cista-front/tsconfig.json @@ -6,6 +6,9 @@ }, { "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.vitest.json" } ] } diff --git a/cista-front/tsconfig.vitest.json b/cista-front/tsconfig.vitest.json new file mode 100644 index 0000000..7865677 --- /dev/null +++ b/cista-front/tsconfig.vitest.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.app.json", + "exclude": [], + "compilerOptions": { + "composite": true, + "types": ["node", "jsdom"] + } +} diff --git a/cista-front/vite.config.ts b/cista-front/vite.config.ts index a595148..9e1b9d8 100644 --- a/cista-front/vite.config.ts +++ b/cista-front/vite.config.ts @@ -3,16 +3,50 @@ import { fileURLToPath, URL } from 'node:url' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' +// @ts-ignore +import pluginRewriteAll from 'vite-plugin-rewrite-all' +import svgLoader from 'vite-svg-loader' +import Components from 'unplugin-vue-components/vite' + +// Development mode: +// npm run dev # Run frontend that proxies to dev_backend +// cista -l :8000 --dev # Run backend +const dev_backend = { + target: "http://localhost:8000", + changeOrigin: false, // Use frontend "host" to match "origin" from browser + ws: true, +} + // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), + pluginRewriteAll(), + svgLoader(), // import svg files + Components(), // auto import components ], + css: { + preprocessorOptions: { + less: { + modifyVars: {}, + javascriptEnabled: true, + }, + }, + }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, + server: { + proxy: { + "/api": dev_backend, + "/files": dev_backend, + "/login": dev_backend, + "/logout": dev_backend, + "/zip": dev_backend, + } + }, build: { outDir: "../cista/wwwroot", emptyOutDir: true, diff --git a/cista/__init__.py b/cista/__init__.py old mode 100755 new mode 100644 diff --git a/cista/__main__.py b/cista/__main__.py old mode 100755 new mode 100644 index bbddc8d..94f2afb --- a/cista/__main__.py +++ b/cista/__main__.py @@ -67,14 +67,14 @@ def _main(): # Maybe run without arguments print(doc) print( - "No config file found! Get started with:\n cista -l :8000 /path/to/files, or\n cista -l example.com --import-droppy # Uses Droppy files\n" + "No config file found! Get started with:\n cista -l :8000 /path/to/files, or\n cista -l example.com --import-droppy # Uses Droppy files\n", ) return 1 settings = {} if import_droppy: if exists: raise ValueError( - f"Importing Droppy: First remove the existing configuration:\n rm {config.conffile}" + f"Importing Droppy: First remove the existing configuration:\n rm {config.conffile}", ) settings = droppy.readconf() if path: @@ -95,6 +95,8 @@ def _main(): print(f"Serving {config.config.path} at {url}{extra}") # Run the server serve.run(dev=dev) + return 0 + def _confdir(args): diff --git a/cista/api.py b/cista/api.py index 39a4a18..cb1e41a 100644 --- a/cista/api.py +++ b/cista/api.py @@ -1,12 +1,13 @@ import asyncio import typing +from secrets import token_bytes import msgspec from sanic import Blueprint -from cista import watching +from cista import __version__, config, watching from cista.fileio import FileServer -from cista.protocol import ControlBase, FileRange, StatusMsg +from cista.protocol import ControlTypes, FileRange, StatusMsg from cista.util.apphelpers import asend, websocket_wrapper bp = Blueprint("api", url_prefix="/api") @@ -32,7 +33,7 @@ async def upload(req, ws): text = await ws.recv() if not isinstance(text, str): raise ValueError( - f"Expected JSON control, got binary len(data) = {len(text)}" + f"Expected JSON control, got binary len(data) = {len(text)}", ) req = msgspec.json.decode(text, type=FileRange) pos = req.start @@ -45,8 +46,8 @@ async def upload(req, ws): raise ValueError(f"Expected {req.end - pos} more bytes, got {d}") # Report success res = StatusMsg(status="ack", req=req) + print("ack", res) await asend(ws, res) - # await ws.drain() @bp.websocket("download") @@ -58,7 +59,7 @@ async def download(req, ws): text = await ws.recv() if not isinstance(text, str): raise ValueError( - f"Expected JSON control, got binary len(data) = {len(text)}" + f"Expected JSON control, got binary len(data) = {len(text)}", ) req = msgspec.json.decode(text, type=FileRange) pos = req.start @@ -70,23 +71,42 @@ async def download(req, ws): # Report success res = StatusMsg(status="ack", req=req) await asend(ws, res) - # await ws.drain() @bp.websocket("control") @websocket_wrapper async def control(req, ws): - cmd = msgspec.json.decode(await ws.recv(), type=ControlBase) - await asyncio.to_thread(cmd) - await asend(ws, StatusMsg(status="ack", req=cmd)) + while True: + cmd = msgspec.json.decode(await ws.recv(), type=ControlTypes) + await asyncio.to_thread(cmd) + await asend(ws, StatusMsg(status="ack", req=cmd)) + @bp.websocket("watch") @websocket_wrapper async def watch(req, ws): + await ws.send( + msgspec.json.encode( + { + "server": { + "name": "Cista", # Should be configurable + "version": __version__, + "public": config.config.public, + }, + "user": { + "username": req.ctx.username, + "privileged": req.ctx.user.privileged, + } + if req.ctx.user + else None, + } + ).decode() + ) + uuid = token_bytes(16) try: with watching.tree_lock: - q = watching.pubsub[ws] = asyncio.Queue() + q = watching.pubsub[uuid] = asyncio.Queue() # Init with disk usage and full tree await ws.send(watching.format_du()) await ws.send(watching.format_tree()) @@ -94,4 +114,4 @@ async def watch(req, ws): while True: await ws.send(await q.get()) finally: - del watching.pubsub[ws] + del watching.pubsub[uuid] diff --git a/cista/app.py b/cista/app.py old mode 100755 new mode 100644 index 6bc09e6..1d5906b --- a/cista/app.py +++ b/cista/app.py @@ -1,15 +1,31 @@ +import asyncio +import datetime import mimetypes +from collections import deque +from concurrent.futures import ThreadPoolExecutor from importlib.resources import files +from pathlib import Path +from stat import S_IFDIR, S_IFREG from urllib.parse import unquote +from wsgiref.handlers import format_date_time -from sanic import Blueprint, Sanic, raw +import brotli +import sanic.helpers +from blake3 import blake3 +from natsort import natsorted, ns +from sanic import Blueprint, Sanic, empty, raw from sanic.exceptions import Forbidden, NotFound +from sanic.log import logging +from stream_zip import ZIP_AUTO, stream_zip from cista import auth, config, session, watching from cista.api import bp -from cista.util import filename +from cista.protocol import DirEntry from cista.util.apphelpers import handle_sanic_exception +# Workaround until Sanic PR #2824 is merged +sanic.helpers._ENTITY_HEADERS = frozenset() + app = Sanic("cista", strict_slashes=True) app.blueprint(auth.bp) app.blueprint(bp) @@ -20,19 +36,25 @@ app.exception(Exception)(handle_sanic_exception) async def main_start(app, loop): config.load_config() await watching.start(app, loop) + app.ctx.threadexec = ThreadPoolExecutor(max_workers=8) + @app.after_server_stop async def main_stop(app, loop): await watching.stop(app, loop) + app.ctx.threadexec.shutdown() + @app.on_request async def use_session(req): req.ctx.session = session.get(req) try: + req.ctx.username = req.ctx.session["username"] req.ctx.user = config.config.users[req.ctx.session["username"]] # type: ignore except (AttributeError, KeyError, TypeError): + req.ctx.username = None req.ctx.user = None # CSRF protection if req.method == "GET" and req.headers.upgrade != "websocket": @@ -58,15 +80,168 @@ def http_fileserver(app, _): app.blueprint(bp) -@app.get("/", static=True) +www = {} + + +@app.before_server_start +async def load_wwwroot(*_ignored): + global www + www = await asyncio.get_event_loop().run_in_executor(None, _load_wwwroot, www) + + +def _load_wwwroot(www): + wwwnew = {} + base = files("cista") / "wwwroot" + paths = ["."] + while paths: + path = paths.pop(0) + current = base / path + for p in current.iterdir(): + if p.is_dir(): + paths.append(current / p.parts[-1]) + continue + name = p.relative_to(base).as_posix() + mime = mimetypes.guess_type(name)[0] or "application/octet-stream" + mtime = p.stat().st_mtime + data = p.read_bytes() + etag = blake3(data).hexdigest(length=8) + if name == "index.html": + name = "" + # Use old data if not changed + if name in www and www[name][2]["etag"] == etag: + wwwnew[name] = www[name] + continue + # Add charset definition + if mime.startswith("text/"): + mime = f"{mime}; charset=UTF-8" + # Asset files names will change whenever the content changes + cached = name.startswith("assets/") + headers = { + "etag": etag, + "last-modified": format_date_time(mtime), + "cache-control": "max-age=31536000, immutable" + if cached + else "no-cache", + "content-type": mime, + } + # Precompress with Brotli + br = brotli.compress(data) + if len(br) >= len(data): + br = False + wwwnew[name] = data, br, headers + return wwwnew + + +@app.add_task +async def refresh_wwwroot(): + while True: + try: + wwwold = www + await load_wwwroot() + changes = "" + for name in sorted(www): + attr = www[name] + if wwwold.get(name) == attr: + continue + headers = attr[2] + changes += f"{headers['last-modified']} {headers['etag']} /{name}\n" + for name in sorted(set(wwwold) - set(www)): + changes += f"Deleted /{name}\n" + if changes: + print(f"Updated wwwroot:\n{changes}", end="", flush=True) + except Exception as e: + print("Error loading wwwroot", e) + if not app.debug: + return + await asyncio.sleep(0.5) + + +@app.route("/", methods=["GET", "HEAD"]) async def wwwroot(req, path=""): """Frontend files only""" - name = filename.sanitize(unquote(path)) if path else "index.html" - try: - index = files("cista").joinpath("wwwroot", name).read_bytes() - except OSError as e: + name = unquote(path) + if name not in www: + raise NotFound(f"File not found: /{path}", extra={"name": name}) + data, br, headers = www[name] + if req.headers.if_none_match == headers["etag"]: + # The client has it cached, respond 304 Not Modified + return empty(304, headers=headers) + # Brotli compressed? + if br and "br" in req.headers.accept_encoding.split(", "): + headers = { + **headers, + "content-encoding": "br", + } + data = br + return raw(data, headers=headers) + + +@app.get("/zip//") +async def zip_download(req, keys, zipfile, ext): + """Download a zip archive of the given keys""" + wanted = set(keys.split("+")) + with watching.tree_lock: + q = deque([([], None, watching.tree[""].dir)]) + files = [] + while q: + locpar, relpar, d = q.pop() + for name, attr in d.items(): + loc = [*locpar, name] + rel = None + if relpar or attr.key in wanted: + rel = [*relpar, name] if relpar else [name] + wanted.discard(attr.key) + isdir = isinstance(attr, DirEntry) + if isdir: + q.append((loc, rel, attr.dir)) + if rel: + files.append( + ("/".join(rel), Path(watching.rootpath.joinpath(*loc))) + ) + + if not files: raise NotFound( - f"File not found: /{path}", extra={"name": name, "exception": repr(e)} + "No files found", + context={"keys": keys, "zipfile": zipfile, "wanted": wanted}, ) - mime = mimetypes.guess_type(name)[0] or "application/octet-stream" - return raw(index, content_type=mime) + if wanted: + raise NotFound("Files not found", context={"missing": wanted}) + + files = natsorted(files, key=lambda f: f[0], alg=ns.IGNORECASE) + + def local_files(files): + for rel, p in files: + s = p.stat() + size = s.st_size + modified = datetime.datetime.fromtimestamp(s.st_mtime, datetime.UTC) + if p.is_dir(): + yield rel, modified, S_IFDIR | 0o755, ZIP_AUTO(size), b"" + else: + yield rel, modified, S_IFREG | 0o644, ZIP_AUTO(size), contents(p) + + def contents(name): + with name.open("rb") as f: + while chunk := f.read(65536): + yield chunk + + def worker(): + try: + for chunk in stream_zip(local_files(files)): + asyncio.run_coroutine_threadsafe(queue.put(chunk), loop) + except Exception: + logging.exception("Error streaming ZIP") + raise + finally: + asyncio.run_coroutine_threadsafe(queue.put(None), loop) + + # Don't block the event loop: run in a thread + queue = asyncio.Queue(maxsize=1) + loop = asyncio.get_event_loop() + thread = loop.run_in_executor(app.ctx.threadexec, worker) + + # Stream the response + res = await req.respond(content_type="application/zip") + while chunk := await queue.get(): + await res.send(chunk) + + await thread # If it raises, the response will fail download diff --git a/cista/auth.py b/cista/auth.py old mode 100755 new mode 100644 index e1974e3..e4647c7 --- a/cista/auth.py +++ b/cista/auth.py @@ -25,7 +25,7 @@ def login(username: str, password: str): try: u = config.config.users[un.decode()] except KeyError: - raise ValueError("Invalid username") + raise ValueError("Invalid username") from None # Verify password need_rehash = False if not u.hash: @@ -41,7 +41,7 @@ def login(username: str, password: str): try: _argon.verify(u.hash, pw) except Exception: - raise ValueError("Invalid password") + raise ValueError("Invalid password") from None if _argon.check_needs_rehash(u.hash): need_rehash = True # Login successful @@ -62,7 +62,7 @@ class LoginResponse(msgspec.Struct): error: str = "" -def verify(request, privileged=False): +def verify(request, *, privileged=False): """Raise Unauthorized or Forbidden if the request is not authorized""" if privileged: if request.ctx.user: @@ -71,7 +71,8 @@ def verify(request, privileged=False): raise Forbidden("Access Forbidden: Only for privileged users") elif config.config.public or request.ctx.user: return - raise Unauthorized("Login required", "cookie", context={"redirect": "/login"}) + raise Unauthorized("Login required", "cookie") + bp = Blueprint("auth") @@ -130,11 +131,14 @@ async def login_post(request): if not username or not password: raise KeyError except KeyError: - raise BadRequest("Missing username or password", context={"redirect": "/login"}) + raise BadRequest( + "Missing username or password", + context={"redirect": "/login"}, + ) from None try: user = login(username, password) except ValueError as e: - raise Forbidden(str(e), context={"redirect": "/login"}) + raise Forbidden(str(e), context={"redirect": "/login"}) from e if "text/html" in request.headers.accept: res = redirect("/") diff --git a/cista/config.py b/cista/config.py old mode 100755 new mode 100644 index fa6fc9f..7883e00 --- a/cista/config.py +++ b/cista/config.py @@ -21,7 +21,8 @@ class Config(msgspec.Struct): class User(msgspec.Struct, omit_defaults=True): privileged: bool = False hash: str = "" - lastSeen: int = 0 + lastSeen: int = 0 # noqa: N815 + class Link(msgspec.Struct, omit_defaults=True): diff --git a/cista/droppy.py b/cista/droppy.py old mode 100755 new mode 100644 index 5636266..3271611 --- a/cista/droppy.py +++ b/cista/droppy.py @@ -30,10 +30,12 @@ def _droppy_listeners(cf): host = listener["host"] if isinstance(host, list): host = host[0] + except (KeyError, IndexError): + continue + else: if host in ("127.0.0.1", "::", "localhost"): return f":{port}" return f"{host}:{port}" - except (KeyError, IndexError): - continue + # If none matched, fallback to Droppy default return "0.0.0.0:8989" diff --git a/cista/fileio.py b/cista/fileio.py old mode 100755 new mode 100644 index 566aaea..8a821d2 --- a/cista/fileio.py +++ b/cista/fileio.py @@ -62,7 +62,9 @@ class FileServer: async def start(self): self.alink = AsyncLink() self.worker = asyncio.get_event_loop().run_in_executor( - None, self.worker_thread, self.alink.to_sync + None, + self.worker_thread, + self.alink.to_sync, ) self.cache = LRUCache(File, capacity=10, maxage=5.0) diff --git a/cista/protocol.py b/cista/protocol.py old mode 100755 new mode 100644 index 31f6eff..2e97176 --- a/cista/protocol.py +++ b/cista/protocol.py @@ -22,7 +22,8 @@ class MkDir(ControlBase): def __call__(self): path = config.config.path / filename.sanitize(self.path) - path.mkdir(parents=False, exist_ok=False) + path.mkdir(parents=True, exist_ok=False) + class Rename(ControlBase): @@ -44,7 +45,11 @@ class Rm(ControlBase): root = config.config.path sel = [root / filename.sanitize(p) for p in self.sel] for p in sel: - shutil.rmtree(p, ignore_errors=True) + if p.is_dir(): + shutil.rmtree(p) + else: + p.unlink() + class Mv(ControlBase): @@ -72,10 +77,19 @@ class Cp(ControlBase): if not dst.is_dir(): raise BadRequest("The destination must be a directory") for p in sel: - # Note: copies as dst rather than in dst unless name is appended. - shutil.copytree( - p, dst / p.name, dirs_exist_ok=True, ignore_dangling_symlinks=True - ) + if p.is_dir(): + # Note: copies as dst rather than in dst unless name is appended. + shutil.copytree( + p, + dst / p.name, + dirs_exist_ok=True, + ignore_dangling_symlinks=True, + ) + else: + shutil.copy2(p, dst) + + +ControlTypes = MkDir | Rename | Rm | Mv | Cp ## File uploads and downloads @@ -101,11 +115,13 @@ class ErrorMsg(msgspec.Struct): class FileEntry(msgspec.Struct): + key: str size: int mtime: int class DirEntry(msgspec.Struct): + key: str size: int mtime: int dir: DirList @@ -133,7 +149,8 @@ DirList = dict[str, FileEntry | DirEntry] class UpdateEntry(msgspec.Struct, omit_defaults=True): """Updates the named entry in the tree. Fields that are set replace old values. A list of entries recurses directories.""" - name: str = "" + name: str + key: str deleted: bool = False size: int | None = None mtime: int | None = None @@ -141,12 +158,12 @@ class UpdateEntry(msgspec.Struct, omit_defaults=True): def make_dir_data(root): - if len(root) == 2: + if len(root) == 3: return FileEntry(*root) - size, mtime, listing = root + id_, size, mtime, listing = root converted = {} for name, data in listing.items(): converted[name] = make_dir_data(data) sz = sum(x.size for x in converted.values()) mt = max(x.mtime for x in converted.values()) - return DirEntry(sz, max(mt, mtime), converted) + return DirEntry(id_, sz, max(mt, mtime), converted) diff --git a/cista/serve.py b/cista/serve.py old mode 100755 new mode 100644 index 05c1232..8ac2166 --- a/cista/serve.py +++ b/cista/serve.py @@ -7,7 +7,7 @@ from sanic import Sanic from cista import config, server80 -def run(dev=False): +def run(*, dev=False): """Run Sanic main process that spawns worker processes to serve HTTP requests.""" from .app import app @@ -30,7 +30,11 @@ def run(dev=False): reload_dir={confdir, wwwroot}, access_log=True, ) # type: ignore - Sanic.serve() + if dev: + Sanic.serve() + else: + Sanic.serve_single() + def check_cert(certdir, domain): @@ -38,7 +42,7 @@ def check_cert(certdir, domain): return # TODO: Use certbot to fetch a cert raise ValueError( - f"TLS certificate files privkey.pem and fullchain.pem needed in {certdir}" + f"TLS certificate files privkey.pem and fullchain.pem needed in {certdir}", ) @@ -47,15 +51,14 @@ def parse_listen(listen): unix = Path(listen).resolve() if not unix.parent.exists(): raise ValueError( - f"Directory for unix socket does not exist: {unix.parent}/" + f"Directory for unix socket does not exist: {unix.parent}/", ) return "http://localhost", {"unix": unix} - elif re.fullmatch(r"(\w+(-\w+)*\.)+\w{2,}", listen, re.UNICODE): + if re.fullmatch(r"(\w+(-\w+)*\.)+\w{2,}", listen, re.UNICODE): return f"https://{listen}", {"host": listen, "port": 443, "ssl": True} - else: - try: - addr, _port = listen.split(":", 1) - port = int(_port) - except Exception: - raise ValueError(f"Invalid listen address: {listen}") - return f"http://localhost:{port}", {"host": addr, "port": port} + try: + addr, _port = listen.split(":", 1) + port = int(_port) + except Exception: + raise ValueError(f"Invalid listen address: {listen}") from None + return f"http://localhost:{port}", {"host": addr, "port": port} diff --git a/cista/session.py b/cista/session.py old mode 100755 new mode 100644 diff --git a/cista/util/__init__.py b/cista/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cista/util/apphelpers.py b/cista/util/apphelpers.py index 7043354..ee2562d 100644 --- a/cista/util/apphelpers.py +++ b/cista/util/apphelpers.py @@ -33,7 +33,8 @@ async def handle_sanic_exception(request, e): # Non-browsers get JSON errors if "text/html" not in request.headers.accept: return jres( - ErrorMsg({"code": code, "message": message, **context}), status=code + ErrorMsg({"code": code, "message": message, **context}), + status=code, ) # Redirections flash the error message via cookies if "redirect" in context: diff --git a/cista/util/asynclink.py b/cista/util/asynclink.py old mode 100755 new mode 100644 index 85b98cc..dee1acb --- a/cista/util/asynclink.py +++ b/cista/util/asynclink.py @@ -80,8 +80,9 @@ class SyncRequest: if exc: self.set_exception(exc) return True - elif not self.done: + if not self.done: self.set_result(None) + return None def set_result(self, value): """Set result value; mark as done.""" diff --git a/cista/util/filename.py b/cista/util/filename.py index c464b2d..9fb3108 100644 --- a/cista/util/filename.py +++ b/cista/util/filename.py @@ -10,4 +10,7 @@ def sanitize(filename: str) -> str: filename = filename.replace("\\", "-") filename = sanitize_filepath(filename) filename = filename.strip("/") - return PurePosixPath(filename).as_posix() + p = PurePosixPath(filename) + if any(n.startswith(".") for n in p.parts): + raise ValueError("Filenames starting with dot are not allowed") + return p.as_posix() diff --git a/cista/util/lrucache.py b/cista/util/lrucache.py old mode 100755 new mode 100644 index 528403f..89154dd --- a/cista/util/lrucache.py +++ b/cista/util/lrucache.py @@ -41,7 +41,7 @@ class LRUCache: The corresponding item's handle. """ # Take from cache or open a new one - for i, (k, f, ts) in enumerate(self.cache): + for i, (k, f, _ts) in enumerate(self.cache): # noqa: B007 if k == key: self.cache.pop(i) break diff --git a/cista/watching.py b/cista/watching.py old mode 100755 new mode 100644 index d383ddc..fd4861f --- a/cista/watching.py +++ b/cista/watching.py @@ -1,13 +1,15 @@ import asyncio import shutil +import sys import threading import time from pathlib import Path, PurePosixPath -import inotify.adapters import msgspec +from sanic.log import logging from cista import config +from cista.fileio import fuid from cista.protocol import DirEntry, FileEntry, UpdateEntry pubsub = {} @@ -28,7 +30,8 @@ disk_usage = None def watcher_thread(loop): - global disk_usage + global disk_usage, rootpath + import inotify.adapters while True: rootpath = config.config.path @@ -66,11 +69,33 @@ def watcher_thread(loop): try: update(path.relative_to(rootpath), loop) except Exception as e: - print("Watching error", e) - break + print("Watching error", e, path, rootpath) + raise i = None # Free the inotify object +def watcher_thread_poll(loop): + global disk_usage, rootpath + + while not quit: + rootpath = config.config.path + old = format_tree() if tree[""] else None + with tree_lock: + # Initialize the tree from filesystem + tree[""] = walk(rootpath) + msg = format_tree() + if msg != old: + asyncio.run_coroutine_threadsafe(broadcast(msg), loop) + + # Disk usage update + du = shutil.disk_usage(rootpath) + if du != disk_usage: + disk_usage = du + asyncio.run_coroutine_threadsafe(broadcast(format_du()), loop) + + time.sleep(1.0) + + def format_du(): return msgspec.json.encode( { @@ -79,24 +104,24 @@ def format_du(): "used": disk_usage.used, "free": disk_usage.free, "storage": tree[""].size, - } - } + }, + }, ).decode() def format_tree(): root = tree[""] - return msgspec.json.encode( - {"update": [UpdateEntry(size=root.size, mtime=root.mtime, dir=root.dir)]} - ).decode() + return msgspec.json.encode({"root": root}).decode() def walk(path: Path) -> DirEntry | FileEntry | None: try: s = path.stat() + key = fuid(s) + assert key, repr(key) mtime = int(s.st_mtime) if path.is_file(): - return FileEntry(s.st_size, mtime) + return FileEntry(key, s.st_size, mtime) tree = { p.name: v @@ -106,10 +131,10 @@ def walk(path: Path) -> DirEntry | FileEntry | None: } if tree: size = sum(v.size for v in tree.values()) - mtime = max(mtime, max(v.mtime for v in tree.values())) + mtime = max(mtime, *(v.mtime for v in tree.values())) else: size = 0 - return DirEntry(size, mtime, tree) + return DirEntry(key, size, mtime, tree) except FileNotFoundError: return None except OSError as e: @@ -119,6 +144,8 @@ def walk(path: Path) -> DirEntry | FileEntry | None: def update(relpath: PurePosixPath, loop): """Called by inotify updates, check the filesystem and broadcast any changes.""" + if rootpath is None or relpath is None: + print("ERROR", rootpath, relpath) new = walk(rootpath / relpath) with tree_lock: update = update_internal(relpath, new) @@ -129,7 +156,8 @@ def update(relpath: PurePosixPath, loop): def update_internal( - relpath: PurePosixPath, new: DirEntry | FileEntry | None + relpath: PurePosixPath, + new: DirEntry | FileEntry | None, ) -> list[UpdateEntry]: path = "", *relpath.parts old = tree @@ -158,7 +186,7 @@ def update_internal( # Update parents update = [] for name, entry in elems[:-1]: - u = UpdateEntry(name) + u = UpdateEntry(name, entry.key) if szdiff: entry.size += szdiff u.size = entry.size @@ -168,16 +196,15 @@ def update_internal( # The last element is the one that changed name, entry = elems[-1] parent = elems[-2][1] if len(elems) > 1 else tree - u = UpdateEntry(name) + u = UpdateEntry(name, new.key if new else entry.key) if new: parent[name] = new if u.size != new.size: u.size = new.size if u.mtime != new.mtime: u.mtime = new.mtime - if isinstance(new, DirEntry): - if u.dir == new.dir: - u.dir = new.dir + if isinstance(new, DirEntry) and u.dir != new.dir: + u.dir = new.dir else: del parent[name] u.deleted = True @@ -186,13 +213,21 @@ def update_internal( async def broadcast(msg): - for queue in pubsub.values(): - await queue.put_nowait(msg) + try: + for queue in pubsub.values(): + queue.put_nowait(msg) + except Exception: + # Log because asyncio would silently eat the error + logging.exception("Broadcast error") + async def start(app, loop): config.load_config() - app.ctx.watcher = threading.Thread(target=watcher_thread, args=[loop]) + app.ctx.watcher = threading.Thread( + target=watcher_thread if sys.platform == "linux" else watcher_thread_poll, + args=[loop], + ) app.ctx.watcher.start() diff --git a/cista/wwwroot/assets/AboutView-4d995ba2.css b/cista/wwwroot/assets/AboutView-4d995ba2.css deleted file mode 100644 index f067b5d..0000000 --- a/cista/wwwroot/assets/AboutView-4d995ba2.css +++ /dev/null @@ -1 +0,0 @@ -@media (min-width: 1024px){.about{min-height:100vh;display:flex;align-items:center}} diff --git a/cista/wwwroot/assets/AboutView-ba1efa64.js b/cista/wwwroot/assets/AboutView-ba1efa64.js deleted file mode 100644 index e1c2f46..0000000 --- a/cista/wwwroot/assets/AboutView-ba1efa64.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as t,c as o,a as s}from"./index-689b26c8.js";const _={},c={class:"about"},a=s("h1",null,"This is an about page",-1),n=[a];function i(r,u){return t(),o("div",c,n)}const l=e(_,[["render",i]]);export{l as default}; diff --git a/cista/wwwroot/assets/index-689b26c8.js b/cista/wwwroot/assets/index-689b26c8.js deleted file mode 100644 index f09ce39..0000000 --- a/cista/wwwroot/assets/index-689b26c8.js +++ /dev/null @@ -1,9 +0,0 @@ -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))s(r);new MutationObserver(r=>{for(const o of r)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&s(i)}).observe(document,{childList:!0,subtree:!0});function n(r){const o={};return r.integrity&&(o.integrity=r.integrity),r.referrerPolicy&&(o.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?o.credentials="include":r.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function s(r){if(r.ep)return;r.ep=!0;const o=n(r);fetch(r.href,o)}})();function es(e,t){const n=Object.create(null),s=e.split(",");for(let r=0;r!!n[r.toLowerCase()]:r=>!!n[r]}const G={},_t=[],Re=()=>{},To=()=>!1,$o=/^on[^a-z]/,mn=e=>$o.test(e),ts=e=>e.startsWith("onUpdate:"),oe=Object.assign,ns=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Ho=Object.prototype.hasOwnProperty,U=(e,t)=>Ho.call(e,t),F=Array.isArray,vt=e=>gn(e)==="[object Map]",wr=e=>gn(e)==="[object Set]",N=e=>typeof e=="function",re=e=>typeof e=="string",ss=e=>typeof e=="symbol",ee=e=>e!==null&&typeof e=="object",Er=e=>(ee(e)||N(e))&&N(e.then)&&N(e.catch),xr=Object.prototype.toString,gn=e=>xr.call(e),jo=e=>gn(e).slice(8,-1),Rr=e=>gn(e)==="[object Object]",rs=e=>re(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,sn=es(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),_n=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Fo=/-(\w)/g,wt=_n(e=>e.replace(Fo,(t,n)=>n?n.toUpperCase():"")),Lo=/\B([A-Z])/g,Mt=_n(e=>e.replace(Lo,"-$1").toLowerCase()),Cr=_n(e=>e.charAt(0).toUpperCase()+e.slice(1)),On=_n(e=>e?`on${Cr(e)}`:""),ct=(e,t)=>!Object.is(e,t),Mn=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},No=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let Os;const Ln=()=>Os||(Os=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function os(e){if(F(e)){const t={};for(let n=0;n{if(n){const s=n.split(Bo);s.length>1&&(t[s[0].trim()]=s[1].trim())}}),t}function is(e){let t="";if(re(e))t=e;else if(F(e))for(let n=0;nre(e)?e:e==null?"":F(e)||ee(e)&&(e.toString===xr||!N(e.toString))?JSON.stringify(e,Or,2):String(e),Or=(e,t)=>t&&t.__v_isRef?Or(e,t.value):vt(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[s,r])=>(n[`${s} =>`]=r,n),{})}:wr(t)?{[`Set(${t.size})`]:[...t.values()]}:ee(t)&&!F(t)&&!Rr(t)?String(t):t;let be;class Mr{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=be,!t&&be&&(this.index=(be.scopes||(be.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=be;try{return be=this,t()}finally{be=n}}}on(){be=this}off(){be=this.parent}stop(t){if(this._active){let n,s;for(n=0,s=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},Ar=e=>(e.w&Ge)>0,zr=e=>(e.n&Ge)>0,Jo=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let s=0;s{(h==="length"||h>=l)&&u.push(a)})}else switch(n!==void 0&&u.push(i.get(n)),t){case"add":F(e)?rs(n)&&u.push(i.get("length")):(u.push(i.get(it)),vt(e)&&u.push(i.get(Bn)));break;case"delete":F(e)||(u.push(i.get(it)),vt(e)&&u.push(i.get(Bn)));break;case"set":vt(e)&&u.push(i.get(it));break}if(u.length===1)u[0]&&Dn(u[0]);else{const l=[];for(const a of u)a&&l.push(...a);Dn(ls(l))}}function Dn(e,t){const n=F(e)?e:[...e];for(const s of n)s.computed&&As(s);for(const s of n)s.computed||As(s)}function As(e,t){(e!==we||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}const Zo=es("__proto__,__v_isRef,__isVue"),Tr=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(ss)),zs=Go();function Go(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const s=V(this);for(let o=0,i=this.length;o{e[t]=function(...n){At();const s=V(this)[t].apply(this,n);return zt(),s}}),e}function ei(e){const t=V(this);return ge(t,"has",e),t.hasOwnProperty(e)}class $r{constructor(t=!1,n=!1){this._isReadonly=t,this._shallow=n}get(t,n,s){const r=this._isReadonly,o=this._shallow;if(n==="__v_isReactive")return!r;if(n==="__v_isReadonly")return r;if(n==="__v_isShallow")return o;if(n==="__v_raw"&&s===(r?o?di:Lr:o?Fr:jr).get(t))return t;const i=F(t);if(!r){if(i&&U(zs,n))return Reflect.get(zs,n,s);if(n==="hasOwnProperty")return ei}const u=Reflect.get(t,n,s);return(ss(n)?Tr.has(n):Zo(n))||(r||ge(t,"get",n),o)?u:fe(u)?i&&rs(n)?u:u.value:ee(u)?r?kr(u):yn(u):u}}class Hr extends $r{constructor(t=!1){super(!1,t)}set(t,n,s,r){let o=t[n];if(Et(o)&&fe(o)&&!fe(s))return!1;if(!this._shallow&&(!un(s)&&!Et(s)&&(o=V(o),s=V(s)),!F(t)&&fe(o)&&!fe(s)))return o.value=s,!0;const i=F(t)&&rs(n)?Number(n)e,vn=e=>Reflect.getPrototypeOf(e);function Xt(e,t,n=!1,s=!1){e=e.__v_raw;const r=V(e),o=V(t);n||(ct(t,o)&&ge(r,"get",t),ge(r,"get",o));const{has:i}=vn(r),u=s?us:n?ds:Kt;if(i.call(r,t))return u(e.get(t));if(i.call(r,o))return u(e.get(o));e!==r&&e.get(t)}function Zt(e,t=!1){const n=this.__v_raw,s=V(n),r=V(e);return t||(ct(e,r)&&ge(s,"has",e),ge(s,"has",r)),e===r?n.has(e):n.has(e)||n.has(r)}function Gt(e,t=!1){return e=e.__v_raw,!t&&ge(V(e),"iterate",it),Reflect.get(e,"size",e)}function Is(e){e=V(e);const t=V(this);return vn(t).has.call(t,e)||(t.add(e),Be(t,"add",e,e)),this}function Ss(e,t){t=V(t);const n=V(this),{has:s,get:r}=vn(n);let o=s.call(n,e);o||(e=V(e),o=s.call(n,e));const i=r.call(n,e);return n.set(e,t),o?ct(t,i)&&Be(n,"set",e,t):Be(n,"add",e,t),this}function Ts(e){const t=V(this),{has:n,get:s}=vn(t);let r=n.call(t,e);r||(e=V(e),r=n.call(t,e)),s&&s.call(t,e);const o=t.delete(e);return r&&Be(t,"delete",e,void 0),o}function $s(){const e=V(this),t=e.size!==0,n=e.clear();return t&&Be(e,"clear",void 0,void 0),n}function en(e,t){return function(s,r){const o=this,i=o.__v_raw,u=V(i),l=t?us:e?ds:Kt;return!e&&ge(u,"iterate",it),i.forEach((a,h)=>s.call(r,l(a),l(h),o))}}function tn(e,t,n){return function(...s){const r=this.__v_raw,o=V(r),i=vt(o),u=e==="entries"||e===Symbol.iterator&&i,l=e==="keys"&&i,a=r[e](...s),h=n?us:t?ds:Kt;return!t&&ge(o,"iterate",l?Bn:it),{next(){const{value:p,done:m}=a.next();return m?{value:p,done:m}:{value:u?[h(p[0]),h(p[1])]:h(p),done:m}},[Symbol.iterator](){return this}}}}function qe(e){return function(...t){return e==="delete"?!1:this}}function oi(){const e={get(o){return Xt(this,o)},get size(){return Gt(this)},has:Zt,add:Is,set:Ss,delete:Ts,clear:$s,forEach:en(!1,!1)},t={get(o){return Xt(this,o,!1,!0)},get size(){return Gt(this)},has:Zt,add:Is,set:Ss,delete:Ts,clear:$s,forEach:en(!1,!0)},n={get(o){return Xt(this,o,!0)},get size(){return Gt(this,!0)},has(o){return Zt.call(this,o,!0)},add:qe("add"),set:qe("set"),delete:qe("delete"),clear:qe("clear"),forEach:en(!0,!1)},s={get(o){return Xt(this,o,!0,!0)},get size(){return Gt(this,!0)},has(o){return Zt.call(this,o,!0)},add:qe("add"),set:qe("set"),delete:qe("delete"),clear:qe("clear"),forEach:en(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(o=>{e[o]=tn(o,!1,!1),n[o]=tn(o,!0,!1),t[o]=tn(o,!1,!0),s[o]=tn(o,!0,!0)}),[e,n,t,s]}const[ii,li,ci,ui]=oi();function fs(e,t){const n=t?e?ui:ci:e?li:ii;return(s,r,o)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?s:Reflect.get(U(n,r)&&r in s?n:s,r,o)}const fi={get:fs(!1,!1)},ai={get:fs(!1,!0)},hi={get:fs(!0,!1)},jr=new WeakMap,Fr=new WeakMap,Lr=new WeakMap,di=new WeakMap;function pi(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function mi(e){return e.__v_skip||!Object.isExtensible(e)?0:pi(jo(e))}function yn(e){return Et(e)?e:as(e,!1,ni,fi,jr)}function Nr(e){return as(e,!1,ri,ai,Fr)}function kr(e){return as(e,!0,si,hi,Lr)}function as(e,t,n,s,r){if(!ee(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const o=r.get(e);if(o)return o;const i=mi(e);if(i===0)return e;const u=new Proxy(e,i===2?s:n);return r.set(e,u),u}function yt(e){return Et(e)?yt(e.__v_raw):!!(e&&e.__v_isReactive)}function Et(e){return!!(e&&e.__v_isReadonly)}function un(e){return!!(e&&e.__v_isShallow)}function Br(e){return yt(e)||Et(e)}function V(e){const t=e&&e.__v_raw;return t?V(t):e}function hs(e){return cn(e,"__v_skip",!0),e}const Kt=e=>ee(e)?yn(e):e,ds=e=>ee(e)?kr(e):e;function Dr(e){Xe&&we&&(e=V(e),Sr(e.dep||(e.dep=ls())))}function Ur(e,t){e=V(e);const n=e.dep;n&&Dn(n)}function fe(e){return!!(e&&e.__v_isRef===!0)}function Kr(e){return Vr(e,!1)}function gi(e){return Vr(e,!0)}function Vr(e,t){return fe(e)?e:new _i(e,t)}class _i{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:V(t),this._value=n?t:Kt(t)}get value(){return Dr(this),this._value}set value(t){const n=this.__v_isShallow||un(t)||Et(t);t=n?t:V(t),ct(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Kt(t),Ur(this))}}function De(e){return fe(e)?e.value:e}const vi={get:(e,t,n)=>De(Reflect.get(e,t,n)),set:(e,t,n,s)=>{const r=e[t];return fe(r)&&!fe(n)?(r.value=n,!0):Reflect.set(e,t,n,s)}};function Wr(e){return yt(e)?e:new Proxy(e,vi)}class yi{constructor(t,n,s,r){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new cs(t,()=>{this._dirty||(this._dirty=!0,Ur(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=s}get value(){const t=V(this);return Dr(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function bi(e,t,n=!1){let s,r;const o=N(e);return o?(s=e,r=Re):(s=e.get,r=e.set),new yi(s,r,o||!r,n)}function Ze(e,t,n,s){let r;try{r=s?e(...s):e()}catch(o){bn(o,t,n)}return r}function Ce(e,t,n,s){if(N(e)){const o=Ze(e,t,n,s);return o&&Er(o)&&o.catch(i=>{bn(i,t,n)}),o}const r=[];for(let o=0;o>>1;Wt(ce[s])He&&ce.splice(t,1)}function Ri(e){F(e)?bt.push(...e):(!ke||!ke.includes(e,e.allowRecurse?rt+1:rt))&&bt.push(e),Qr()}function Hs(e,t=Vt?He+1:0){for(;tWt(n)-Wt(s)),rt=0;rte.id==null?1/0:e.id,Ci=(e,t)=>{const n=Wt(e)-Wt(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Xr(e){Un=!1,Vt=!0,ce.sort(Ci);const t=Re;try{for(He=0;Here(w)?w.trim():w)),p&&(r=n.map(No))}let u,l=s[u=On(t)]||s[u=On(wt(t))];!l&&o&&(l=s[u=On(Mt(t))]),l&&Ce(l,e,6,r);const a=s[u+"Once"];if(a){if(!e.emitted)e.emitted={};else if(e.emitted[u])return;e.emitted[u]=!0,Ce(a,e,6,r)}}function Zr(e,t,n=!1){const s=t.emitsCache,r=s.get(e);if(r!==void 0)return r;const o=e.emits;let i={},u=!1;if(!N(e)){const l=a=>{const h=Zr(a,t,!0);h&&(u=!0,oe(i,h))};!n&&t.mixins.length&&t.mixins.forEach(l),e.extends&&l(e.extends),e.mixins&&e.mixins.forEach(l)}return!o&&!u?(ee(e)&&s.set(e,null),null):(F(o)?o.forEach(l=>i[l]=null):oe(i,o),ee(e)&&s.set(e,i),i)}function wn(e,t){return!e||!mn(t)?!1:(t=t.slice(2).replace(/Once$/,""),U(e,t[0].toLowerCase()+t.slice(1))||U(e,Mt(t))||U(e,t))}let me=null,En=null;function fn(e){const t=me;return me=e,En=e&&e.type.__scopeId||null,t}function Gr(e){En=e}function eo(){En=null}function se(e,t=me,n){if(!t||e._n)return e;const s=(...r)=>{s._d&&Vs(-1);const o=fn(t);let i;try{i=e(...r)}finally{fn(o),s._d&&Vs(1)}return i};return s._n=!0,s._c=!0,s._d=!0,s}function An(e){const{type:t,vnode:n,proxy:s,withProxy:r,props:o,propsOptions:[i],slots:u,attrs:l,emit:a,render:h,renderCache:p,data:m,setupState:w,ctx:M,inheritAttrs:z}=e;let L,T;const $=fn(e);try{if(n.shapeFlag&4){const H=r||s;L=$e(h.call(H,H,p,o,w,m,M)),T=l}else{const H=t;L=$e(H.length>1?H(o,{attrs:l,slots:u,emit:a}):H(o,null)),T=t.props?l:Oi(l)}}catch(H){Bt.length=0,bn(H,e,1),L=J(xt)}let K=L;if(T&&z!==!1){const H=Object.keys(T),{shapeFlag:ie}=K;H.length&&ie&7&&(i&&H.some(ts)&&(T=Mi(T,i)),K=Rt(K,T))}return n.dirs&&(K=Rt(K),K.dirs=K.dirs?K.dirs.concat(n.dirs):n.dirs),n.transition&&(K.transition=n.transition),L=K,fn($),L}const Oi=e=>{let t;for(const n in e)(n==="class"||n==="style"||mn(n))&&((t||(t={}))[n]=e[n]);return t},Mi=(e,t)=>{const n={};for(const s in e)(!ts(s)||!(s.slice(9)in t))&&(n[s]=e[s]);return n};function Ai(e,t,n){const{props:s,children:r,component:o}=e,{props:i,children:u,patchFlag:l}=t,a=o.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&l>=0){if(l&1024)return!0;if(l&16)return s?js(s,i,a):!!i;if(l&8){const h=t.dynamicProps;for(let p=0;pe.__isSuspense;function Si(e,t){t&&t.pendingBranch?F(e)?t.effects.push(...e):t.effects.push(e):Ri(e)}const nn={};function rn(e,t,n){return to(e,t,n)}function to(e,t,{immediate:n,deep:s,flush:r,onTrack:o,onTrigger:i}=G){var u;const l=Qo()===((u=ue)==null?void 0:u.scope)?ue:null;let a,h=!1,p=!1;if(fe(e)?(a=()=>e.value,h=un(e)):yt(e)?(a=()=>e,s=!0):F(e)?(p=!0,h=e.some(H=>yt(H)||un(H)),a=()=>e.map(H=>{if(fe(H))return H.value;if(yt(H))return gt(H);if(N(H))return Ze(H,l,2)})):N(e)?t?a=()=>Ze(e,l,2):a=()=>{if(!(l&&l.isUnmounted))return m&&m(),Ce(e,l,3,[w])}:a=Re,t&&s){const H=a;a=()=>gt(H())}let m,w=H=>{m=$.onStop=()=>{Ze(H,l,4)}},M;if(Yt)if(w=Re,t?n&&Ce(t,l,3,[a(),p?[]:void 0,w]):a(),r==="sync"){const H=Rl();M=H.__watcherHandles||(H.__watcherHandles=[])}else return Re;let z=p?new Array(e.length).fill(nn):nn;const L=()=>{if($.active)if(t){const H=$.run();(s||h||(p?H.some((ie,ae)=>ct(ie,z[ae])):ct(H,z)))&&(m&&m(),Ce(t,l,3,[H,z===nn?void 0:p&&z[0]===nn?[]:z,w]),z=H)}else $.run()};L.allowRecurse=!!t;let T;r==="sync"?T=L:r==="post"?T=()=>pe(L,l&&l.suspense):(L.pre=!0,l&&(L.id=l.uid),T=()=>ms(L));const $=new cs(a,T);t?n?L():z=$.run():r==="post"?pe($.run.bind($),l&&l.suspense):$.run();const K=()=>{$.stop(),l&&l.scope&&ns(l.scope.effects,$)};return M&&M.push(K),K}function Ti(e,t,n){const s=this.proxy,r=re(e)?e.includes(".")?no(s,e):()=>s[e]:e.bind(s,s);let o;N(t)?o=t:(o=t.handler,n=t);const i=ue;Ct(this);const u=to(r,o.bind(s),n);return i?Ct(i):lt(),u}function no(e,t){const n=t.split(".");return()=>{let s=e;for(let r=0;r{gt(n,t)});else if(Rr(e))for(const n in e)gt(e[n],t);return e}function nt(e,t,n,s){const r=e.dirs,o=t&&t.dirs;for(let i=0;ioe({name:e.name},t,{setup:e}))():e}const Nt=e=>!!e.type.__asyncLoader,so=e=>e.type.__isKeepAlive;function $i(e,t){ro(e,"a",t)}function Hi(e,t){ro(e,"da",t)}function ro(e,t,n=ue){const s=e.__wdc||(e.__wdc=()=>{let r=n;for(;r;){if(r.isDeactivated)return;r=r.parent}return e()});if(xn(t,s,n),n){let r=n.parent;for(;r&&r.parent;)so(r.parent.vnode)&&ji(s,t,n,r),r=r.parent}}function ji(e,t,n,s){const r=xn(t,e,s,!0);oo(()=>{ns(s[t],r)},n)}function xn(e,t,n=ue,s=!1){if(n){const r=n[e]||(n[e]=[]),o=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;At(),Ct(n);const u=Ce(t,n,e,i);return lt(),zt(),u});return s?r.unshift(o):r.push(o),o}}const Ke=e=>(t,n=ue)=>(!Yt||e==="sp")&&xn(e,(...s)=>t(...s),n),Fi=Ke("bm"),Li=Ke("m"),Ni=Ke("bu"),ki=Ke("u"),Bi=Ke("bum"),oo=Ke("um"),Di=Ke("sp"),Ui=Ke("rtg"),Ki=Ke("rtc");function Vi(e,t=ue){xn("ec",e,t)}const Wi=Symbol.for("v-ndc");function zn(e,t,n={},s,r){if(me.isCE||me.parent&&Nt(me.parent)&&me.parent.isCE)return t!=="default"&&(n.name=t),J("slot",n,s&&s());let o=e[t];o&&o._c&&(o._d=!1),Oe();const i=o&&io(o(n)),u=al(ve,{key:n.key||i&&i.key||`_${t}`},i||(s?s():[]),i&&e._===1?64:-2);return!r&&u.scopeId&&(u.slotScopeIds=[u.scopeId+"-s"]),o&&o._c&&(o._d=!0),u}function io(e){return e.some(t=>dn(t)?!(t.type===xt||t.type===ve&&!io(t.children)):!0)?e:null}const Kn=e=>e?vo(e)?bs(e)||e.proxy:Kn(e.parent):null,kt=oe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Kn(e.parent),$root:e=>Kn(e.root),$emit:e=>e.emit,$options:e=>gs(e),$forceUpdate:e=>e.f||(e.f=()=>ms(e.update)),$nextTick:e=>e.n||(e.n=Yr.bind(e.proxy)),$watch:e=>Ti.bind(e)}),In=(e,t)=>e!==G&&!e.__isScriptSetup&&U(e,t),qi={get({_:e},t){const{ctx:n,setupState:s,data:r,props:o,accessCache:i,type:u,appContext:l}=e;let a;if(t[0]!=="$"){const w=i[t];if(w!==void 0)switch(w){case 1:return s[t];case 2:return r[t];case 4:return n[t];case 3:return o[t]}else{if(In(s,t))return i[t]=1,s[t];if(r!==G&&U(r,t))return i[t]=2,r[t];if((a=e.propsOptions[0])&&U(a,t))return i[t]=3,o[t];if(n!==G&&U(n,t))return i[t]=4,n[t];Vn&&(i[t]=0)}}const h=kt[t];let p,m;if(h)return t==="$attrs"&&ge(e,"get",t),h(e);if((p=u.__cssModules)&&(p=p[t]))return p;if(n!==G&&U(n,t))return i[t]=4,n[t];if(m=l.config.globalProperties,U(m,t))return m[t]},set({_:e},t,n){const{data:s,setupState:r,ctx:o}=e;return In(r,t)?(r[t]=n,!0):s!==G&&U(s,t)?(s[t]=n,!0):U(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(o[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:s,appContext:r,propsOptions:o}},i){let u;return!!n[i]||e!==G&&U(e,i)||In(t,i)||(u=o[0])&&U(u,i)||U(s,i)||U(kt,i)||U(r.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:U(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function Fs(e){return F(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let Vn=!0;function Yi(e){const t=gs(e),n=e.proxy,s=e.ctx;Vn=!1,t.beforeCreate&&Ls(t.beforeCreate,e,"bc");const{data:r,computed:o,methods:i,watch:u,provide:l,inject:a,created:h,beforeMount:p,mounted:m,beforeUpdate:w,updated:M,activated:z,deactivated:L,beforeDestroy:T,beforeUnmount:$,destroyed:K,unmounted:H,render:ie,renderTracked:ae,renderTriggered:Me,errorCaptured:Fe,serverPrefetch:ut,expose:Ae,inheritAttrs:Ve,components:tt,directives:ze,filters:St}=t;if(a&&Qi(a,s,null),i)for(const X in i){const W=i[X];N(W)&&(s[X]=W.bind(n))}if(r){const X=r.call(n,n);ee(X)&&(e.data=yn(X))}if(Vn=!0,o)for(const X in o){const W=o[X],Le=N(W)?W.bind(n,n):N(W.get)?W.get.bind(n,n):Re,We=!N(W)&&N(W.set)?W.set.bind(n):Re,Ie=Ee({get:Le,set:We});Object.defineProperty(s,X,{enumerable:!0,configurable:!0,get:()=>Ie.value,set:de=>Ie.value=de})}if(u)for(const X in u)lo(u[X],s,n,X);if(l){const X=N(l)?l.call(n):l;Reflect.ownKeys(X).forEach(W=>{on(W,X[W])})}h&&Ls(h,e,"c");function ne(X,W){F(W)?W.forEach(Le=>X(Le.bind(n))):W&&X(W.bind(n))}if(ne(Fi,p),ne(Li,m),ne(Ni,w),ne(ki,M),ne($i,z),ne(Hi,L),ne(Vi,Fe),ne(Ki,ae),ne(Ui,Me),ne(Bi,$),ne(oo,H),ne(Di,ut),F(Ae))if(Ae.length){const X=e.exposed||(e.exposed={});Ae.forEach(W=>{Object.defineProperty(X,W,{get:()=>n[W],set:Le=>n[W]=Le})})}else e.exposed||(e.exposed={});ie&&e.render===Re&&(e.render=ie),Ve!=null&&(e.inheritAttrs=Ve),tt&&(e.components=tt),ze&&(e.directives=ze)}function Qi(e,t,n=Re){F(e)&&(e=Wn(e));for(const s in e){const r=e[s];let o;ee(r)?"default"in r?o=Ue(r.from||s,r.default,!0):o=Ue(r.from||s):o=Ue(r),fe(o)?Object.defineProperty(t,s,{enumerable:!0,configurable:!0,get:()=>o.value,set:i=>o.value=i}):t[s]=o}}function Ls(e,t,n){Ce(F(e)?e.map(s=>s.bind(t.proxy)):e.bind(t.proxy),t,n)}function lo(e,t,n,s){const r=s.includes(".")?no(n,s):()=>n[s];if(re(e)){const o=t[e];N(o)&&rn(r,o)}else if(N(e))rn(r,e.bind(n));else if(ee(e))if(F(e))e.forEach(o=>lo(o,t,n,s));else{const o=N(e.handler)?e.handler.bind(n):t[e.handler];N(o)&&rn(r,o,e)}}function gs(e){const t=e.type,{mixins:n,extends:s}=t,{mixins:r,optionsCache:o,config:{optionMergeStrategies:i}}=e.appContext,u=o.get(t);let l;return u?l=u:!r.length&&!n&&!s?l=t:(l={},r.length&&r.forEach(a=>an(l,a,i,!0)),an(l,t,i)),ee(t)&&o.set(t,l),l}function an(e,t,n,s=!1){const{mixins:r,extends:o}=t;o&&an(e,o,n,!0),r&&r.forEach(i=>an(e,i,n,!0));for(const i in t)if(!(s&&i==="expose")){const u=Ji[i]||n&&n[i];e[i]=u?u(e[i],t[i]):t[i]}return e}const Ji={data:Ns,props:ks,emits:ks,methods:Lt,computed:Lt,beforeCreate:he,created:he,beforeMount:he,mounted:he,beforeUpdate:he,updated:he,beforeDestroy:he,beforeUnmount:he,destroyed:he,unmounted:he,activated:he,deactivated:he,errorCaptured:he,serverPrefetch:he,components:Lt,directives:Lt,watch:Zi,provide:Ns,inject:Xi};function Ns(e,t){return t?e?function(){return oe(N(e)?e.call(this,this):e,N(t)?t.call(this,this):t)}:t:e}function Xi(e,t){return Lt(Wn(e),Wn(t))}function Wn(e){if(F(e)){const t={};for(let n=0;n1)return n&&N(t)?t.call(s&&s.proxy):t}}function tl(e,t,n,s=!1){const r={},o={};cn(o,Cn,1),e.propsDefaults=Object.create(null),uo(e,t,r,o);for(const i in e.propsOptions[0])i in r||(r[i]=void 0);n?e.props=s?r:Nr(r):e.type.props?e.props=r:e.props=o,e.attrs=o}function nl(e,t,n,s){const{props:r,attrs:o,vnode:{patchFlag:i}}=e,u=V(r),[l]=e.propsOptions;let a=!1;if((s||i>0)&&!(i&16)){if(i&8){const h=e.vnode.dynamicProps;for(let p=0;p{l=!0;const[m,w]=fo(p,t,!0);oe(i,m),w&&u.push(...w)};!n&&t.mixins.length&&t.mixins.forEach(h),e.extends&&h(e.extends),e.mixins&&e.mixins.forEach(h)}if(!o&&!l)return ee(e)&&s.set(e,_t),_t;if(F(o))for(let h=0;h-1,w[1]=z<0||M-1||U(w,"default"))&&u.push(p)}}}const a=[i,u];return ee(e)&&s.set(e,a),a}function Bs(e){return e[0]!=="$"}function Ds(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function Us(e,t){return Ds(e)===Ds(t)}function Ks(e,t){return F(t)?t.findIndex(n=>Us(n,e)):N(t)&&Us(t,e)?0:-1}const ao=e=>e[0]==="_"||e==="$stable",_s=e=>F(e)?e.map($e):[$e(e)],sl=(e,t,n)=>{if(t._n)return t;const s=se((...r)=>_s(t(...r)),n);return s._c=!1,s},ho=(e,t,n)=>{const s=e._ctx;for(const r in e){if(ao(r))continue;const o=e[r];if(N(o))t[r]=sl(r,o,s);else if(o!=null){const i=_s(o);t[r]=()=>i}}},po=(e,t)=>{const n=_s(t);e.slots.default=()=>n},rl=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=V(t),cn(t,"_",n)):ho(t,e.slots={})}else e.slots={},t&&po(e,t);cn(e.slots,Cn,1)},ol=(e,t,n)=>{const{vnode:s,slots:r}=e;let o=!0,i=G;if(s.shapeFlag&32){const u=t._;u?n&&u===1?o=!1:(oe(r,t),!n&&u===1&&delete r._):(o=!t.$stable,ho(t,r)),i=t}else t&&(po(e,t),i={default:1});if(o)for(const u in r)!ao(u)&&i[u]==null&&delete r[u]};function Yn(e,t,n,s,r=!1){if(F(e)){e.forEach((m,w)=>Yn(m,t&&(F(t)?t[w]:t),n,s,r));return}if(Nt(s)&&!r)return;const o=s.shapeFlag&4?bs(s.component)||s.component.proxy:s.el,i=r?null:o,{i:u,r:l}=e,a=t&&t.r,h=u.refs===G?u.refs={}:u.refs,p=u.setupState;if(a!=null&&a!==l&&(re(a)?(h[a]=null,U(p,a)&&(p[a]=null)):fe(a)&&(a.value=null)),N(l))Ze(l,u,12,[i,h]);else{const m=re(l),w=fe(l);if(m||w){const M=()=>{if(e.f){const z=m?U(p,l)?p[l]:h[l]:l.value;r?F(z)&&ns(z,o):F(z)?z.includes(o)||z.push(o):m?(h[l]=[o],U(p,l)&&(p[l]=h[l])):(l.value=[o],e.k&&(h[e.k]=l.value))}else m?(h[l]=i,U(p,l)&&(p[l]=i)):w&&(l.value=i,e.k&&(h[e.k]=i))};i?(M.id=-1,pe(M,n)):M()}}}const pe=Si;function il(e){return ll(e)}function ll(e,t){const n=Ln();n.__VUE__=!0;const{insert:s,remove:r,patchProp:o,createElement:i,createText:u,createComment:l,setText:a,setElementText:h,parentNode:p,nextSibling:m,setScopeId:w=Re,insertStaticContent:M}=e,z=(c,f,d,g=null,v=null,y=null,C=!1,E=null,x=!!f.dynamicChildren)=>{if(c===f)return;c&&!$t(c,f)&&(g=_(c),de(c,v,y,!0),c=null),f.patchFlag===-2&&(x=!1,f.dynamicChildren=null);const{type:b,ref:I,shapeFlag:O}=f;switch(b){case Rn:L(c,f,d,g);break;case xt:T(c,f,d,g);break;case Sn:c==null&&$(f,d,g,C);break;case ve:tt(c,f,d,g,v,y,C,E,x);break;default:O&1?ie(c,f,d,g,v,y,C,E,x):O&6?ze(c,f,d,g,v,y,C,E,x):(O&64||O&128)&&b.process(c,f,d,g,v,y,C,E,x,R)}I!=null&&v&&Yn(I,c&&c.ref,y,f||c,!f)},L=(c,f,d,g)=>{if(c==null)s(f.el=u(f.children),d,g);else{const v=f.el=c.el;f.children!==c.children&&a(v,f.children)}},T=(c,f,d,g)=>{c==null?s(f.el=l(f.children||""),d,g):f.el=c.el},$=(c,f,d,g)=>{[c.el,c.anchor]=M(c.children,f,d,g,c.el,c.anchor)},K=({el:c,anchor:f},d,g)=>{let v;for(;c&&c!==f;)v=m(c),s(c,d,g),c=v;s(f,d,g)},H=({el:c,anchor:f})=>{let d;for(;c&&c!==f;)d=m(c),r(c),c=d;r(f)},ie=(c,f,d,g,v,y,C,E,x)=>{C=C||f.type==="svg",c==null?ae(f,d,g,v,y,C,E,x):ut(c,f,v,y,C,E,x)},ae=(c,f,d,g,v,y,C,E)=>{let x,b;const{type:I,props:O,shapeFlag:S,transition:j,dirs:k}=c;if(x=c.el=i(c.type,y,O&&O.is,O),S&8?h(x,c.children):S&16&&Fe(c.children,x,null,g,v,y&&I!=="foreignObject",C,E),k&&nt(c,null,g,"created"),Me(x,c,c.scopeId,C,g),O){for(const Q in O)Q!=="value"&&!sn(Q)&&o(x,Q,null,O[Q],y,c.children,g,v,le);"value"in O&&o(x,"value",null,O.value),(b=O.onVnodeBeforeMount)&&Te(b,g,c)}k&&nt(c,null,g,"beforeMount");const Z=(!v||v&&!v.pendingBranch)&&j&&!j.persisted;Z&&j.beforeEnter(x),s(x,f,d),((b=O&&O.onVnodeMounted)||Z||k)&&pe(()=>{b&&Te(b,g,c),Z&&j.enter(x),k&&nt(c,null,g,"mounted")},v)},Me=(c,f,d,g,v)=>{if(d&&w(c,d),g)for(let y=0;y{for(let b=x;b{const E=f.el=c.el;let{patchFlag:x,dynamicChildren:b,dirs:I}=f;x|=c.patchFlag&16;const O=c.props||G,S=f.props||G;let j;d&&st(d,!1),(j=S.onVnodeBeforeUpdate)&&Te(j,d,f,c),I&&nt(f,c,d,"beforeUpdate"),d&&st(d,!0);const k=v&&f.type!=="foreignObject";if(b?Ae(c.dynamicChildren,b,E,d,g,k,y):C||W(c,f,E,null,d,g,k,y,!1),x>0){if(x&16)Ve(E,f,O,S,d,g,v);else if(x&2&&O.class!==S.class&&o(E,"class",null,S.class,v),x&4&&o(E,"style",O.style,S.style,v),x&8){const Z=f.dynamicProps;for(let Q=0;Q{j&&Te(j,d,f,c),I&&nt(f,c,d,"updated")},g)},Ae=(c,f,d,g,v,y,C)=>{for(let E=0;E{if(d!==g){if(d!==G)for(const E in d)!sn(E)&&!(E in g)&&o(c,E,d[E],null,C,f.children,v,y,le);for(const E in g){if(sn(E))continue;const x=g[E],b=d[E];x!==b&&E!=="value"&&o(c,E,b,x,C,f.children,v,y,le)}"value"in g&&o(c,"value",d.value,g.value)}},tt=(c,f,d,g,v,y,C,E,x)=>{const b=f.el=c?c.el:u(""),I=f.anchor=c?c.anchor:u("");let{patchFlag:O,dynamicChildren:S,slotScopeIds:j}=f;j&&(E=E?E.concat(j):j),c==null?(s(b,d,g),s(I,d,g),Fe(f.children,d,I,v,y,C,E,x)):O>0&&O&64&&S&&c.dynamicChildren?(Ae(c.dynamicChildren,S,d,v,y,C,E),(f.key!=null||v&&f===v.subTree)&&mo(c,f,!0)):W(c,f,d,I,v,y,C,E,x)},ze=(c,f,d,g,v,y,C,E,x)=>{f.slotScopeIds=E,c==null?f.shapeFlag&512?v.ctx.activate(f,d,g,C,x):St(f,d,g,v,y,C,x):ft(c,f,x)},St=(c,f,d,g,v,y,C)=>{const E=c.component=_l(c,g,v);if(so(c)&&(E.ctx.renderer=R),vl(E),E.asyncDep){if(v&&v.registerDep(E,ne),!c.el){const x=E.subTree=J(xt);T(null,x,f,d)}return}ne(E,c,f,d,v,y,C)},ft=(c,f,d)=>{const g=f.component=c.component;if(Ai(c,f,d))if(g.asyncDep&&!g.asyncResolved){X(g,f,d);return}else g.next=f,xi(g.update),g.update();else f.el=c.el,g.vnode=f},ne=(c,f,d,g,v,y,C)=>{const E=()=>{if(c.isMounted){let{next:I,bu:O,u:S,parent:j,vnode:k}=c,Z=I,Q;st(c,!1),I?(I.el=k.el,X(c,I,C)):I=k,O&&Mn(O),(Q=I.props&&I.props.onVnodeBeforeUpdate)&&Te(Q,j,I,k),st(c,!0);const te=An(c),ye=c.subTree;c.subTree=te,z(ye,te,p(ye.el),_(ye),c,v,y),I.el=te.el,Z===null&&zi(c,te.el),S&&pe(S,v),(Q=I.props&&I.props.onVnodeUpdated)&&pe(()=>Te(Q,j,I,k),v)}else{let I;const{el:O,props:S}=f,{bm:j,m:k,parent:Z}=c,Q=Nt(f);if(st(c,!1),j&&Mn(j),!Q&&(I=S&&S.onVnodeBeforeMount)&&Te(I,Z,f),st(c,!0),O&&q){const te=()=>{c.subTree=An(c),q(O,c.subTree,c,v,null)};Q?f.type.__asyncLoader().then(()=>!c.isUnmounted&&te()):te()}else{const te=c.subTree=An(c);z(null,te,d,g,c,v,y),f.el=te.el}if(k&&pe(k,v),!Q&&(I=S&&S.onVnodeMounted)){const te=f;pe(()=>Te(I,Z,te),v)}(f.shapeFlag&256||Z&&Nt(Z.vnode)&&Z.vnode.shapeFlag&256)&&c.a&&pe(c.a,v),c.isMounted=!0,f=d=g=null}},x=c.effect=new cs(E,()=>ms(b),c.scope),b=c.update=()=>x.run();b.id=c.uid,st(c,!0),b()},X=(c,f,d)=>{f.component=c;const g=c.vnode.props;c.vnode=f,c.next=null,nl(c,f.props,g,d),ol(c,f.children,d),At(),Hs(),zt()},W=(c,f,d,g,v,y,C,E,x=!1)=>{const b=c&&c.children,I=c?c.shapeFlag:0,O=f.children,{patchFlag:S,shapeFlag:j}=f;if(S>0){if(S&128){We(b,O,d,g,v,y,C,E,x);return}else if(S&256){Le(b,O,d,g,v,y,C,E,x);return}}j&8?(I&16&&le(b,v,y),O!==b&&h(d,O)):I&16?j&16?We(b,O,d,g,v,y,C,E,x):le(b,v,y,!0):(I&8&&h(d,""),j&16&&Fe(O,d,g,v,y,C,E,x))},Le=(c,f,d,g,v,y,C,E,x)=>{c=c||_t,f=f||_t;const b=c.length,I=f.length,O=Math.min(b,I);let S;for(S=0;SI?le(c,v,y,!0,!1,O):Fe(f,d,g,v,y,C,E,x,O)},We=(c,f,d,g,v,y,C,E,x)=>{let b=0;const I=f.length;let O=c.length-1,S=I-1;for(;b<=O&&b<=S;){const j=c[b],k=f[b]=x?Qe(f[b]):$e(f[b]);if($t(j,k))z(j,k,d,null,v,y,C,E,x);else break;b++}for(;b<=O&&b<=S;){const j=c[O],k=f[S]=x?Qe(f[S]):$e(f[S]);if($t(j,k))z(j,k,d,null,v,y,C,E,x);else break;O--,S--}if(b>O){if(b<=S){const j=S+1,k=jS)for(;b<=O;)de(c[b],v,y,!0),b++;else{const j=b,k=b,Z=new Map;for(b=k;b<=S;b++){const _e=f[b]=x?Qe(f[b]):$e(f[b]);_e.key!=null&&Z.set(_e.key,b)}let Q,te=0;const ye=S-k+1;let dt=!1,Rs=0;const Tt=new Array(ye);for(b=0;b=ye){de(_e,v,y,!0);continue}let Se;if(_e.key!=null)Se=Z.get(_e.key);else for(Q=k;Q<=S;Q++)if(Tt[Q-k]===0&&$t(_e,f[Q])){Se=Q;break}Se===void 0?de(_e,v,y,!0):(Tt[Se-k]=b+1,Se>=Rs?Rs=Se:dt=!0,z(_e,f[Se],d,null,v,y,C,E,x),te++)}const Cs=dt?cl(Tt):_t;for(Q=Cs.length-1,b=ye-1;b>=0;b--){const _e=k+b,Se=f[_e],Ps=_e+1{const{el:y,type:C,transition:E,children:x,shapeFlag:b}=c;if(b&6){Ie(c.component.subTree,f,d,g);return}if(b&128){c.suspense.move(f,d,g);return}if(b&64){C.move(c,f,d,R);return}if(C===ve){s(y,f,d);for(let O=0;OE.enter(y),v);else{const{leave:O,delayLeave:S,afterLeave:j}=E,k=()=>s(y,f,d),Z=()=>{O(y,()=>{k(),j&&j()})};S?S(y,k,Z):Z()}else s(y,f,d)},de=(c,f,d,g=!1,v=!1)=>{const{type:y,props:C,ref:E,children:x,dynamicChildren:b,shapeFlag:I,patchFlag:O,dirs:S}=c;if(E!=null&&Yn(E,null,d,c,!0),I&256){f.ctx.deactivate(c);return}const j=I&1&&S,k=!Nt(c);let Z;if(k&&(Z=C&&C.onVnodeBeforeUnmount)&&Te(Z,f,c),I&6)Jt(c.component,d,g);else{if(I&128){c.suspense.unmount(d,g);return}j&&nt(c,null,f,"beforeUnmount"),I&64?c.type.remove(c,f,d,v,R,g):b&&(y!==ve||O>0&&O&64)?le(b,f,d,!1,!0):(y===ve&&O&384||!v&&I&16)&&le(x,f,d),g&&at(c)}(k&&(Z=C&&C.onVnodeUnmounted)||j)&&pe(()=>{Z&&Te(Z,f,c),j&&nt(c,null,f,"unmounted")},d)},at=c=>{const{type:f,el:d,anchor:g,transition:v}=c;if(f===ve){ht(d,g);return}if(f===Sn){H(c);return}const y=()=>{r(d),v&&!v.persisted&&v.afterLeave&&v.afterLeave()};if(c.shapeFlag&1&&v&&!v.persisted){const{leave:C,delayLeave:E}=v,x=()=>C(d,y);E?E(c.el,y,x):x()}else y()},ht=(c,f)=>{let d;for(;c!==f;)d=m(c),r(c),c=d;r(f)},Jt=(c,f,d)=>{const{bum:g,scope:v,update:y,subTree:C,um:E}=c;g&&Mn(g),v.stop(),y&&(y.active=!1,de(C,c,f,d)),E&&pe(E,f),pe(()=>{c.isUnmounted=!0},f),f&&f.pendingBranch&&!f.isUnmounted&&c.asyncDep&&!c.asyncResolved&&c.suspenseId===f.pendingId&&(f.deps--,f.deps===0&&f.resolve())},le=(c,f,d,g=!1,v=!1,y=0)=>{for(let C=y;Cc.shapeFlag&6?_(c.component.subTree):c.shapeFlag&128?c.suspense.next():m(c.anchor||c.el),P=(c,f,d)=>{c==null?f._vnode&&de(f._vnode,null,null,!0):z(f._vnode||null,c,f,null,null,null,d),Hs(),Jr(),f._vnode=c},R={p:z,um:de,m:Ie,r:at,mt:St,mc:Fe,pc:W,pbc:Ae,n:_,o:e};let A,q;return t&&([A,q]=t(R)),{render:P,hydrate:A,createApp:el(P,A)}}function st({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function mo(e,t,n=!1){const s=e.children,r=t.children;if(F(s)&&F(r))for(let o=0;o>1,e[n[u]]0&&(t[s]=n[o-1]),n[o]=s)}}for(o=n.length,i=n[o-1];o-- >0;)n[o]=i,i=t[i];return n}const ul=e=>e.__isTeleport,ve=Symbol.for("v-fgt"),Rn=Symbol.for("v-txt"),xt=Symbol.for("v-cmt"),Sn=Symbol.for("v-stc"),Bt=[];let xe=null;function Oe(e=!1){Bt.push(xe=e?null:[])}function fl(){Bt.pop(),xe=Bt[Bt.length-1]||null}let qt=1;function Vs(e){qt+=e}function go(e){return e.dynamicChildren=qt>0?xe||_t:null,fl(),qt>0&&xe&&xe.push(e),e}function je(e,t,n,s,r,o){return go(B(e,t,n,s,r,o,!0))}function al(e,t,n,s,r){return go(J(e,t,n,s,r,!0))}function dn(e){return e?e.__v_isVNode===!0:!1}function $t(e,t){return e.type===t.type&&e.key===t.key}const Cn="__vInternal",_o=({key:e})=>e??null,ln=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?re(e)||fe(e)||N(e)?{i:me,r:e,k:t,f:!!n}:e:null);function B(e,t=null,n=null,s=0,r=null,o=e===ve?0:1,i=!1,u=!1){const l={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&_o(t),ref:t&&ln(t),scopeId:En,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:s,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:me};return u?(vs(l,n),o&128&&e.normalize(l)):n&&(l.shapeFlag|=re(n)?8:16),qt>0&&!i&&xe&&(l.patchFlag>0||o&6)&&l.patchFlag!==32&&xe.push(l),l}const J=hl;function hl(e,t=null,n=null,s=0,r=null,o=!1){if((!e||e===Wi)&&(e=xt),dn(e)){const u=Rt(e,t,!0);return n&&vs(u,n),qt>0&&!o&&xe&&(u.shapeFlag&6?xe[xe.indexOf(e)]=u:xe.push(u)),u.patchFlag|=-2,u}if(El(e)&&(e=e.__vccOpts),t){t=dl(t);let{class:u,style:l}=t;u&&!re(u)&&(t.class=is(u)),ee(l)&&(Br(l)&&!F(l)&&(l=oe({},l)),t.style=os(l))}const i=re(e)?1:Ii(e)?128:ul(e)?64:ee(e)?4:N(e)?2:0;return B(e,t,n,s,r,i,o,!0)}function dl(e){return e?Br(e)||Cn in e?oe({},e):e:null}function Rt(e,t,n=!1){const{props:s,ref:r,patchFlag:o,children:i}=e,u=t?pl(s||{},t):s;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:u,key:u&&_o(u),ref:t&&t.ref?n&&r?F(r)?r.concat(ln(t)):[r,ln(t)]:ln(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==ve?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Rt(e.ssContent),ssFallback:e.ssFallback&&Rt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function D(e=" ",t=0){return J(Rn,null,e,t)}function $e(e){return e==null||typeof e=="boolean"?J(xt):F(e)?J(ve,null,e.slice()):typeof e=="object"?Qe(e):J(Rn,null,String(e))}function Qe(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Rt(e)}function vs(e,t){let n=0;const{shapeFlag:s}=e;if(t==null)t=null;else if(F(t))n=16;else if(typeof t=="object")if(s&65){const r=t.default;r&&(r._c&&(r._d=!1),vs(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!(Cn in t)?t._ctx=me:r===3&&me&&(me.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else N(t)?(t={default:t,_ctx:me},n=32):(t=String(t),s&64?(n=16,t=[D(t)]):n=8);e.children=t,e.shapeFlag|=n}function pl(...e){const t={};for(let n=0;nue=e),ys=e=>{pt.length>1?pt.forEach(t=>t(e)):pt[0](e)};const Ct=e=>{ys(e),e.scope.on()},lt=()=>{ue&&ue.scope.off(),ys(null)};function vo(e){return e.vnode.shapeFlag&4}let Yt=!1;function vl(e,t=!1){Yt=t;const{props:n,children:s}=e.vnode,r=vo(e);tl(e,n,r,t),rl(e,s);const o=r?yl(e,t):void 0;return Yt=!1,o}function yl(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=hs(new Proxy(e.ctx,qi));const{setup:s}=n;if(s){const r=e.setupContext=s.length>1?wl(e):null;Ct(e),At();const o=Ze(s,e,0,[e.props,r]);if(zt(),lt(),Er(o)){if(o.then(lt,lt),t)return o.then(i=>{qs(e,i,t)}).catch(i=>{bn(i,e,0)});e.asyncDep=o}else qs(e,o,t)}else yo(e,t)}function qs(e,t,n){N(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ee(t)&&(e.setupState=Wr(t)),yo(e,n)}let Ys;function yo(e,t,n){const s=e.type;if(!e.render){if(!t&&Ys&&!s.render){const r=s.template||gs(e).template;if(r){const{isCustomElement:o,compilerOptions:i}=e.appContext.config,{delimiters:u,compilerOptions:l}=s,a=oe(oe({isCustomElement:o,delimiters:u},i),l);s.render=Ys(r,a)}}e.render=s.render||Re}{Ct(e),At();try{Yi(e)}finally{zt(),lt()}}}function bl(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return ge(e,"get","$attrs"),t[n]}}))}function wl(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return bl(e)},slots:e.slots,emit:e.emit,expose:t}}function bs(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Wr(hs(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in kt)return kt[n](e)},has(t,n){return n in t||n in kt}}))}function El(e){return N(e)&&"__vccOpts"in e}const Ee=(e,t)=>bi(e,t,Yt);function bo(e,t,n){const s=arguments.length;return s===2?ee(t)&&!F(t)?dn(t)?J(e,null,[t]):J(e,t):J(e,null,t):(s>3?n=Array.prototype.slice.call(arguments,2):s===3&&dn(n)&&(n=[n]),J(e,t,n))}const xl=Symbol.for("v-scx"),Rl=()=>Ue(xl),Cl="3.3.6",Pl="http://www.w3.org/2000/svg",ot=typeof document<"u"?document:null,Qs=ot&&ot.createElement("template"),Ol={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,s)=>{const r=t?ot.createElementNS(Pl,e):ot.createElement(e,n?{is:n}:void 0);return e==="select"&&s&&s.multiple!=null&&r.setAttribute("multiple",s.multiple),r},createText:e=>ot.createTextNode(e),createComment:e=>ot.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>ot.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,s,r,o){const i=n?n.previousSibling:t.lastChild;if(r&&(r===o||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===o||!(r=r.nextSibling)););else{Qs.innerHTML=s?`${e}`:e;const u=Qs.content;if(s){const l=u.firstChild;for(;l.firstChild;)u.appendChild(l.firstChild);u.removeChild(l)}t.insertBefore(u,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},Ml=Symbol("_vtc");function Al(e,t,n){const s=e[Ml];s&&(t=(t?[t,...s]:[...s]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const zl=Symbol("_vod");function Il(e,t,n){const s=e.style,r=re(n);if(n&&!r){if(t&&!re(t))for(const o in t)n[o]==null&&Qn(s,o,"");for(const o in n)Qn(s,o,n[o])}else{const o=s.display;r?t!==n&&(s.cssText=n):t&&e.removeAttribute("style"),zl in e&&(s.display=o)}}const Js=/\s*!important$/;function Qn(e,t,n){if(F(n))n.forEach(s=>Qn(e,t,s));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const s=Sl(e,t);Js.test(n)?e.setProperty(Mt(s),n.replace(Js,""),"important"):e[s]=n}}const Xs=["Webkit","Moz","ms"],Tn={};function Sl(e,t){const n=Tn[t];if(n)return n;let s=wt(t);if(s!=="filter"&&s in e)return Tn[t]=s;s=Cr(s);for(let r=0;r$n||(Nl.then(()=>$n=0),$n=Date.now());function Bl(e,t){const n=s=>{if(!s._vts)s._vts=Date.now();else if(s._vts<=n.attached)return;Ce(Dl(s,n.value),t,5,[s])};return n.value=e,n.attached=kl(),n}function Dl(e,t){if(F(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(s=>r=>!r._stopped&&s&&s(r))}else return t}const tr=/^on[a-z]/,Ul=(e,t,n,s,r=!1,o,i,u,l)=>{t==="class"?Al(e,s,r):t==="style"?Il(e,n,s):mn(t)?ts(t)||Fl(e,t,n,s,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Kl(e,t,s,r))?$l(e,t,s,o,i,u,l):(t==="true-value"?e._trueValue=s:t==="false-value"&&(e._falseValue=s),Tl(e,t,s,r))};function Kl(e,t,n,s){return s?!!(t==="innerHTML"||t==="textContent"||t in e&&tr.test(t)&&N(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||tr.test(t)&&re(n)?!1:t in e}const Vl=oe({patchProp:Ul},Ol);let nr;function Wl(){return nr||(nr=il(Vl))}const ql=(...e)=>{const t=Wl().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=Yl(s);if(!r)return;const o=t._component;!N(o)&&!o.render&&!o.template&&(o.template=r.innerHTML),r.innerHTML="";const i=n(r,!1,r instanceof SVGElement);return r instanceof Element&&(r.removeAttribute("v-cloak"),r.setAttribute("data-v-app","")),i},t};function Yl(e){return re(e)?document.querySelector(e):e}var Ql=!1;/*! - * pinia v2.1.7 - * (c) 2023 Eduardo San Martin Morote - * @license MIT - */const Jl=Symbol();var sr;(function(e){e.direct="direct",e.patchObject="patch object",e.patchFunction="patch function"})(sr||(sr={}));function Xl(){const e=qo(!0),t=e.run(()=>Kr({}));let n=[],s=[];const r=hs({install(o){r._a=o,o.provide(Jl,r),o.config.globalProperties.$pinia=r,s.forEach(i=>n.push(i)),s=[]},use(o){return!this._a&&!Ql?s.push(o):n.push(o),this},_p:n,_a:null,_e:e,_s:new Map,state:t});return r}const Zl="/assets/logo-277e0e97.svg";/*! - * vue-router v4.2.5 - * (c) 2023 Eduardo San Martin Morote - * @license MIT - */const mt=typeof window<"u";function Gl(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const Y=Object.assign;function Hn(e,t){const n={};for(const s in t){const r=t[s];n[s]=Pe(r)?r.map(e):e(r)}return n}const Dt=()=>{},Pe=Array.isArray,ec=/\/$/,tc=e=>e.replace(ec,"");function jn(e,t,n="/"){let s,r={},o="",i="";const u=t.indexOf("#");let l=t.indexOf("?");return u=0&&(l=-1),l>-1&&(s=t.slice(0,l),o=t.slice(l+1,u>-1?u:t.length),r=e(o)),u>-1&&(s=s||t.slice(0,u),i=t.slice(u,t.length)),s=oc(s??t,n),{fullPath:s+(o&&"?")+o+i,path:s,query:r,hash:i}}function nc(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function rr(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function sc(e,t,n){const s=t.matched.length-1,r=n.matched.length-1;return s>-1&&s===r&&Pt(t.matched[s],n.matched[r])&&wo(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Pt(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function wo(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!rc(e[n],t[n]))return!1;return!0}function rc(e,t){return Pe(e)?or(e,t):Pe(t)?or(t,e):e===t}function or(e,t){return Pe(t)?e.length===t.length&&e.every((n,s)=>n===t[s]):e.length===1&&e[0]===t}function oc(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),s=e.split("/"),r=s[s.length-1];(r===".."||r===".")&&s.push("");let o=n.length-1,i,u;for(i=0;i1&&o--;else break;return n.slice(0,o).join("/")+"/"+s.slice(i-(i===s.length?1:0)).join("/")}var Qt;(function(e){e.pop="pop",e.push="push"})(Qt||(Qt={}));var Ut;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Ut||(Ut={}));function ic(e){if(!e)if(mt){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),tc(e)}const lc=/^[^#]+#/;function cc(e,t){return e.replace(lc,"#")+t}function uc(e,t){const n=document.documentElement.getBoundingClientRect(),s=e.getBoundingClientRect();return{behavior:t.behavior,left:s.left-n.left-(t.left||0),top:s.top-n.top-(t.top||0)}}const Pn=()=>({left:window.pageXOffset,top:window.pageYOffset});function fc(e){let t;if("el"in e){const n=e.el,s=typeof n=="string"&&n.startsWith("#"),r=typeof n=="string"?s?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!r)return;t=uc(r,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function ir(e,t){return(history.state?history.state.position-t:-1)+e}const Jn=new Map;function ac(e,t){Jn.set(e,t)}function hc(e){const t=Jn.get(e);return Jn.delete(e),t}let dc=()=>location.protocol+"//"+location.host;function Eo(e,t){const{pathname:n,search:s,hash:r}=t,o=e.indexOf("#");if(o>-1){let u=r.includes(e.slice(o))?e.slice(o).length:1,l=r.slice(u);return l[0]!=="/"&&(l="/"+l),rr(l,"")}return rr(n,e)+s+r}function pc(e,t,n,s){let r=[],o=[],i=null;const u=({state:m})=>{const w=Eo(e,location),M=n.value,z=t.value;let L=0;if(m){if(n.value=w,t.value=m,i&&i===M){i=null;return}L=z?m.position-z.position:0}else s(w);r.forEach(T=>{T(n.value,M,{delta:L,type:Qt.pop,direction:L?L>0?Ut.forward:Ut.back:Ut.unknown})})};function l(){i=n.value}function a(m){r.push(m);const w=()=>{const M=r.indexOf(m);M>-1&&r.splice(M,1)};return o.push(w),w}function h(){const{history:m}=window;m.state&&m.replaceState(Y({},m.state,{scroll:Pn()}),"")}function p(){for(const m of o)m();o=[],window.removeEventListener("popstate",u),window.removeEventListener("beforeunload",h)}return window.addEventListener("popstate",u),window.addEventListener("beforeunload",h,{passive:!0}),{pauseListeners:l,listen:a,destroy:p}}function lr(e,t,n,s=!1,r=!1){return{back:e,current:t,forward:n,replaced:s,position:window.history.length,scroll:r?Pn():null}}function mc(e){const{history:t,location:n}=window,s={value:Eo(e,n)},r={value:t.state};r.value||o(s.value,{back:null,current:s.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function o(l,a,h){const p=e.indexOf("#"),m=p>-1?(n.host&&document.querySelector("base")?e:e.slice(p))+l:dc()+e+l;try{t[h?"replaceState":"pushState"](a,"",m),r.value=a}catch(w){console.error(w),n[h?"replace":"assign"](m)}}function i(l,a){const h=Y({},t.state,lr(r.value.back,l,r.value.forward,!0),a,{position:r.value.position});o(l,h,!0),s.value=l}function u(l,a){const h=Y({},r.value,t.state,{forward:l,scroll:Pn()});o(h.current,h,!0);const p=Y({},lr(s.value,l,null),{position:h.position+1},a);o(l,p,!1),s.value=l}return{location:s,state:r,push:u,replace:i}}function gc(e){e=ic(e);const t=mc(e),n=pc(e,t.state,t.location,t.replace);function s(o,i=!0){i||n.pauseListeners(),history.go(o)}const r=Y({location:"",base:e,go:s,createHref:cc.bind(null,e)},t,n);return Object.defineProperty(r,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(r,"state",{enumerable:!0,get:()=>t.state.value}),r}function _c(e){return typeof e=="string"||e&&typeof e=="object"}function xo(e){return typeof e=="string"||typeof e=="symbol"}const Ye={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Ro=Symbol("");var cr;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(cr||(cr={}));function Ot(e,t){return Y(new Error,{type:e,[Ro]:!0},t)}function Ne(e,t){return e instanceof Error&&Ro in e&&(t==null||!!(e.type&t))}const ur="[^/]+?",vc={sensitive:!1,strict:!1,start:!0,end:!0},yc=/[.+*?^${}()[\]/\\]/g;function bc(e,t){const n=Y({},vc,t),s=[];let r=n.start?"^":"";const o=[];for(const a of e){const h=a.length?[]:[90];n.strict&&!a.length&&(r+="/");for(let p=0;pt.length?t.length===1&&t[0]===40+40?1:-1:0}function Ec(e,t){let n=0;const s=e.score,r=t.score;for(;n0&&t[t.length-1]<0}const xc={type:0,value:""},Rc=/[a-zA-Z0-9_]/;function Cc(e){if(!e)return[[]];if(e==="/")return[[xc]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(w){throw new Error(`ERR (${n})/"${a}": ${w}`)}let n=0,s=n;const r=[];let o;function i(){o&&r.push(o),o=[]}let u=0,l,a="",h="";function p(){a&&(n===0?o.push({type:0,value:a}):n===1||n===2||n===3?(o.length>1&&(l==="*"||l==="+")&&t(`A repeatable param (${a}) must be alone in its segment. eg: '/:ids+.`),o.push({type:1,value:a,regexp:h,repeatable:l==="*"||l==="+",optional:l==="*"||l==="?"})):t("Invalid state to consume buffer"),a="")}function m(){a+=l}for(;u{i($)}:Dt}function i(h){if(xo(h)){const p=s.get(h);p&&(s.delete(h),n.splice(n.indexOf(p),1),p.children.forEach(i),p.alias.forEach(i))}else{const p=n.indexOf(h);p>-1&&(n.splice(p,1),h.record.name&&s.delete(h.record.name),h.children.forEach(i),h.alias.forEach(i))}}function u(){return n}function l(h){let p=0;for(;p=0&&(h.record.path!==n[p].record.path||!Co(h,n[p]));)p++;n.splice(p,0,h),h.record.name&&!hr(h)&&s.set(h.record.name,h)}function a(h,p){let m,w={},M,z;if("name"in h&&h.name){if(m=s.get(h.name),!m)throw Ot(1,{location:h});z=m.record.name,w=Y(ar(p.params,m.keys.filter($=>!$.optional).map($=>$.name)),h.params&&ar(h.params,m.keys.map($=>$.name))),M=m.stringify(w)}else if("path"in h)M=h.path,m=n.find($=>$.re.test(M)),m&&(w=m.parse(M),z=m.record.name);else{if(m=p.name?s.get(p.name):n.find($=>$.re.test(p.path)),!m)throw Ot(1,{location:h,currentLocation:p});z=m.record.name,w=Y({},p.params,h.params),M=m.stringify(w)}const L=[];let T=m;for(;T;)L.unshift(T.record),T=T.parent;return{name:z,path:M,params:w,matched:L,meta:zc(L)}}return e.forEach(h=>o(h)),{addRoute:o,resolve:a,removeRoute:i,getRoutes:u,getRecordMatcher:r}}function ar(e,t){const n={};for(const s of t)s in e&&(n[s]=e[s]);return n}function Mc(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:Ac(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function Ac(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const s in e.components)t[s]=typeof n=="object"?n[s]:n;return t}function hr(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function zc(e){return e.reduce((t,n)=>Y(t,n.meta),{})}function dr(e,t){const n={};for(const s in e)n[s]=s in t?t[s]:e[s];return n}function Co(e,t){return t.children.some(n=>n===e||Co(e,n))}const Po=/#/g,Ic=/&/g,Sc=/\//g,Tc=/=/g,$c=/\?/g,Oo=/\+/g,Hc=/%5B/g,jc=/%5D/g,Mo=/%5E/g,Fc=/%60/g,Ao=/%7B/g,Lc=/%7C/g,zo=/%7D/g,Nc=/%20/g;function ws(e){return encodeURI(""+e).replace(Lc,"|").replace(Hc,"[").replace(jc,"]")}function kc(e){return ws(e).replace(Ao,"{").replace(zo,"}").replace(Mo,"^")}function Xn(e){return ws(e).replace(Oo,"%2B").replace(Nc,"+").replace(Po,"%23").replace(Ic,"%26").replace(Fc,"`").replace(Ao,"{").replace(zo,"}").replace(Mo,"^")}function Bc(e){return Xn(e).replace(Tc,"%3D")}function Dc(e){return ws(e).replace(Po,"%23").replace($c,"%3F")}function Uc(e){return e==null?"":Dc(e).replace(Sc,"%2F")}function pn(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function Kc(e){const t={};if(e===""||e==="?")return t;const s=(e[0]==="?"?e.slice(1):e).split("&");for(let r=0;ro&&Xn(o)):[s&&Xn(s)]).forEach(o=>{o!==void 0&&(t+=(t.length?"&":"")+n,o!=null&&(t+="="+o))})}return t}function Vc(e){const t={};for(const n in e){const s=e[n];s!==void 0&&(t[n]=Pe(s)?s.map(r=>r==null?null:""+r):s==null?s:""+s)}return t}const Wc=Symbol(""),mr=Symbol(""),Es=Symbol(""),Io=Symbol(""),Zn=Symbol("");function Ht(){let e=[];function t(s){return e.push(s),()=>{const r=e.indexOf(s);r>-1&&e.splice(r,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Je(e,t,n,s,r){const o=s&&(s.enterCallbacks[r]=s.enterCallbacks[r]||[]);return()=>new Promise((i,u)=>{const l=p=>{p===!1?u(Ot(4,{from:n,to:t})):p instanceof Error?u(p):_c(p)?u(Ot(2,{from:t,to:p})):(o&&s.enterCallbacks[r]===o&&typeof p=="function"&&o.push(p),i())},a=e.call(s&&s.instances[r],t,n,l);let h=Promise.resolve(a);e.length<3&&(h=h.then(l)),h.catch(p=>u(p))})}function Fn(e,t,n,s){const r=[];for(const o of e)for(const i in o.components){let u=o.components[i];if(!(t!=="beforeRouteEnter"&&!o.instances[i]))if(qc(u)){const a=(u.__vccOpts||u)[t];a&&r.push(Je(a,n,s,o,i))}else{let l=u();r.push(()=>l.then(a=>{if(!a)return Promise.reject(new Error(`Couldn't resolve component "${i}" at "${o.path}"`));const h=Gl(a)?a.default:a;o.components[i]=h;const m=(h.__vccOpts||h)[t];return m&&Je(m,n,s,o,i)()}))}}return r}function qc(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function gr(e){const t=Ue(Es),n=Ue(Io),s=Ee(()=>t.resolve(De(e.to))),r=Ee(()=>{const{matched:l}=s.value,{length:a}=l,h=l[a-1],p=n.matched;if(!h||!p.length)return-1;const m=p.findIndex(Pt.bind(null,h));if(m>-1)return m;const w=_r(l[a-2]);return a>1&&_r(h)===w&&p[p.length-1].path!==w?p.findIndex(Pt.bind(null,l[a-2])):m}),o=Ee(()=>r.value>-1&&Jc(n.params,s.value.params)),i=Ee(()=>r.value>-1&&r.value===n.matched.length-1&&wo(n.params,s.value.params));function u(l={}){return Qc(l)?t[De(e.replace)?"replace":"push"](De(e.to)).catch(Dt):Promise.resolve()}return{route:s,href:Ee(()=>s.value.href),isActive:o,isExactActive:i,navigate:u}}const Yc=It({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:gr,setup(e,{slots:t}){const n=yn(gr(e)),{options:s}=Ue(Es),r=Ee(()=>({[vr(e.activeClass,s.linkActiveClass,"router-link-active")]:n.isActive,[vr(e.exactActiveClass,s.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const o=t.default&&t.default(n);return e.custom?o:bo("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:r.value},o)}}}),Gn=Yc;function Qc(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Jc(e,t){for(const n in t){const s=t[n],r=e[n];if(typeof s=="string"){if(s!==r)return!1}else if(!Pe(r)||r.length!==s.length||s.some((o,i)=>o!==r[i]))return!1}return!0}function _r(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const vr=(e,t,n)=>e??t??n,Xc=It({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const s=Ue(Zn),r=Ee(()=>e.route||s.value),o=Ue(mr,0),i=Ee(()=>{let a=De(o);const{matched:h}=r.value;let p;for(;(p=h[a])&&!p.components;)a++;return a}),u=Ee(()=>r.value.matched[i.value]);on(mr,Ee(()=>i.value+1)),on(Wc,u),on(Zn,r);const l=Kr();return rn(()=>[l.value,u.value,e.name],([a,h,p],[m,w,M])=>{h&&(h.instances[p]=a,w&&w!==h&&a&&a===m&&(h.leaveGuards.size||(h.leaveGuards=w.leaveGuards),h.updateGuards.size||(h.updateGuards=w.updateGuards))),a&&h&&(!w||!Pt(h,w)||!m)&&(h.enterCallbacks[p]||[]).forEach(z=>z(a))},{flush:"post"}),()=>{const a=r.value,h=e.name,p=u.value,m=p&&p.components[h];if(!m)return yr(n.default,{Component:m,route:a});const w=p.props[h],M=w?w===!0?a.params:typeof w=="function"?w(a):w:null,L=bo(m,Y({},M,t,{onVnodeUnmounted:T=>{T.component.isUnmounted&&(p.instances[h]=null)},ref:l}));return yr(n.default,{Component:L,route:a})||L}}});function yr(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const So=Xc;function Zc(e){const t=Oc(e.routes,e),n=e.parseQuery||Kc,s=e.stringifyQuery||pr,r=e.history,o=Ht(),i=Ht(),u=Ht(),l=gi(Ye);let a=Ye;mt&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const h=Hn.bind(null,_=>""+_),p=Hn.bind(null,Uc),m=Hn.bind(null,pn);function w(_,P){let R,A;return xo(_)?(R=t.getRecordMatcher(_),A=P):A=_,t.addRoute(A,R)}function M(_){const P=t.getRecordMatcher(_);P&&t.removeRoute(P)}function z(){return t.getRoutes().map(_=>_.record)}function L(_){return!!t.getRecordMatcher(_)}function T(_,P){if(P=Y({},P||l.value),typeof _=="string"){const d=jn(n,_,P.path),g=t.resolve({path:d.path},P),v=r.createHref(d.fullPath);return Y(d,g,{params:m(g.params),hash:pn(d.hash),redirectedFrom:void 0,href:v})}let R;if("path"in _)R=Y({},_,{path:jn(n,_.path,P.path).path});else{const d=Y({},_.params);for(const g in d)d[g]==null&&delete d[g];R=Y({},_,{params:p(d)}),P.params=p(P.params)}const A=t.resolve(R,P),q=_.hash||"";A.params=h(m(A.params));const c=nc(s,Y({},_,{hash:kc(q),path:A.path})),f=r.createHref(c);return Y({fullPath:c,hash:q,query:s===pr?Vc(_.query):_.query||{}},A,{redirectedFrom:void 0,href:f})}function $(_){return typeof _=="string"?jn(n,_,l.value.path):Y({},_)}function K(_,P){if(a!==_)return Ot(8,{from:P,to:_})}function H(_){return Me(_)}function ie(_){return H(Y($(_),{replace:!0}))}function ae(_){const P=_.matched[_.matched.length-1];if(P&&P.redirect){const{redirect:R}=P;let A=typeof R=="function"?R(_):R;return typeof A=="string"&&(A=A.includes("?")||A.includes("#")?A=$(A):{path:A},A.params={}),Y({query:_.query,hash:_.hash,params:"path"in A?{}:_.params},A)}}function Me(_,P){const R=a=T(_),A=l.value,q=_.state,c=_.force,f=_.replace===!0,d=ae(R);if(d)return Me(Y($(d),{state:typeof d=="object"?Y({},q,d.state):q,force:c,replace:f}),P||R);const g=R;g.redirectedFrom=P;let v;return!c&&sc(s,A,R)&&(v=Ot(16,{to:g,from:A}),Ie(A,A,!0,!1)),(v?Promise.resolve(v):Ae(g,A)).catch(y=>Ne(y)?Ne(y,2)?y:We(y):W(y,g,A)).then(y=>{if(y){if(Ne(y,2))return Me(Y({replace:f},$(y.to),{state:typeof y.to=="object"?Y({},q,y.to.state):q,force:c}),P||g)}else y=tt(g,A,!0,f,q);return Ve(g,A,y),y})}function Fe(_,P){const R=K(_,P);return R?Promise.reject(R):Promise.resolve()}function ut(_){const P=ht.values().next().value;return P&&typeof P.runWithContext=="function"?P.runWithContext(_):_()}function Ae(_,P){let R;const[A,q,c]=Gc(_,P);R=Fn(A.reverse(),"beforeRouteLeave",_,P);for(const d of A)d.leaveGuards.forEach(g=>{R.push(Je(g,_,P))});const f=Fe.bind(null,_,P);return R.push(f),le(R).then(()=>{R=[];for(const d of o.list())R.push(Je(d,_,P));return R.push(f),le(R)}).then(()=>{R=Fn(q,"beforeRouteUpdate",_,P);for(const d of q)d.updateGuards.forEach(g=>{R.push(Je(g,_,P))});return R.push(f),le(R)}).then(()=>{R=[];for(const d of c)if(d.beforeEnter)if(Pe(d.beforeEnter))for(const g of d.beforeEnter)R.push(Je(g,_,P));else R.push(Je(d.beforeEnter,_,P));return R.push(f),le(R)}).then(()=>(_.matched.forEach(d=>d.enterCallbacks={}),R=Fn(c,"beforeRouteEnter",_,P),R.push(f),le(R))).then(()=>{R=[];for(const d of i.list())R.push(Je(d,_,P));return R.push(f),le(R)}).catch(d=>Ne(d,8)?d:Promise.reject(d))}function Ve(_,P,R){u.list().forEach(A=>ut(()=>A(_,P,R)))}function tt(_,P,R,A,q){const c=K(_,P);if(c)return c;const f=P===Ye,d=mt?history.state:{};R&&(A||f?r.replace(_.fullPath,Y({scroll:f&&d&&d.scroll},q)):r.push(_.fullPath,q)),l.value=_,Ie(_,P,R,f),We()}let ze;function St(){ze||(ze=r.listen((_,P,R)=>{if(!Jt.listening)return;const A=T(_),q=ae(A);if(q){Me(Y(q,{replace:!0}),A).catch(Dt);return}a=A;const c=l.value;mt&&ac(ir(c.fullPath,R.delta),Pn()),Ae(A,c).catch(f=>Ne(f,12)?f:Ne(f,2)?(Me(f.to,A).then(d=>{Ne(d,20)&&!R.delta&&R.type===Qt.pop&&r.go(-1,!1)}).catch(Dt),Promise.reject()):(R.delta&&r.go(-R.delta,!1),W(f,A,c))).then(f=>{f=f||tt(A,c,!1),f&&(R.delta&&!Ne(f,8)?r.go(-R.delta,!1):R.type===Qt.pop&&Ne(f,20)&&r.go(-1,!1)),Ve(A,c,f)}).catch(Dt)}))}let ft=Ht(),ne=Ht(),X;function W(_,P,R){We(_);const A=ne.list();return A.length?A.forEach(q=>q(_,P,R)):console.error(_),Promise.reject(_)}function Le(){return X&&l.value!==Ye?Promise.resolve():new Promise((_,P)=>{ft.add([_,P])})}function We(_){return X||(X=!_,St(),ft.list().forEach(([P,R])=>_?R(_):P()),ft.reset()),_}function Ie(_,P,R,A){const{scrollBehavior:q}=e;if(!mt||!q)return Promise.resolve();const c=!R&&hc(ir(_.fullPath,0))||(A||!R)&&history.state&&history.state.scroll||null;return Yr().then(()=>q(_,P,c)).then(f=>f&&fc(f)).catch(f=>W(f,_,P))}const de=_=>r.go(_);let at;const ht=new Set,Jt={currentRoute:l,listening:!0,addRoute:w,removeRoute:M,hasRoute:L,getRoutes:z,resolve:T,options:e,push:H,replace:ie,go:de,back:()=>de(-1),forward:()=>de(1),beforeEach:o.add,beforeResolve:i.add,afterEach:u.add,onError:ne.add,isReady:Le,install(_){const P=this;_.component("RouterLink",Gn),_.component("RouterView",So),_.config.globalProperties.$router=P,Object.defineProperty(_.config.globalProperties,"$route",{enumerable:!0,get:()=>De(l)}),mt&&!at&&l.value===Ye&&(at=!0,H(r.location).catch(q=>{}));const R={};for(const q in Ye)Object.defineProperty(R,q,{get:()=>l.value[q],enumerable:!0});_.provide(Es,P),_.provide(Io,Nr(R)),_.provide(Zn,l);const A=_.unmount;ht.add(_),_.unmount=function(){ht.delete(_),ht.size<1&&(a=Ye,ze&&ze(),ze=null,l.value=Ye,at=!1,X=!1),A()}}};function le(_){return _.reduce((P,R)=>P.then(()=>ut(R)),Promise.resolve())}return Jt}function Gc(e,t){const n=[],s=[],r=[],o=Math.max(t.matched.length,e.matched.length);for(let i=0;iPt(a,u))?s.push(u):n.push(u));const l=e.matched[i];l&&(t.matched.find(a=>Pt(a,l))||r.push(l))}return[n,s,r]}const eu=e=>(Gr("data-v-a47c673d"),e=e(),eo(),e),tu={class:"greetings"},nu={class:"green"},su=eu(()=>B("h3",null,[D(" You’ve successfully created a project with "),B("a",{href:"https://vitejs.dev/",target:"_blank",rel:"noopener"},"Vite"),D(" + "),B("a",{href:"https://vuejs.org/",target:"_blank",rel:"noopener"},"Vue 3"),D(". What's next? ")],-1)),ru=It({__name:"HelloWorld",props:{msg:{}},setup(e){return(t,n)=>(Oe(),je("div",tu,[B("h1",nu,Wo(t.msg),1),su]))}});const et=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},ou=et(ru,[["__scopeId","data-v-a47c673d"]]),iu=e=>(Gr("data-v-85852c48"),e=e(),eo(),e),lu=iu(()=>B("img",{alt:"Vue logo",class:"logo",src:Zl,width:"125",height:"125"},null,-1)),cu={class:"wrapper"},uu=It({__name:"App",setup(e){return(t,n)=>(Oe(),je(ve,null,[B("header",null,[lu,B("div",cu,[J(ou,{msg:"You did it!"}),B("nav",null,[J(De(Gn),{to:"/"},{default:se(()=>[D("Home")]),_:1}),J(De(Gn),{to:"/about"},{default:se(()=>[D("About")]),_:1})])])]),J(De(So))],64))}});const fu=et(uu,[["__scopeId","data-v-85852c48"]]),au="modulepreload",hu=function(e){return"/"+e},br={},du=function(t,n,s){if(!n||n.length===0)return t();const r=document.getElementsByTagName("link");return Promise.all(n.map(o=>{if(o=hu(o),o in br)return;br[o]=!0;const i=o.endsWith(".css"),u=i?'[rel="stylesheet"]':"";if(!!s)for(let h=r.length-1;h>=0;h--){const p=r[h];if(p.href===o&&(!i||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${o}"]${u}`))return;const a=document.createElement("link");if(a.rel=i?"stylesheet":au,i||(a.as="script",a.crossOrigin=""),a.href=o,document.head.appendChild(a),i)return new Promise((h,p)=>{a.addEventListener("load",h),a.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${o}`)))})})).then(()=>t()).catch(o=>{const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=o,window.dispatchEvent(i),!i.defaultPrevented)throw o})};const pu={},mu={class:"item"},gu={class:"details"};function _u(e,t){return Oe(),je("div",mu,[B("i",null,[zn(e.$slots,"icon",{},void 0,!0)]),B("div",gu,[B("h3",null,[zn(e.$slots,"heading",{},void 0,!0)]),zn(e.$slots,"default",{},void 0,!0)])])}const jt=et(pu,[["render",_u],["__scopeId","data-v-fd0742eb"]]),vu={},yu={xmlns:"http://www.w3.org/2000/svg",width:"20",height:"17",fill:"currentColor"},bu=B("path",{d:"M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"},null,-1),wu=[bu];function Eu(e,t){return Oe(),je("svg",yu,wu)}const xu=et(vu,[["render",Eu]]),Ru={},Cu={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink","aria-hidden":"true",role:"img",class:"iconify iconify--mdi",width:"24",height:"24",preserveAspectRatio:"xMidYMid meet",viewBox:"0 0 24 24"},Pu=B("path",{d:"M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z",fill:"currentColor"},null,-1),Ou=[Pu];function Mu(e,t){return Oe(),je("svg",Cu,Ou)}const Au=et(Ru,[["render",Mu]]),zu={},Iu={xmlns:"http://www.w3.org/2000/svg",width:"18",height:"20",fill:"currentColor"},Su=B("path",{d:"M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"},null,-1),Tu=[Su];function $u(e,t){return Oe(),je("svg",Iu,Tu)}const Hu=et(zu,[["render",$u]]),ju={},Fu={xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",fill:"currentColor"},Lu=B("path",{d:"M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"},null,-1),Nu=[Lu];function ku(e,t){return Oe(),je("svg",Fu,Nu)}const Bu=et(ju,[["render",ku]]),Du={},Uu={xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",fill:"currentColor"},Ku=B("path",{d:"M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"},null,-1),Vu=[Ku];function Wu(e,t){return Oe(),je("svg",Uu,Vu)}const qu=et(Du,[["render",Wu]]),Yu=B("a",{href:"https://vuejs.org/",target:"_blank",rel:"noopener"},"official documentation",-1),Qu=B("a",{href:"https://vitejs.dev/guide/features.html",target:"_blank",rel:"noopener"},"Vite",-1),Ju=B("a",{href:"https://code.visualstudio.com/",target:"_blank",rel:"noopener"},"VSCode",-1),Xu=B("a",{href:"https://github.com/johnsoncodehk/volar",target:"_blank",rel:"noopener"},"Volar",-1),Zu=B("a",{href:"https://www.cypress.io/",target:"_blank",rel:"noopener"},"Cypress",-1),Gu=B("a",{href:"https://on.cypress.io/component",target:"_blank",rel:"noopener"},"Cypress Component Testing",-1),ef=B("br",null,null,-1),tf=B("code",null,"README.md",-1),nf=B("a",{href:"https://pinia.vuejs.org/",target:"_blank",rel:"noopener"},"Pinia",-1),sf=B("a",{href:"https://router.vuejs.org/",target:"_blank",rel:"noopener"},"Vue Router",-1),rf=B("a",{href:"https://test-utils.vuejs.org/",target:"_blank",rel:"noopener"},"Vue Test Utils",-1),of=B("a",{href:"https://github.com/vuejs/devtools",target:"_blank",rel:"noopener"},"Vue Dev Tools",-1),lf=B("a",{href:"https://github.com/vuejs/awesome-vue",target:"_blank",rel:"noopener"},"Awesome Vue",-1),cf=B("a",{href:"https://chat.vuejs.org",target:"_blank",rel:"noopener"},"Vue Land",-1),uf=B("a",{href:"https://stackoverflow.com/questions/tagged/vue.js",target:"_blank",rel:"noopener"},"StackOverflow",-1),ff=B("a",{href:"https://news.vuejs.org",target:"_blank",rel:"noopener"},"our mailing list",-1),af=B("a",{href:"https://twitter.com/vuejs",target:"_blank",rel:"noopener"},"@vuejs",-1),hf=B("a",{href:"https://vuejs.org/sponsor/",target:"_blank",rel:"noopener"},"becoming a sponsor",-1),df=It({__name:"TheWelcome",setup(e){return(t,n)=>(Oe(),je(ve,null,[J(jt,null,{icon:se(()=>[J(xu)]),heading:se(()=>[D("Documentation")]),default:se(()=>[D(" Vue’s "),Yu,D(" provides you with all information you need to get started. ")]),_:1}),J(jt,null,{icon:se(()=>[J(Au)]),heading:se(()=>[D("Tooling")]),default:se(()=>[D(" This project is served and bundled with "),Qu,D(". The recommended IDE setup is "),Ju,D(" + "),Xu,D(". If you need to test your components and web pages, check out "),Zu,D(" and "),Gu,D(". "),ef,D(" More instructions are available in "),tf,D(". ")]),_:1}),J(jt,null,{icon:se(()=>[J(Hu)]),heading:se(()=>[D("Ecosystem")]),default:se(()=>[D(" Get official tools and libraries for your project: "),nf,D(", "),sf,D(", "),rf,D(", and "),of,D(". If you need more resources, we suggest paying "),lf,D(" a visit. ")]),_:1}),J(jt,null,{icon:se(()=>[J(Bu)]),heading:se(()=>[D("Community")]),default:se(()=>[D(" Got stuck? Ask your question on "),cf,D(", our official Discord server, or "),uf,D(". You should also subscribe to "),ff,D(" and follow the official "),af,D(" twitter account for latest news in the Vue world. ")]),_:1}),J(jt,null,{icon:se(()=>[J(qu)]),heading:se(()=>[D("Support Vue")]),default:se(()=>[D(" As an independent project, Vue relies on community backing for its sustainability. You can help us by "),hf,D(". ")]),_:1})],64))}}),pf=It({__name:"HomeView",setup(e){return(t,n)=>(Oe(),je("main",null,[J(df)]))}}),mf=Zc({history:gc("/"),routes:[{path:"/",name:"home",component:pf},{path:"/about",name:"about",component:()=>du(()=>import("./AboutView-ba1efa64.js"),["assets/AboutView-ba1efa64.js","assets/AboutView-4d995ba2.css"])}]}),xs=ql(fu);xs.use(Xl());xs.use(mf);xs.mount("#app");export{et as _,B as a,je as c,Oe as o}; diff --git a/cista/wwwroot/assets/index-9f680dd7.css b/cista/wwwroot/assets/index-9f680dd7.css deleted file mode 100644 index fa03bf9..0000000 --- a/cista/wwwroot/assets/index-9f680dd7.css +++ /dev/null @@ -1 +0,0 @@ -:root{--vt-c-white: #ffffff;--vt-c-white-soft: #f8f8f8;--vt-c-white-mute: #f2f2f2;--vt-c-black: #181818;--vt-c-black-soft: #222222;--vt-c-black-mute: #282828;--vt-c-indigo: #2c3e50;--vt-c-divider-light-1: rgba(60, 60, 60, .29);--vt-c-divider-light-2: rgba(60, 60, 60, .12);--vt-c-divider-dark-1: rgba(84, 84, 84, .65);--vt-c-divider-dark-2: rgba(84, 84, 84, .48);--vt-c-text-light-1: var(--vt-c-indigo);--vt-c-text-light-2: rgba(60, 60, 60, .66);--vt-c-text-dark-1: var(--vt-c-white);--vt-c-text-dark-2: rgba(235, 235, 235, .64)}:root{--color-background: var(--vt-c-white);--color-background-soft: var(--vt-c-white-soft);--color-background-mute: var(--vt-c-white-mute);--color-border: var(--vt-c-divider-light-2);--color-border-hover: var(--vt-c-divider-light-1);--color-heading: var(--vt-c-text-light-1);--color-text: var(--vt-c-text-light-1);--section-gap: 160px}@media (prefers-color-scheme: dark){:root{--color-background: var(--vt-c-black);--color-background-soft: var(--vt-c-black-soft);--color-background-mute: var(--vt-c-black-mute);--color-border: var(--vt-c-divider-dark-2);--color-border-hover: var(--vt-c-divider-dark-1);--color-heading: var(--vt-c-text-dark-1);--color-text: var(--vt-c-text-dark-2)}}*,*:before,*:after{box-sizing:border-box;margin:0;font-weight:400}body{min-height:100vh;color:var(--color-text);background:var(--color-background);transition:color .5s,background-color .5s;line-height:1.6;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:15px;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#app{max-width:1280px;margin:0 auto;padding:2rem;font-weight:400}a,.green{text-decoration:none;color:#00bd7e;transition:.4s}@media (hover: hover){a:hover{background-color:#00bd7e33}}@media (min-width: 1024px){body{display:flex;place-items:center}#app{display:grid;grid-template-columns:1fr 1fr;padding:0 2rem}}h1[data-v-a47c673d]{font-weight:500;font-size:2.6rem;position:relative;top:-10px}h3[data-v-a47c673d]{font-size:1.2rem}.greetings h1[data-v-a47c673d],.greetings h3[data-v-a47c673d]{text-align:center}@media (min-width: 1024px){.greetings h1[data-v-a47c673d],.greetings h3[data-v-a47c673d]{text-align:left}}header[data-v-85852c48]{line-height:1.5;max-height:100vh}.logo[data-v-85852c48]{display:block;margin:0 auto 2rem}nav[data-v-85852c48]{width:100%;font-size:12px;text-align:center;margin-top:2rem}nav a.router-link-exact-active[data-v-85852c48]{color:var(--color-text)}nav a.router-link-exact-active[data-v-85852c48]:hover{background-color:transparent}nav a[data-v-85852c48]{display:inline-block;padding:0 1rem;border-left:1px solid var(--color-border)}nav a[data-v-85852c48]:first-of-type{border:0}@media (min-width: 1024px){header[data-v-85852c48]{display:flex;place-items:center;padding-right:calc(var(--section-gap) / 2)}.logo[data-v-85852c48]{margin:0 2rem 0 0}header .wrapper[data-v-85852c48]{display:flex;place-items:flex-start;flex-wrap:wrap}nav[data-v-85852c48]{text-align:left;margin-left:-1rem;font-size:1rem;padding:1rem 0;margin-top:1rem}}.item[data-v-fd0742eb]{margin-top:2rem;display:flex;position:relative}.details[data-v-fd0742eb]{flex:1;margin-left:1rem}i[data-v-fd0742eb]{display:flex;place-items:center;place-content:center;width:32px;height:32px;color:var(--color-text)}h3[data-v-fd0742eb]{font-size:1.2rem;font-weight:500;margin-bottom:.4rem;color:var(--color-heading)}@media (min-width: 1024px){.item[data-v-fd0742eb]{margin-top:0;padding:.4rem 0 1rem calc(var(--section-gap) / 2)}i[data-v-fd0742eb]{top:calc(50% - 25px);left:-26px;position:absolute;border:1px solid var(--color-border);background:var(--color-background);border-radius:8px;width:50px;height:50px}.item[data-v-fd0742eb]:before{content:" ";border-left:1px solid var(--color-border);position:absolute;left:0;bottom:calc(50% + 25px);height:calc(50% - 25px)}.item[data-v-fd0742eb]:after{content:" ";border-left:1px solid var(--color-border);position:absolute;left:0;top:calc(50% + 25px);height:calc(50% - 25px)}.item[data-v-fd0742eb]:first-of-type:before{display:none}.item[data-v-fd0742eb]:last-of-type:after{display:none}} diff --git a/cista/wwwroot/assets/logo-277e0e97.svg b/cista/wwwroot/assets/logo-277e0e97.svg deleted file mode 100644 index 7565660..0000000 --- a/cista/wwwroot/assets/logo-277e0e97.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cista/wwwroot/favicon.ico b/cista/wwwroot/favicon.ico deleted file mode 100644 index df36fcfb72584e00488330b560ebcf34a41c64c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56 zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp= zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8 zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3 z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6 z={Xwx{TKxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S diff --git a/cista/wwwroot/index.html b/cista/wwwroot/index.html deleted file mode 100644 index ed93a06..0000000 --- a/cista/wwwroot/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - Vite App - - - - -
- - - diff --git a/cista/wwwroot/old-index.html b/cista/wwwroot/old-index.html deleted file mode 100755 index db336cb..0000000 --- a/cista/wwwroot/old-index.html +++ /dev/null @@ -1,241 +0,0 @@ - -Storage - -
-

Quick file upload

-

Uses parallel WebSocket connections for increased bandwidth /api/upload

- - -
- -
-

Files

-
    -
    - - diff --git a/pyproject.toml b/pyproject.toml index bcfd2e9..f8e721b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,22 +7,27 @@ name = "cista" dynamic = ["version"] description = "Dropbox-like file server with modern web interface" readme = "README.md" -license = "" authors = [ { name = "Vasanko" }, ] classifiers = [ ] +requires-python = ">=3.11" dependencies = [ "argon2-cffi", + "blake3", + "brotli", "docopt", "inotify", "msgspec", + "natsort", "pathvalidate", "pyjwt", "sanic", + "stream-zip", "tomli_w", ] +requires-python = ">=3.10" [project.urls] Homepage = "" @@ -33,6 +38,7 @@ cista = "cista.__main__:main" [project.optional-dependencies] dev = [ "pytest", + "ruff", ] [tool.hatchling] @@ -63,7 +69,36 @@ testpaths = [ "tests", ] -[tool.isort] -#src_paths = ["cista", "tests"] -line_length = 120 -multi_line_output = 5 +[tool.ruff] +select = ["ALL"] +ignore = [ + "A0", + "ARG001", + "ANN", + "B018", + "BLE001", + "C901", + "COM812", # conflicts with ruff format + "D", + "E501", + "EM1", + "FIX002", + "ISC001", # conflicts with ruff format + "PGH003", + "PLR0912", + "PLR2004", + "PLW0603", + "S101", + "SLF001", + "T201", + "TD0", + "TRY", +] +show-source = true +show-fixes = true + +[tool.ruff.isort] +known-first-party = ["cista"] + +[tool.ruff.per-file-ignores] +"tests/*" = ["S", "ANN", "D", "INP"] diff --git a/tests/test_control.py b/tests/test_control.py index 06c3406..02552f8 100644 --- a/tests/test_control.py +++ b/tests/test_control.py @@ -2,12 +2,11 @@ import tempfile from pathlib import Path import pytest - from cista import config from cista.protocol import Cp, MkDir, Mv, Rename, Rm -@pytest.fixture +@pytest.fixture() def setup_temp_dir(): with tempfile.TemporaryDirectory() as tmpdirname: config.config = config.Config(path=Path(tmpdirname), listen=":0")