commit dae1a31b4c5ffc67c4acdceb6bac12ab0b11f314 Author: li0nhunter Date: Tue Aug 19 09:55:57 2025 -0400 Initial commit diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..b5233cd --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,22 @@ +name: 'Deploy To Dokku' +on: + push: + branches: + - master + - main + +jobs: + deploy: + runs-on: self-hosted + container: node:24 + steps: + - name: Cloning repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Push to dokku + uses: dokku/github-action@master + with: + git_remote_url: 'ssh://dokku@192.168.1.2:22/to-do-list-partner' + ssh_private_key: { secrets.DOKKU_DEPLOY_KEY } \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8738c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,228 @@ +# ---> Vue +# gitignore template for Vue.js projects +# +# Recommended template: Node.gitignore + +# TODO: where does this rule come from? +docs/_book + +# TODO: where does this rule come from? +test/ + +# ---> Node +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# ---> JetBrains +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..dfdf7aa --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,11 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +/dataSources.xml +/deployment.xml +/GitLink.xml diff --git a/.idea/Website-template.iml b/.idea/Website-template.iml new file mode 100644 index 0000000..24643cc --- /dev/null +++ b/.idea/Website-template.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..8ca546d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..0b9cdc6 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,32 @@ + + + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..d23208f --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..602423a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..6df4889 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 0000000..a3471d6 --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3b79db4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +# Use official Node.js image +LABEL authors="Ari Yeger" +### Client Stage +FROM node:24 AS client-build +# Set the working directory for the client +WORKDIR /app/client + +# Copy package files and install dependencies +COPY /client/package*.json ./ +RUN npm ci + +# Copy server source code +COPY /client . + +# Build the server (if using TypeScript or build step) +RUN npm run build + +### Server Stage +FROM node:24 AS server-build +# Set the working directory for the server +WORKDIR /app/server + +# Copy package files and install dependencies +COPY /server/package*.json ./ +RUN npm install + +# Copy server source code +COPY /server . + +# no build step for server, its already JavaScript + +# Production stage +FROM node:24-slim AS production +WORKDIR /app +# Copy built client files from client-build stage +COPY --from=client-build /app/client/dist ./client/dist +# Copy server files from server-build stage +COPY --from=server-build /app/server ./server +WORKDIR /app/server +# Expose the port your server runs on (change if needed) +EXPOSE 8000 + +# Start the server +CMD ["npm", "start"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ad431aa --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2025 li0nhunter + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8d4843 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# Website-template + +vue ts + vite frontend, express backend, with a dockerfile and a deploy script to my dokku + +### steps to get started on the dokku side +1. create a dokku app + ```bash + dokku apps:create to-do-list-partner + ``` +2. create postgres database + ```bash + dokku postgres:create to-do-list-partner-db + ``` +3. link the database to the app + ```bash + dokku postgres:link to-do-list-partner-db to-do-list-partner + ``` +4. setup db if applicable + - dump local db + ```bash + pg_dump -Fc --no-acl --no-owner -h localhost -U > db.dump + ``` + - restore to dokku db + ```bash + dokku postgres:import to-do-list-partner-db < db.dump + ``` +5. set app to use nginx + - set proxy to nginx + ```bash + dokku proxy:set to-do-list-partner nginx + ``` + + - map nginx port to internal docker port + ```bash + dokku ports:add to-do-list-partner :: + ``` +6. set repo dokku deploy key + - copy the private key from dokku into the repo secrets under the name `DOKKU_DEPLOY_KEY` +7. add the public key to known hosts on dokku + ```bash + dokku ssh-keys:add to-do-list-partner + ``` +8. set environment variables + - create a `.env` file on the dokku server + - add the variables to the `.env` file + - add the variables to the app + ```bash + cat .env | xargs dokku config:set --no-restart to-do-list-partner + ``` +it should be ready to go now, you can deploy with the deployment script + \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..33895ab --- /dev/null +++ b/client/README.md @@ -0,0 +1,5 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..342e746 --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,2252 @@ +{ + "name": "client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "client", + "version": "0.0.0", + "dependencies": { + "jwt-decode": "^4.0.0", + "vue": "^3.5.17", + "vue-router": "^4.5.1", + "vue-toast-notification": "^3.1.3" + }, + "devDependencies": { + "@types/node": "^24.0.10", + "@vitejs/plugin-vue": "^6.0.0", + "@volar/typescript": "2.4.23", + "@vue/tsconfig": "^0.7.0", + "bootstrap": "^5.3.3", + "npm-run-all2": "^7.0.2", + "sass": "^1.89.2", + "typescript": "~5.8.3", + "vite": "^7.0.4", + "vue-tsc": "^2.2.12" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", + "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", + "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", + "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", + "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", + "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", + "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", + "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", + "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", + "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", + "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", + "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", + "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", + "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", + "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", + "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", + "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", + "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", + "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", + "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", + "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", + "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", + "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", + "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", + "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", + "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", + "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", + "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz", + "integrity": "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.19" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz", + "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@vue/shared": "3.5.17", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz", + "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz", + "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@vue/compiler-core": "3.5.17", + "@vue/compiler-dom": "3.5.17", + "@vue/compiler-ssr": "3.5.17", + "@vue/shared": "3.5.17", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz", + "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz", + "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz", + "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz", + "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.17", + "@vue/runtime-core": "3.5.17", + "@vue/shared": "3.5.17", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz", + "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.17", + "@vue/shared": "3.5.17" + }, + "peerDependencies": { + "vue": "3.5.17" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz", + "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz", + "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT", + "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, + "license": "MIT" + }, + "node_modules/bootstrap": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz", + "integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "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/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-7.0.2.tgz", + "integrity": "sha512-7tXR+r9hzRNOPNTvXegM+QzCuMjzUIIq66VDunL6j60O4RrExx32XUhlrS7UK4VcdGw5/Wxzb3kfNcFix9JKDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "minimatch": "^9.0.0", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "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": "^18.17.0 || >=20.5.0", + "npm": ">= 9" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rollup": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/sass": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.2", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz", + "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.17", + "@vue/compiler-sfc": "3.5.17", + "@vue/runtime-dom": "3.5.17", + "@vue/server-renderer": "3.5.17", + "@vue/shared": "3.5.17" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-toast-notification": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vue-toast-notification/-/vue-toast-notification-3.1.3.tgz", + "integrity": "sha512-XNyWqwLIGBFfX5G9sK+clq3N3IPlhDjzNdbZaXkEElcotPlWs0wWZailk1vqhdtLYT/93Y4FHAVuzyatLmPZRA==", + "license": "MIT", + "engines": { + "node": ">=12.15.0" + }, + "peerDependencies": { + "vue": "^3.0" + } + }, + "node_modules/vue-tsc": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz", + "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/language-core": "2.2.12" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..3bb05a4 --- /dev/null +++ b/client/package.json @@ -0,0 +1,36 @@ +{ + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "description": "Client application for to-do-list-partner", + "author": "li0nhunter", + "scripts": { + "dev": "npm-run-all --parallel dev:client dev:server", + "dev:server": "cd ../server && npm run dev", + "dev:client": "vite", + "prod": "cd ../server && npm run start", + "build": "run-p build:vue build:sass", + "build:vue": "vue-tsc -b && vite build", + "build:sass": "sass src/assets/css/base.scss src/assets/css/base.css && sass src/assets/css/main.scss src/assets/css/main.css", + "preview": "vite preview" + }, + "dependencies": { + "jwt-decode": "^4.0.0", + "vue": "^3.5.17", + "vue-router": "^4.5.1", + "vue-toast-notification": "^3.1.3" + }, + "devDependencies": { + "@types/node": "^24.0.10", + "@vitejs/plugin-vue": "^6.0.0", + "@vue/tsconfig": "^0.7.0", + "@volar/typescript": "2.4.23", + "bootstrap": "^5.3.3", + "npm-run-all2": "^7.0.2", + "sass": "^1.89.2", + "typescript": "~5.8.3", + "vite": "^7.0.4", + "vue-tsc": "^2.2.12" + } +} diff --git a/client/src/App.vue b/client/src/App.vue new file mode 100644 index 0000000..e139985 --- /dev/null +++ b/client/src/App.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/client/src/assets/css/base.css b/client/src/assets/css/base.css new file mode 100644 index 0000000..dde5199 --- /dev/null +++ b/client/src/assets/css/base.css @@ -0,0 +1,86 @@ +/* semantic color variables for this project */ +@media (prefers-color-scheme: dark), (prefers-color-scheme: no-preference) { + :root { + --color-divider: rgba(84, 84, 84, 0.65); + --color-divider-alt: rgba(84, 84, 84, 0.48); + --color-text: #E7E7E7; + --color-text-alt: #e7e7e7; + --color-modal-background: #464646; + --color-modal-background-inverted: #b9b9b9; + --color-form-background: #222222; + --color-form-background-focus: rgb(56.1, 56.1, 56.1); + --color-border-1: #cccccc; + --color-border-2: rgb(163.2, 163.2, 163.2); + --color-nav-text: #a8a8a8; + --color-nav-text-hover: rgb(185.4, 185.4, 185.4); + --color-nav-text-active: rgb(202.8, 202.8, 202.8); + --color-nav-text-disabled: #909090ff; + --color-background: #181818; + --color-background-soft: rgb(70.2, 70.2, 70.2); + --color-background-mute: rgb(116.4, 116.4, 116.4); + --color-primary: #035768; + --color-primary-hover: rgb(5.2598130841, 152.5345794393, 182.3401869159); + --color-primary-active: rgb(19.9794392523, 209.8037383178, 248.2205607477); + --color-primary-disabled: #909090ff; + --color-secondary: #60aeae; + --color-secondary-hover: rgb(127.8, 190.2, 190.2); + --color-secondary-active: rgb(159.6, 206.4, 206.4); + --color-secondary-disabled: #909090ff; + --color-danger: #fd0000; + --color-danger-hover: rgb(255, 49.4, 49.4); + --color-danger-active: rgb(255, 100.8, 100.8); + --color-danger-disabled: #909090ff; + } +} +@media (prefers-color-scheme: light) { + :root { + --color-divider: rgba(60, 60, 60, 0.29); + --color-divider-alt: rgba(60, 60, 60, 0.12); + --color-text: #181818; + --color-text-alt: #181818; + --color-modal-background: #b9b9b9; + --color-modal-background-inverted: #464646; + --color-form-background: #cdcdcd; + --color-form-background-focus: #a4a4a4; + --color-border-1: #333333ff; + --color-border-2: #6D6D6Dff; + --color-nav-text: #178c85; + --color-nav-text-hover: rgb(18.4, 112, 106.4); + --color-nav-text-active: rgb(13.8, 84, 79.8); + --color-nav-text-disabled: #909090ff; + --color-primary: #99edcd; + --color-primary-hover: rgb(86.7, 225.3, 172.5); + --color-primary-active: rgb(35.1, 198.9, 136.5); + --color-primary-disabled: #909090ff; + --color-background: #f0f0f0; + --color-background-soft: silver; + --color-background-mute: #909090; + --color-secondary: #7fd9bf; + --color-secondary-hover: rgb(73.9493975904, 201.2506024096, 164.4746987952); + --color-secondary-active: rgb(47.2481927711, 159.1518072289, 126.8240963855); + --color-secondary-disabled: #909090ff; + --color-danger: #fd0000; + --color-danger-hover: rgb(202.4, 0, 0); + --color-danger-active: rgb(151.8, 0, 0); + --color-danger-disabled: #909090ff; + } +} +:root { + --color-background-reversion: var(--color-background); + --color-border: var(--color-border-1); + --color-border-invert: var(--color-border-2); + --color-border-hover: var(--color-divider); + --color-primary-reversion: var(--color-primary); + --color-secondary-reversion: var(--color-secondary); + --color-primary-font: var(--color-text); + --color-secondary-font: var(--color-text); + --color-background-font: var(--color-text); + --color-background-mute-font: var(--color-text); + --color-background-soft-font: var(--color-text); + --color-modal-background-font: var(--color-text); + --color-form-background-font: var(--color-text); + --color-modal-background-inverted-font: var(--color-text); + --color-heading: var(--color-nav-text); +} + +/*# sourceMappingURL=base.css.map */ diff --git a/client/src/assets/css/base.css.map b/client/src/assets/css/base.css.map new file mode 100644 index 0000000..6922aa7 --- /dev/null +++ b/client/src/assets/css/base.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["base.scss"],"names":[],"mappings":"AACA;AACA;EACE;IACE;IACA;IAGA;IACA;IAGA;IACA;IAGA;IACA;IAGA;IACA;IAGA;IACA;IACA;IACA;IAGA;IACA;IACA;IAGA;IACA;IACA;IACA;IAIA;IACA;IACA;IACA;IAGA;IACA;IACA;IACA;;;AAIJ;EACE;IACE;IACA;IAEA;IACA;IAGA;IACA;IAGA;IACA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IACA;IACA;IACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA","file":"base.css"} \ No newline at end of file diff --git a/client/src/assets/css/base.scss b/client/src/assets/css/base.scss new file mode 100644 index 0000000..c8eb715 --- /dev/null +++ b/client/src/assets/css/base.scss @@ -0,0 +1,121 @@ +@use "sass:color"; +/* semantic color variables for this project */ +@media (prefers-color-scheme: dark), (prefers-color-scheme: no-preference) { + :root { + --color-divider: rgba(84, 84, 84, 0.65); + --color-divider-alt: rgba(84, 84, 84, 0.48); + + $color-text: #E7E7E7; + --color-text: #{$color-text}; + --color-text-alt: #{color.scale($color-text, $alpha: 64%)}; + + $color-modal-background: #464646ff; + --color-modal-background: #{$color-modal-background}; + --color-modal-background-inverted: #{color.invert($color-modal-background)}; + + $color-form-background: #222222ff; + --color-form-background: #{$color-form-background}; + --color-form-background-focus: #{color.scale($color-form-background, $lightness: 10%)}; + + $color-border-1: #ccccccff; + --color-border-1: #{$color-border-1}; + --color-border-2: #{color.scale($color-border-1, $lightness: -20%)}; + + $color-nav-text: #a8a8a8ff; + --color-nav-text: #{$color-nav-text}; + --color-nav-text-hover: #{color.scale($color-nav-text, $lightness: 20%)}; + --color-nav-text-active: #{color.scale($color-nav-text, $lightness: 40%)}; + --color-nav-text-disabled: #909090ff; + + $color-background: #181818ff; + --color-background: #{$color-background}; + --color-background-soft: #{color.scale($color-background, $lightness: 20%)}; + --color-background-mute: #{color.scale($color-background, $lightness: 40%)}; + + $color-primary: #035768ff; + --color-primary: #{$color-primary}; + --color-primary-hover: #{color.scale($color-primary, $lightness: 20%)}; + --color-primary-active: #{color.scale($color-primary, $lightness: 40%)}; + --color-primary-disabled: #909090ff; + + + $color-secondary: #60AEAEff; + --color-secondary: #{$color-secondary}; + --color-secondary-hover: #{color.scale($color-secondary, $lightness: 20%)}; + --color-secondary-active: #{color.scale($color-secondary, $lightness: 40%)}; + --color-secondary-disabled: #909090ff; + + $color-danger: #fd0000ff; + --color-danger: #{$color-danger}; + --color-danger-hover: #{color.scale($color-danger, $lightness: 20%)}; + --color-danger-active: #{color.scale($color-danger, $lightness: 40%)}; + --color-danger-disabled: #909090ff; + } +} + +@media (prefers-color-scheme: light) { + :root { + --color-divider: rgba(60, 60, 60, 0.29); + --color-divider-alt: rgba(60, 60, 60, 0.12); + $color-text: #181818; + --color-text: #{$color-text}; + --color-text-alt: #{color.scale($color-text, $alpha: 66%)}; + + $color-modal-background: #b9b9b9ff; + --color-modal-background: #{$color-modal-background}; + --color-modal-background-inverted: #{color.invert($color-modal-background)}; + + $color-form-background: #cdcdcdff; + --color-form-background: #{$color-form-background}; + --color-form-background-focus: #{color.scale($color-form-background, $lightness: -20%)}; + --color-border-1: #333333ff; + --color-border-2: #6D6D6Dff; + + $color-nav-text: #178c85ff; + --color-nav-text: #{$color-nav-text}; + --color-nav-text-hover: #{color.scale($color-nav-text, $lightness: -20%)}; + --color-nav-text-active: #{color.scale($color-nav-text, $lightness: -40%)}; + --color-nav-text-disabled: #909090ff; + + $color-primary: #99edcdff; + --color-primary: #{$color-primary}; + --color-primary-hover: #{color.scale($color-primary, $lightness: -20%)}; + --color-primary-active: #{color.scale($color-primary, $lightness: -40%)}; + --color-primary-disabled: #909090ff; + + $color-background: #f0f0f0ff; + --color-background: #{$color-background}; + --color-background-soft: #{color.scale($color-background, $lightness: -20%)}; + --color-background-mute: #{color.scale($color-background, $lightness: -40%)}; + + $color-secondary: #7fd9bfff; + --color-secondary: #{$color-secondary}; + --color-secondary-hover: #{color.scale($color-secondary, $lightness: -20%)}; + --color-secondary-active: #{color.scale($color-secondary, $lightness: -40%)}; + --color-secondary-disabled: #909090ff; + + $color-danger: #fd0000ff; + --color-danger: #{$color-danger}; + --color-danger-hover: #{color.scale($color-danger, $lightness: -20%)}; + --color-danger-active: #{color.scale($color-danger, $lightness: -40%)}; + --color-danger-disabled: #909090ff; + } +} + +:root { + --color-background-reversion: var(--color-background); + --color-border: var(--color-border-1); + --color-border-invert: var(--color-border-2); + --color-border-hover: var(--color-divider); + --color-primary-reversion: var(--color-primary); + --color-secondary-reversion: var(--color-secondary); + --color-primary-font: var(--color-text); + --color-secondary-font: var(--color-text); + --color-background-font: var(--color-text); + --color-background-mute-font: var(--color-text); + --color-background-soft-font: var(--color-text); + --color-modal-background-font: var(--color-text); + --color-form-background-font: var(--color-text); + --color-modal-background-inverted-font: var(--color-text); + --color-heading: var(--color-nav-text); +} \ No newline at end of file diff --git a/client/src/assets/css/jic.css b/client/src/assets/css/jic.css new file mode 100644 index 0000000..3e2335e --- /dev/null +++ b/client/src/assets/css/jic.css @@ -0,0 +1,64 @@ +.dp__input, +.dp__input:focus, +.dp__input:hover { + background-color: var(--color-background) !important; + color: var(--color-background-font) !important; + border-color: var(--color-modal-background-inverted) !important; +} + +.dp__theme_light { + background-color: transparent !important; + border-color: var(--color-modal-background-inverted) !important; + --dp-range-between-dates-background-color: var(--color-background) !important; + --dp-range-between-border-color: var(--color-background) !important; + --dp-hover-color: var(--color-form-background) !important; + --dp-background-color: var(--color-background-mute) !important; +} + +.dp__arrow_bottom { + background-color: var(--color-background-mute) !important; + color: var(--color-background-mute-font) !important; + border-color: var(--color-modal-background-inverted) !important; + --dp-range-between-dates-background-color: var(--color-background) !important; + --dp-range-between-border-color: var(--color-background) !important; + --dp-hover-color: var(--color-form-background) !important; + --dp-background-color: var(--color-background-mute) !important; +} + +.dp__input_icon, +.dp__clear_icon { + color: var(--color-background-font) !important; +} + + +.accordion-button, +.accordion-button:not(.collapsed) { + color: var(--color-background-mute-font); + background-color: var(--color-background-mute); +} + +.accordion-item { + border: 1px solid var(--color-modal-background-inverted); + background-color: var(--color-background); +} + +.dropdown-menu.show, +.dropdown-menu .dropdown-menu { + background-color: var(--color-background); + border: 1px solid var(--color-modal-background-inverted); +} + +.dropdown-item { + color: var(--color-nav-text); +} + +.dropdown-menu li a:hover, +.dropdown-item:hover, +.dropdown-submenu .dropdown-item:hover { + background-color: var(--color-background); + color: var(--color-background-font); +} + +.dropdown { + cursor: pointer; +} \ No newline at end of file diff --git a/client/src/assets/css/main.css b/client/src/assets/css/main.css new file mode 100644 index 0000000..67bf128 --- /dev/null +++ b/client/src/assets/css/main.css @@ -0,0 +1,309 @@ +@import url("../../../node_modules/bootstrap/dist/css/bootstrap.min.css"); +@import url("https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,100..900;1,100..900&display=swap"); +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; +} + +body { + min-height: 100vh; + width: 100vw; + color: var(--color-background-font); + background: var(--color-background); + transition: color 0.2s ease, background-color 0.2s ease; + 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: 1rem; + font-weight: 500; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-align: left; + overflow-x: hidden; + overflow-y: auto; + box-sizing: border-box; +} + +.media-content { + overflow: hidden; + position: absolute; + left: 175px; + margin-top: 1rem; + margin-bottom: 1rem; +} + +input, select, textarea { + color: var(--color-background-font); + background-color: var(--color-form-background); + border-color: var(--color-border); +} + +.input-group { + display: flex; + align-items: center; +} + +.input-group-text { + background-color: var(--color-form-background); + padding: 0.5rem; + display: flex; + align-items: center; +} + +input:disabled { + background-color: var(--color-primary-disabled); +} + +.container { + flex-direction: column; + align-items: center; + justify-content: center; +} + +.form-check-label { + user-select: none; +} + +.tooltip-modal { + position: relative; + display: inline-block; + opacity: 1; +} + +.tooltip { + position: absolute; + transform: translateY(-50%); + margin-left: 3rem; + padding: 6px 10px; + border-radius: 4px; + color: rgb(0, 0, 0); + font-size: 15px; + white-space: nowrap; + opacity: 0.9; +} + +.tooltip-modal .tooltiptext { + visibility: hidden; + opacity: 0; + transition: opacity 0.2s linear, visibility 0.2s linear 0.2s; + width: 200px; + background-color: var(--color-modal-background-inverted); + color: var(--color-background); + text-align: center; + border-radius: 6px; + padding: 5px 0; + position: absolute; + z-index: 10; + bottom: 125%; + left: 50%; + margin-left: -100px; +} + +.tooltip-modal .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: var(--color-modal-background-inverted) transparent transparent transparent; +} + +.tooltip-modal:hover .tooltiptext { + visibility: visible; + opacity: 1; + transition: opacity 0.2s linear; +} + +.tooltip-modal .tooltiptext:hover { + visibility: hidden; + opacity: 0; +} + +.btn { + user-select: none; +} + +.btn:hover { + cursor: pointer; +} + +.input-group:focus-within .input-group-text:not(:focus), +.form-check-input:focus, +.form-control:focus { + outline: none; + box-shadow: none; + border-color: var(--color-secondary-active); + color: var(--color-form-background-font); + background-color: var(--color-form-background-focus); + transition: border-color 0.2s ease-in-out; +} + +.input-group .input-group-text, +.input-group-text:focus { + border-color: var(--color-border); + transition: border-color 0.2s ease-in-out; +} + +.form-control { + color: var(--color-background-font); + background-color: var(--color-form-background); + border-color: var(--color-border); +} + +.form-control::placeholder { + color: rgba(128, 128, 128, 0.8156862745); +} + +select.form-control, +select#location { + appearance: none; + -webkit-appearance: none; + background-image: url("data:image/svg+xml;charset=UTF-8,"); + background-repeat: no-repeat; + background-position: right 0.25rem center; + background-size: 1.25em; + padding-right: 2em; + transition: background 0.2s, border-color 0.2s; +} + +select.form-control:hover, +select#location:hover, +select.form-control:focus, +select#location:focus { + border-color: var(--color-secondary-active); +} + +select.form-control:focus, +select#location:focus { + box-shadow: 0 0 0 0.2rem rgba(134, 223, 195, 0.25); +} + +.is-primary { + color: var(--color-primary-font); + background-color: var(--color-primary); + border-color: var(--color-primary); +} + +.is-secondary { + color: var(--color-secondary-font); + background-color: var(--color-secondary); + border-color: var(--color-secondary); +} + +.btn-primary { + --bs-btn-color: var(--color-primary-font); + --bs-btn-bg: var(--color-primary); + --bs-btn-border-color: var(--color-primary); + --bs-btn-hover-color: var(--color-primary-font); + --bs-btn-hover-bg: var(--color-primary-hover); + --bs-btn-hover-border-color: var(--color-primary-hover); + --bs-btn-focus-shadow-rgb: 49, 132, 253; + --bs-btn-active-color: var(--color-primary-font); + --bs-btn-active-bg: var(--color-primary-active); + --bs-btn-active-border-color: var(--color-primary-active); + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: var(--color-primary-font); + --bs-btn-disabled-bg: var(--color-primary-disabled); + --bs-btn-disabled-border-color: var(--color-primary-disabled); +} + +.btn-secondary { + --bs-btn-color: var(--color-secondary-font); + --bs-btn-bg: var(--color-secondary); + --bs-btn-border-color: var(--color-secondary); + --bs-btn-hover-color: var(--color-secondary-font); + --bs-btn-hover-bg: var(--color-secondary-hover); + --bs-btn-hover-border-color: var(--color-secondary-hover); + --bs-btn-focus-shadow-rgb: 49, 132, 253; + --bs-btn-active-color: var(--color-secondary-font); + --bs-btn-active-bg: var(--color-secondary-active); + --bs-btn-active-border-color: var(--color-secondary-active); + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: var(--color-secondary-font); + --bs-btn-disabled-bg: var(--color-secondary-disabled); + --bs-btn-disabled-border-color: var(--color-secondary-disabled); +} + +.btn-danger { + --bs-btn-bg: var(--color-danger); + --bs-btn-border-color: var(--color-danger); + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: var(--color-danger-hover); + --bs-btn-hover-border-color: var(--color-danger-hover); + --bs-btn-focus-shadow-rgb: 225, 83, 97; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: var(--color-danger-active); + --bs-btn-active-border-color: var(--color-danger-active); + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: var(--color-danger-disabled); + --bs-btn-disabled-border-color: var(--color-danger-disabled); +} + +.btn-submit { + --bs-btn-color: var(--color-primary-font); + --bs-btn-bg: var(--color-primary); + --bs-btn-border-color: var(--color-modal-background-inverted); + --bs-btn-hover-color: var(--color-primary-font); + --bs-btn-hover-bg: var(--color-primary-hover); + --bs-btn-hover-border-color: var(--color-modal-background-inverted); + --bs-btn-focus-shadow-rgb: 49, 132, 253; + --bs-btn-active-color: var(--color-primary-font); + --bs-btn-active-bg: var(--color-primary-active); + --bs-btn-active-border-color: var(--color-modal-background-inverted); + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: var(--color-primary-font); + --bs-btn-disabled-bg: var(--color-primary-disabled); + --bs-btn-disabled-border-color: var(--color-modal-background-inverted); +} + +.routerLink { + color: var(--color-primary); +} + +.routerLink:hover { + color: var(--color-primary-hover); +} + +.routerLink:active { + color: var(--color-primary-active); +} + +.alert { + --bs-alert-border: 1px solid #ad6060; + --bs-alert-bg: #e4b6b6; +} + +.offcanvas-header { + background-color: var(--color-background); + color: var(--color-background-font); + box-shadow: var(--color-text) 0, 0, 10px; +} + +.offcanvas-body { + background-color: var(--color-background); + color: var(--color-background-font); + position: relative; +} + +.ellipsis { + color: var(--color-background-font); +} + +.fade-enter-active, .fade-leave-active { + transition: opacity 0.5s; +} + +.fade-enter-from, .fade-leave-to { + opacity: 0; +} + +.fade-enter-to, .fade-leave-from { + opacity: 1; +} + +/*# sourceMappingURL=main.css.map */ diff --git a/client/src/assets/css/main.css.map b/client/src/assets/css/main.css.map new file mode 100644 index 0000000..f261302 --- /dev/null +++ b/client/src/assets/css/main.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["main.scss"],"names":[],"mappings":"AAAQ;AACA;AAER;AAAA;AAAA;EAGE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAYA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACI;EACA;;;AAGJ;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;AAAA;AAAA;EAGE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;;;AAGF;AAAA;EAEE;;;AAGF;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE","file":"main.css"} \ No newline at end of file diff --git a/client/src/assets/css/main.scss b/client/src/assets/css/main.scss new file mode 100644 index 0000000..2a3bb38 --- /dev/null +++ b/client/src/assets/css/main.scss @@ -0,0 +1,322 @@ +@import url('../../../node_modules/bootstrap/dist/css/bootstrap.min.css'); +@import url('https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,100..900;1,100..900&display=swap'); + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; +} + +body { + min-height: 100vh; + width: 100vw; + color: var(--color-background-font); + background: var(--color-background); + transition: color 0.2s ease, background-color 0.2s ease; + 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: 1rem; + font-weight: 500; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-align: left; + overflow-x: hidden; + overflow-y: auto; + box-sizing: border-box; +} + +.media-content { + overflow: hidden; + position: absolute; + left: 175px; + margin-top: 1rem; + margin-bottom: 1rem; +} + +input, select, textarea { + color: var(--color-background-font); + background-color: var(--color-form-background); + border-color: var(--color-border); +} + +.input-group { + display: flex; + align-items: center; +} + +.input-group-text { + background-color: var(--color-form-background); + padding: 0.5rem; + display: flex; + align-items: center; +} + +input:disabled { + background-color: var(--color-primary-disabled); +} + +.container { + flex-direction: column; + align-items: center; + justify-content: center; +} + +.form-check-label { + user-select: none; +} + +.tooltip-modal { + position: relative; + display: inline-block; + opacity: 1; +} + +.tooltip { + position: absolute; + transform: translateY(-50%); + margin-left: 3rem; + padding: 6px 10px; + border-radius: 4px; + color: rgb(0, 0, 0); + font-size: 15px; + white-space: nowrap; + opacity: 0.9; +} + +.tooltip-modal .tooltiptext { + visibility: hidden; + opacity: 0; + transition: opacity 0.2s linear, + visibility 0.2s linear 0.2s; + width: 200px; + background-color: var(--color-modal-background-inverted); + color: var(--color-background); + text-align: center; + border-radius: 6px; + padding: 5px 0; + position: absolute; + z-index: 10; + bottom: 125%; + left: 50%; + margin-left: -100px; +} + +.tooltip-modal .tooltiptext::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: var(--color-modal-background-inverted) transparent transparent transparent; +} + +.tooltip-modal:hover .tooltiptext { + visibility: visible; + opacity: 1; + transition: opacity 0.2s linear; +} + +.tooltip-modal .tooltiptext:hover { + visibility: hidden; + opacity: 0; +} + +.btn { + user-select: none; +} + +.btn:hover { + cursor: pointer; +} + + +.input-group:focus-within .input-group-text:not(:focus), +.form-check-input:focus, +.form-control:focus { + outline: none; + box-shadow: none; + border-color: var(--color-secondary-active); + color: var(--color-form-background-font); + background-color: var(--color-form-background-focus); + transition: border-color 0.2s ease-in-out; +} + +.input-group .input-group-text, +.input-group-text:focus { + border-color: var(--color-border); + transition: border-color 0.2s ease-in-out; +} + +.form-control { + color: var(--color-background-font); + background-color: var(--color-form-background); + border-color: var(--color-border) +} + +.form-control::placeholder { + color: #808080D0; +} + +select.form-control, +select#location { + appearance: none; + -webkit-appearance: none; + background-image: url("data:image/svg+xml;charset=UTF-8,"); + background-repeat: no-repeat; + background-position: right 0.25rem center; + background-size: 1.25em; + padding-right: 2em; + transition: background 0.2s, border-color 0.2s; +} + +select.form-control:hover, +select#location:hover, +select.form-control:focus, +select#location:focus { + border-color: var(--color-secondary-active) +} + +select.form-control:focus, +select#location:focus { + box-shadow: 0 0 0 0.2rem rgba(134, 223, 195, 0.25); +} + +.is-primary{ + color: var(--color-primary-font); + background-color: var(--color-primary); + border-color: var(--color-primary); +} + +.is-secondary{ + color: var(--color-secondary-font); + background-color: var(--color-secondary); + border-color: var(--color-secondary); +} + +.btn-primary { + --bs-btn-color: var(--color-primary-font); + --bs-btn-bg: var(--color-primary); + --bs-btn-border-color: var(--color-primary); + --bs-btn-hover-color: var(--color-primary-font); + --bs-btn-hover-bg: var(--color-primary-hover); + --bs-btn-hover-border-color: var(--color-primary-hover); + --bs-btn-focus-shadow-rgb: 49, 132, 253; + --bs-btn-active-color: var(--color-primary-font); + --bs-btn-active-bg: var(--color-primary-active); + --bs-btn-active-border-color: var(--color-primary-active); + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: var(--color-primary-font); + --bs-btn-disabled-bg: var(--color-primary-disabled); + --bs-btn-disabled-border-color: var(--color-primary-disabled); +} + +.btn-secondary { + --bs-btn-color: var(--color-secondary-font); + --bs-btn-bg: var(--color-secondary); + --bs-btn-border-color: var(--color-secondary); + --bs-btn-hover-color: var(--color-secondary-font); + --bs-btn-hover-bg: var(--color-secondary-hover); + --bs-btn-hover-border-color: var(--color-secondary-hover); + --bs-btn-focus-shadow-rgb: 49, 132, 253; + --bs-btn-active-color: var(--color-secondary-font); + --bs-btn-active-bg: var(--color-secondary-active); + --bs-btn-active-border-color: var(--color-secondary-active); + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: var(--color-secondary-font); + --bs-btn-disabled-bg: var(--color-secondary-disabled); + --bs-btn-disabled-border-color: var(--color-secondary-disabled); +} + +.btn-danger { + --bs-btn-bg: var(--color-danger); + --bs-btn-border-color: var(--color-danger); + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: var(--color-danger-hover); + --bs-btn-hover-border-color: var(--color-danger-hover); + --bs-btn-focus-shadow-rgb: 225, 83, 97; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: var(--color-danger-active); + --bs-btn-active-border-color: var(--color-danger-active); + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: var(--color-danger-disabled); + --bs-btn-disabled-border-color: var(--color-danger-disabled); +} + +.btn-submit { + --bs-btn-color: var(--color-primary-font); + --bs-btn-bg: var(--color-primary); + --bs-btn-border-color: var(--color-modal-background-inverted); + --bs-btn-hover-color: var(--color-primary-font); + --bs-btn-hover-bg: var(--color-primary-hover); + --bs-btn-hover-border-color: var(--color-modal-background-inverted); + --bs-btn-focus-shadow-rgb: 49, 132, 253; + --bs-btn-active-color: var(--color-primary-font); + --bs-btn-active-bg: var(--color-primary-active); + --bs-btn-active-border-color: var(--color-modal-background-inverted); + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: var(--color-primary-font); + --bs-btn-disabled-bg: var(--color-primary-disabled); + --bs-btn-disabled-border-color: var(--color-modal-background-inverted); +} + +.routerLink { + color: var(--color-primary); +} + +.routerLink:hover { + color: var(--color-primary-hover); +} + +.routerLink:active { + color: var(--color-primary-active); +} + +.alert { + --bs-alert-border: 1px solid #ad6060; + --bs-alert-bg: #e4b6b6; +} + +.offcanvas-header { + background-color: var(--color-background); + color: var(--color-background-font); + box-shadow: var(--color-text) 0, 0, 10px; +} + +.offcanvas-body { + background-color: var(--color-background); + color: var(--color-background-font); + position: relative; +} + +.ellipsis { + color: var(--color-background-font); +} + +.fade-enter-active, .fade-leave-active { + transition: opacity .5s; +} + +.fade-enter-from, .fade-leave-to { + opacity: 0; +} + +.fade-enter-to, .fade-leave-from { + opacity: 1; +} + diff --git a/client/src/assets/img/Instagram_icon.png b/client/src/assets/img/Instagram_icon.png new file mode 100644 index 0000000..081eb4d Binary files /dev/null and b/client/src/assets/img/Instagram_icon.png differ diff --git a/client/src/assets/img/L10n.png b/client/src/assets/img/L10n.png new file mode 100644 index 0000000..98a3c32 Binary files /dev/null and b/client/src/assets/img/L10n.png differ diff --git a/client/src/assets/img/Linkedin_icon.png b/client/src/assets/img/Linkedin_icon.png new file mode 100644 index 0000000..96a7d9a Binary files /dev/null and b/client/src/assets/img/Linkedin_icon.png differ diff --git a/client/src/assets/svg/edit.svg b/client/src/assets/svg/edit.svg new file mode 100644 index 0000000..260d1ca --- /dev/null +++ b/client/src/assets/svg/edit.svg @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/client/src/assets/svg/eye-slash.svg b/client/src/assets/svg/eye-slash.svg new file mode 100644 index 0000000..d3e2396 --- /dev/null +++ b/client/src/assets/svg/eye-slash.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/client/src/assets/svg/eye.svg b/client/src/assets/svg/eye.svg new file mode 100644 index 0000000..033faf4 --- /dev/null +++ b/client/src/assets/svg/eye.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/client/src/assets/svg/trash.svg b/client/src/assets/svg/trash.svg new file mode 100644 index 0000000..fe71de0 --- /dev/null +++ b/client/src/assets/svg/trash.svg @@ -0,0 +1,17 @@ + + + + + trash + Created with Sketch Beta. + + + + + + + + + + \ No newline at end of file diff --git a/client/src/components/Modal.vue b/client/src/components/Modal.vue new file mode 100644 index 0000000..80b99c3 --- /dev/null +++ b/client/src/components/Modal.vue @@ -0,0 +1,84 @@ + + + + + \ No newline at end of file diff --git a/client/src/components/Navbar.vue b/client/src/components/Navbar.vue new file mode 100644 index 0000000..1396259 --- /dev/null +++ b/client/src/components/Navbar.vue @@ -0,0 +1,182 @@ + + + + + \ No newline at end of file diff --git a/client/src/components/Pagination.vue b/client/src/components/Pagination.vue new file mode 100644 index 0000000..b3cd8a0 --- /dev/null +++ b/client/src/components/Pagination.vue @@ -0,0 +1,40 @@ + + + + + \ No newline at end of file diff --git a/client/src/components/Table.vue b/client/src/components/Table.vue new file mode 100644 index 0000000..254f8d3 --- /dev/null +++ b/client/src/components/Table.vue @@ -0,0 +1,276 @@ + + + + + \ No newline at end of file diff --git a/client/src/components/readableComponents/ReadableButton.vue b/client/src/components/readableComponents/ReadableButton.vue new file mode 100644 index 0000000..fb2db22 --- /dev/null +++ b/client/src/components/readableComponents/ReadableButton.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/client/src/components/readableComponents/ReadableDiv.vue b/client/src/components/readableComponents/ReadableDiv.vue new file mode 100644 index 0000000..fc5443c --- /dev/null +++ b/client/src/components/readableComponents/ReadableDiv.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/client/src/components/readableComponents/ReadableInput.vue b/client/src/components/readableComponents/ReadableInput.vue new file mode 100644 index 0000000..16d3a65 --- /dev/null +++ b/client/src/components/readableComponents/ReadableInput.vue @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/client/src/components/readableComponents/ReadableSVG.vue b/client/src/components/readableComponents/ReadableSVG.vue new file mode 100644 index 0000000..9907591 --- /dev/null +++ b/client/src/components/readableComponents/ReadableSVG.vue @@ -0,0 +1,65 @@ + + + + \ No newline at end of file diff --git a/client/src/directives/vRecolorSvg.ts b/client/src/directives/vRecolorSvg.ts new file mode 100644 index 0000000..b26defa --- /dev/null +++ b/client/src/directives/vRecolorSvg.ts @@ -0,0 +1,56 @@ +// client/src/directives/vUseReadableSvgColor.ts +import { useReadableTextColor } from './vRecolorText.ts'; // reuse your function +import { darkModeActive } from "@models/globals.ts"; +import { watch } from 'vue'; + +function updateSvgColor(el: SVGElement) { + let bgColor: string | null = window.getComputedStyle(el).backgroundColor; + if (!bgColor || bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') { + let parent = el.parentElement; + bgColor = parent ? window.getComputedStyle(parent).backgroundColor : null; + while (parent && !(bgColor && bgColor !== 'transparent' && bgColor !== 'rgba(0, 0, 0, 0)')) { + parent = parent.parentElement; + if (parent) bgColor = window.getComputedStyle(parent).backgroundColor; + else bgColor = null; + } + } + if (bgColor) { + el.style.fill = useReadableTextColor(bgColor); + } else { + const stopWatch = watch( + () => el.parentElement, + (parent) => { + if (parent) { + el.style.fill = useReadableTextColor(window.getComputedStyle(parent).backgroundColor); + stopWatch(); + } + } + ); + } +} + +export default { + mounted(el: SVGElement) { + const updateColor = () => updateSvgColor(el); + updateColor(); + el.addEventListener('click', updateColor); + el.addEventListener('mouseenter', updateColor); + el.addEventListener('mouseleave', updateColor); + el.addEventListener('transitionend', updateColor); + el.addEventListener('change', updateColor); + watch(() => darkModeActive.value, () => updateSvgColor(el)); + (el as any)._readableSvgListeners = [updateColor]; + }, + unmounted(el: SVGElement) { + const listeners = (el as any)._readableSvgListeners || []; + for (const listener of listeners) { + el.removeEventListener('click', listener); + el.removeEventListener('mouseenter', listener); + el.removeEventListener('mouseleave', listener); + el.removeEventListener('transitionend', listener); + el.removeEventListener('change', listener); + + } + delete (el as any)._readableSvgListeners; + } +} \ No newline at end of file diff --git a/client/src/directives/vRecolorText.ts b/client/src/directives/vRecolorText.ts new file mode 100644 index 0000000..0d11887 --- /dev/null +++ b/client/src/directives/vRecolorText.ts @@ -0,0 +1,136 @@ +import {watch} from "vue"; +import {darkModeActive} from "@models/globals.ts"; + +function getLuminance(color: string): number { + let r = 0, g = 0, b = 0; + //parse hex color to RGB + if(/^#.*$/.test(color)) { + if (/^#(|[0-9A-Fa-f]{6})$/.test(color)) { + // Parse hex color + r = parseInt(color.substring(1, 3), 16); + g = parseInt(color.substring(3, 5), 16); + b = parseInt(color.substring(5, 7), 16); + } else if (/^#([0-9A-Fa-f]{3})$/.test(color)) { + // Parse short hex color + r = parseInt(color[1] + color[1], 16); + g = parseInt(color[2] + color[2], 16); + b = parseInt(color[3] + color[3], 16); + } else if (/^#([0-9A-Fa-f]{8})$/.test(color)) { + // Parse hex with alpha + r = parseInt(color.substring(1, 3), 16); + g = parseInt(color.substring(3, 5), 16); + b = parseInt(color.substring(5, 7), 16); + } else { + throw new Error('Invalid color format: cause: ' + color); + } + } else if (/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*([\d.]+)\s*)?\)$/.test(color)) { + // Parse RGB or RGBA + const rgbaMatch = color.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*([\d.]+)\s*)?\)/); + if (rgbaMatch) { + r = parseInt(rgbaMatch[1], 10); + g = parseInt(rgbaMatch[2], 10); + b = parseInt(rgbaMatch[3], 10); + } + } else { + throw new Error('Invalid color format: cause: ' + color); + } + // RGB to sRGB conversion + r = r / 255; + g = g / 255; + b = b / 255; + + // sRBG to linear RGB conversion + r = r <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4); + g = g <= 0.04045 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4); + b = b <= 0.04045 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4); + + // Calculate luminance + return (0.1726 * r + 0.7552 * g + 0.0722 * b); +} + +const blackTextColor = '#181818'; +const whiteTextColor = '#E7E7E7'; +const blackLuminance = getLuminance(blackTextColor); +const whiteLuminance = getLuminance(whiteTextColor); + +const blackTextColorDeep = '#0B0B0B'; +const whiteTextColorDeep = '#F4F4F4'; +const blackLuminanceDeep = getLuminance(blackTextColorDeep); +const whiteLuminanceDeep = getLuminance(whiteTextColorDeep); + +const blackTextColorAbsolute = '#000000'; +const whiteTextColorAbsolute = '#FFFFFF'; +const blackLuminanceAbsolute = getLuminance(blackTextColorAbsolute); +const whiteLuminanceAbsolute = getLuminance(whiteTextColorAbsolute); + +export function useReadableTextColor(bgColor: string): string { + const bgLuminance = getLuminance(bgColor); + + let blackLighter = Math.max(bgLuminance, blackLuminance); + let blackDarker = Math.min(bgLuminance, blackLuminance); + let whiteLighter = Math.max(bgLuminance, whiteLuminance); + let whiteDarker = Math.min(bgLuminance, whiteLuminance); + + // Calculate contrast ratio + let blackRatio = (blackLighter + 0.05) / (blackDarker + 0.05); + let whiteRatio = (whiteLighter + 0.05) / (whiteDarker + 0.05); + // Return black or white based on luminance + if (blackRatio >= 4.5 || whiteRatio >= 4.5) { + //console.debug("bgL:", bgLuminance, "bL:", blackLuminance, "wL:", whiteLuminance, "bR:", blackRatio, "wR:", whiteRatio); + return blackRatio > whiteRatio ? '#181818' : '#E7E7E7'; + } + // If contrast is not enough, use deep colors + blackLighter = Math.max(bgLuminance, blackLuminanceDeep); + blackDarker = Math.min(bgLuminance, blackLuminanceDeep); + whiteLighter = Math.max(bgLuminance, whiteLuminanceDeep); + whiteDarker = Math.min(bgLuminance, whiteLuminanceDeep); + blackRatio = (blackLighter + 0.05) / (blackDarker + 0.05); + whiteRatio = (whiteLighter + 0.05) / (whiteDarker + 0.05); + if (blackRatio >= 4.5 || whiteRatio >= 4.5) { + //console.debug("bgL:", bgLuminance, "bL:", blackLuminanceDeep, "wL:", whiteLuminanceDeep, "bR:", blackRatio, "wR:", whiteRatio); + return blackRatio > whiteRatio ? '#0B0B0B' : '#F4F4F4'; + } + // If still not enough, use absolute colors + blackLighter = Math.max(bgLuminance, blackLuminanceAbsolute); + blackDarker = Math.min(bgLuminance, blackLuminanceAbsolute); + whiteLighter = Math.max(bgLuminance, whiteLuminanceAbsolute); + whiteDarker = Math.min(bgLuminance, whiteLuminanceAbsolute); + blackRatio = (blackLighter + 0.05) / (blackDarker + 0.05); + whiteRatio = (whiteLighter + 0.05) / (whiteDarker + 0.05); + if (blackRatio >= 4.5 || whiteRatio >= 4.5) { + //console.debug("bgL:", bgLuminance, "bL:", blackLuminanceAbsolute, "wL:", whiteLuminanceAbsolute, "bR:", blackRatio, "wR:", whiteRatio); + return blackRatio > whiteRatio ? '#000000' : '#FFFFFF'; + } + //console.warn(`Not enough contrast for background color: ${bgColor}. Using fallback colors.`); + return bgLuminance > 0.5 ? blackTextColor : whiteTextColor; // Fallback to default colors +} + +function updateTextColor(el: HTMLElement) { + el.style.color = useReadableTextColor(window.getComputedStyle(el).backgroundColor); +} + +export default { + mounted(el: HTMLElement) { + const updateColor = () => updateTextColor(el); + // Initial color update + updateColor(); + // Listen for background color changes + el.addEventListener('click', updateColor); + el.addEventListener('mouseover', updateColor); + el.addEventListener('mouseout', updateColor); + el.addEventListener('transitionend', updateColor); + watch(() => darkModeActive.value, () => updateTextColor(el)); + (el as any)._readableTextListeners = [updateColor]; + }, + unmounted(el: HTMLElement) { + // Remove event listeners + const listeners = (el as any)._readableTextListeners || []; + for (const listener of listeners) { + el.removeEventListener('click', listener); + el.removeEventListener('mouseover', listener); + el.removeEventListener('mouseout', listener); + el.removeEventListener('transitionend', listener); + } + delete (el as any)._readableTextListeners; + } +} \ No newline at end of file diff --git a/client/src/main.ts b/client/src/main.ts new file mode 100644 index 0000000..1b39d53 --- /dev/null +++ b/client/src/main.ts @@ -0,0 +1,26 @@ +import {createApp} from "vue"; +import '@css/base.css' +import '@css/main.css' +import App from './App.vue' +import router from "@/router"; +import { useToast } from "vue-toast-notification"; +import 'vue-toast-notification/dist/theme-bootstrap.css'; +import { session } from "@models/session.ts"; +import {jwtDecode} from 'jwt-decode'; +import type {SecureUser} from "@models/session.ts"; + +if (localStorage.getItem("token") && localStorage.getItem("username")) { + const decode: SecureUser = jwtDecode(localStorage.getItem("token") || ""); + session.user = { + username: localStorage.getItem("username") || "", + token: localStorage.getItem("token") || "", + role: decode?.role || "user" + }; + session.token = localStorage.getItem("token"); + +} + +createApp(App) + .use(router) + .use(useToast) + .mount('#app') diff --git a/client/src/models/TransferTypes.ts b/client/src/models/TransferTypes.ts new file mode 100644 index 0000000..c6e06e4 --- /dev/null +++ b/client/src/models/TransferTypes.ts @@ -0,0 +1,12 @@ +export interface DataEnvelope { + data: T; + message?: string; + error?: Error; +} +export interface DataListEnvelope extends DataEnvelope{ + data: T[]; + totalItems?: number; + pageLimit?: number; +} + +export type DynamicDataEnvelope = T extends (infer U)[] ? DataListEnvelope : DataEnvelope; \ No newline at end of file diff --git a/client/src/models/globals.ts b/client/src/models/globals.ts new file mode 100644 index 0000000..c959054 --- /dev/null +++ b/client/src/models/globals.ts @@ -0,0 +1,11 @@ +import { ref } from "vue"; + +export const isMobile = ref(window.innerWidth <= 768); +export function updateIsMobile() {isMobile.value = window.innerWidth <= 768;} + +export const routerTransitioning = ref(false); +export const subTabTransitioning = ref(false); + +const darkModePreference = window.matchMedia('(prefers-color-scheme: dark)'); +export const darkModeActive = ref(darkModePreference.matches); +darkModePreference.addEventListener('change', (e) => e.matches ? darkModeActive.value = true : darkModeActive.value = false); diff --git a/client/src/models/rest.ts b/client/src/models/rest.ts new file mode 100644 index 0000000..baefc94 --- /dev/null +++ b/client/src/models/rest.ts @@ -0,0 +1,30 @@ +import type {DynamicDataEnvelope} from "@models/TransferTypes.ts"; + +export const API_ROOT = (import.meta.env.VITE_API_ROOT ?? window.location.origin) + "/api"; + +async function rest(url: string, body?: unknown, method?: string, headers?: HeadersInit) { + const isFormData = body instanceof FormData; + const options: RequestInit = { + method: method ?? (body ? "POST" : "GET"), + headers: { + ...headers + }, + body: isFormData ? body : JSON.stringify(body) + }; + + if (!isFormData) { + options.headers = options.headers || {}; + (options.headers as Record)['Content-Type'] = 'application/json'; + } + + return await fetch(url, options) + .then(response => response.ok ? response.json() : response.json().then(err => Promise.reject(err))) + .catch(err => Promise.reject(err)); +} + +export async function api(action: string, body?: unknown, method?: string, headers?: HeadersInit) { + return rest(`${API_ROOT}${action}`, body, method, headers).then(data => { + if (data && typeof data === 'object' && 'data' in data) return data as DynamicDataEnvelope; + else throw new Error("Invalid response format"); + }) as Promise>; +} \ No newline at end of file diff --git a/client/src/models/session.ts b/client/src/models/session.ts new file mode 100644 index 0000000..5120fd9 --- /dev/null +++ b/client/src/models/session.ts @@ -0,0 +1,61 @@ +import {reactive} from "vue"; +import {useRouter} from "vue-router"; +import {toast} from "@models/toast.ts"; +import { type DataEnvelope } from "./TransferTypes"; +import {api} from "@models/rest.ts"; + +export interface SecureUser { + username: string; + role?: string; + id?: number; + token?: string; +} + +export const session = reactive({ + user: null as SecureUser | null, + token: null as string | null, + redirectURL: null as string | null, + messages: [] as { + type: string, + message: string + }[], +}); + +export function useLogin() { + const router = useRouter(); + return { + async login(username: string, password: string): Promise { + return await api("/users/login", {username, password}, "POST") + .then((response: DataEnvelope ) => { + if (!response.data) throw new Error("Invalid login credentials. Please try again."); + session.user = response.data; + if (!session.user) throw new Error("Invalid login credentials. Please try again."); + session.token = response.data.token || null; + router.push(session.redirectURL ?? "/").then((r) => r); + session.redirectURL = null; + toast.success("Welcome " + session.user.username + "!\nYou are now logged in."); + localStorage.setItem("username", session.user.username); + localStorage.setItem("token", session.token ?? ""); + return session.user; + }) + .catch((envelope: DataEnvelope)=>{ + toast.error(envelope.message || envelope.error?.message || "An error occurred while trying to log in. Please try again later.") + }) as SecureUser; + }, + async logout(): Promise { + session.user = null; + session.token = null; + localStorage.removeItem("token"); + localStorage.removeItem("username"); + for(let i = 0; i < session.messages.length; i++) { + console.debug("Messages: "); + if(session.messages[i].type === "error") { + console.error(session.messages[i].message); + } else { + console.debug(session.messages[i].message); + } + } + router.push('/').then((r) => r); + } + } +} \ No newline at end of file diff --git a/client/src/models/toast.ts b/client/src/models/toast.ts new file mode 100644 index 0000000..e86a241 --- /dev/null +++ b/client/src/models/toast.ts @@ -0,0 +1,8 @@ +import { useToast } from "vue-toast-notification"; +export const toast = useToast({ + position: 'top', + duration: 5000, + dismissible: true, + pauseOnHover: true, + type: 'default', +}) \ No newline at end of file diff --git a/client/src/router/index.ts b/client/src/router/index.ts new file mode 100644 index 0000000..004a4b8 --- /dev/null +++ b/client/src/router/index.ts @@ -0,0 +1,78 @@ +import {createRouter, createWebHashHistory} from 'vue-router' +import {session} from '@models/session.ts'; +import { toast } from '@models/toast.ts'; + +import {jwtDecode} from 'jwt-decode'; + +const routes = [ + + { + path: '/', + name: 'Main Page', + component: () => import('@views/MainPage.vue') + }, + { + path: '/about', + name: 'About', + component: () => import('@views/AboutPage.vue') + }, + { + path: '/contact', + name: 'Contact', + component: () => import('@views/ContactPage.vue') + }, + { + path: '/login', + name: 'Login', + component: () => import('@views/LoginPage.vue') + } +] + +const router = createRouter({ + history: createWebHashHistory(), + routes +}) + +router.beforeEach((to, from, next) => { + if (to.meta.requiresAuth) { + const token = session.token ?? localStorage.getItem('token'); + if (!token) { + session.redirectURL = to.fullPath; // Save the intended route + return next("Login"); + } + try { + const decoded: any = jwtDecode(token); + if( !decoded ) { + toast.error("Invalid token. Please log in again."); + session.token = null; + localStorage.removeItem('token'); + session.user = null; + localStorage.removeItem('username'); + return next("Login"); + } + if (decoded.exp * 1000 < Date.now()) { + toast.error("Token expired. Please log in again."); + session.token = null; + localStorage.removeItem('token'); + session.user = null; + localStorage.removeItem('username'); + return next("Login"); + } + if (decoded.role === 'admin') { + return next(); + } else if (decoded.role === 'user') { + toast.error("You do not have permission to access this page."); + return from.fullPath; + } + else { + return next({name: 'NotFound'}); + } + } catch (e) { + return next({name: 'NotFound'}); + } + } + next(); +}); + + +export default router \ No newline at end of file diff --git a/client/src/views/AboutPage.vue b/client/src/views/AboutPage.vue new file mode 100644 index 0000000..92819fc --- /dev/null +++ b/client/src/views/AboutPage.vue @@ -0,0 +1,117 @@ + + + + + \ No newline at end of file diff --git a/client/src/views/ContactPage.vue b/client/src/views/ContactPage.vue new file mode 100644 index 0000000..74aad98 --- /dev/null +++ b/client/src/views/ContactPage.vue @@ -0,0 +1,102 @@ + + +