diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 10d6ef9..384e775 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,6 +5,9 @@ on: branches: - master - develop + push: + branches: + - develop jobs: test-tauri: diff --git a/.vscode/settings.json b/.vscode/settings.json index a0966b1..511a1c7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,9 +13,6 @@ "[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" }, - "[svelte]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, "editor.codeActionsOnSave": { "quickfix.biome": "explicit", "source.organizeImports.biome": "explicit" @@ -24,5 +21,9 @@ "editor.defaultFormatter": "rust-lang.rust-analyzer", "editor.formatOnSave": true, "editor.inlayHints.enabled": "off" - } + }, + "cSpell.words": ["consts", "nvapi", "tauri"], + "tailwindCSS.experimental.classRegex": [ + "tv\\(([^)(]*(?:\\([^)(]*(?:\\([^)(]*(?:\\([^)(]*\\)[^)(]*)*\\)[^)(]*)*\\)[^)(]*)*)\\)" + ] } diff --git a/README.md b/README.md index 465e31e..393d59a 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,34 @@ -# create App +# hardware-monitor -## Require +## Dashboard + +![image](https://github.com/user-attachments/assets/9a2bf54f-d6e5-4c20-b0e4-f249fd5b8433) + +## Usage Graph + +![image](https://github.com/user-attachments/assets/b8fa7d67-a015-487f-aeb4-f43306d28f54) + + +## Development + +### Require - Node.js 20 - Rust -## Creating a project +### Creating a project ```bash npm ci ``` -## Developing +### Developing ```bash npm run tauri dev ``` -## Building +### Building ```bash npm run tauri build diff --git a/biome.json b/biome.json index 36e3fcc..83aad5d 100644 --- a/biome.json +++ b/biome.json @@ -1,12 +1,19 @@ -{ - "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json", - "organizeImports": { - "enabled": true - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - } - } -} +{ + "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "nursery": { + "useSortedClasses": "off" + } + } + }, + "formatter": { + "indentStyle": "space", + "indentWidth": 2 + } +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..31514bf --- /dev/null +++ b/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": false, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} diff --git a/package-lock.json b/package-lock.json index 32ec561..1a7b235 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,25 +9,36 @@ "version": "0.0.1", "dependencies": { "@phosphor-icons/react": "^2.1.7", + "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.0", "@tauri-apps/api": "^1", "chart.js": "^4.4.4", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", "jotai": "^2.9.3", + "lucide-react": "^0.437.0", "react": "^18.3.1", "react-chartjs-2": "^5.2.0", "react-dom": "^18.3.1", - "tailwind-variants": "^0.2.1" + "tailwind-merge": "^2.5.2", + "tailwind-variants": "^0.2.1", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "@biomejs/biome": "1.8.3", + "@biomejs/biome": "1.9.2", "@tauri-apps/cli": "^1", + "@types/node": "^22.5.1", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.20", - "postcss": "^8.4.41", + "postcss": "^8.4.42", "tailwindcss": "^3.4.10", "typescript": "^5.2.2", - "vite": "^5.3.1", + "vite": "^5.4.6", "vitest": "^2.0.5" } }, @@ -113,13 +124,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", - "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -233,14 +244,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -263,13 +274,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4" + "@babel/types": "^7.25.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -326,17 +337,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", - "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.4", - "@babel/parser": "^7.25.4", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -345,9 +356,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "license": "MIT", "dependencies": { @@ -360,9 +371,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.8.3.tgz", - "integrity": "sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.2.tgz", + "integrity": "sha512-4j2Gfwft8Jqp1X0qLYvK4TEy4xhTo4o6rlvJPsjPeEame8gsmbGQfOPBkw7ur+7/Z/f0HZmCZKqbMvR7vTXQYQ==", "dev": true, "hasInstallScript": true, "license": "MIT OR Apache-2.0", @@ -377,20 +388,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.8.3", - "@biomejs/cli-darwin-x64": "1.8.3", - "@biomejs/cli-linux-arm64": "1.8.3", - "@biomejs/cli-linux-arm64-musl": "1.8.3", - "@biomejs/cli-linux-x64": "1.8.3", - "@biomejs/cli-linux-x64-musl": "1.8.3", - "@biomejs/cli-win32-arm64": "1.8.3", - "@biomejs/cli-win32-x64": "1.8.3" + "@biomejs/cli-darwin-arm64": "1.9.2", + "@biomejs/cli-darwin-x64": "1.9.2", + "@biomejs/cli-linux-arm64": "1.9.2", + "@biomejs/cli-linux-arm64-musl": "1.9.2", + "@biomejs/cli-linux-x64": "1.9.2", + "@biomejs/cli-linux-x64-musl": "1.9.2", + "@biomejs/cli-win32-arm64": "1.9.2", + "@biomejs/cli-win32-x64": "1.9.2" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.8.3.tgz", - "integrity": "sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.2.tgz", + "integrity": "sha512-rbs9uJHFmhqB3Td0Ro+1wmeZOHhAPTL3WHr8NtaVczUmDhXkRDWScaxicG9+vhSLj1iLrW47itiK6xiIJy6vaA==", "cpu": [ "arm64" ], @@ -405,9 +416,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.8.3.tgz", - "integrity": "sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.2.tgz", + "integrity": "sha512-BlfULKijNaMigQ9GH9fqJVt+3JTDOSiZeWOQtG/1S1sa8Lp046JHG3wRJVOvekTPL9q/CNFW1NVG8J0JN+L1OA==", "cpu": [ "x64" ], @@ -422,9 +433,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.8.3.tgz", - "integrity": "sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.2.tgz", + "integrity": "sha512-T8TJuSxuBDeQCQzxZu2o3OU4eyLumTofhCxxFd3+aH2AEWVMnH7Z/c3QP1lHI5RRMBP9xIJeMORqDQ5j+gVZzw==", "cpu": [ "arm64" ], @@ -439,9 +450,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.8.3.tgz", - "integrity": "sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.2.tgz", + "integrity": "sha512-ZATvbUWhNxegSALUnCKWqetTZqrK72r2RsFD19OK5jXDj/7o1hzI1KzDNG78LloZxftrwr3uI9SqCLh06shSZw==", "cpu": [ "arm64" ], @@ -456,9 +467,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.8.3.tgz", - "integrity": "sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.2.tgz", + "integrity": "sha512-T0cPk3C3Jr2pVlsuQVTBqk2qPjTm8cYcTD9p/wmR9MeVqui1C/xTVfOIwd3miRODFMrJaVQ8MYSXnVIhV9jTjg==", "cpu": [ "x64" ], @@ -473,9 +484,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.8.3.tgz", - "integrity": "sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.2.tgz", + "integrity": "sha512-CjPM6jT1miV5pry9C7qv8YJk0FIZvZd86QRD3atvDgfgeh9WQU0k2Aoo0xUcPdTnoz0WNwRtDicHxwik63MmSg==", "cpu": [ "x64" ], @@ -490,9 +501,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.8.3.tgz", - "integrity": "sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.2.tgz", + "integrity": "sha512-2x7gSty75bNIeD23ZRPXyox6Z/V0M71ObeJtvQBhi1fgrvPdtkEuw7/0wEHg6buNCubzOFuN9WYJm6FKoUHfhg==", "cpu": [ "arm64" ], @@ -507,9 +518,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.8.3.tgz", - "integrity": "sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.2.tgz", + "integrity": "sha512-JC3XvdYcjmu1FmAehVwVV0SebLpeNTnO2ZaMdGCSOdS7f8O9Fq14T2P1gTG1Q29Q8Dt1S03hh0IdVpIZykOL8g==", "cpu": [ "x64" ], @@ -914,6 +925,44 @@ "node": ">=12" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", + "license": "MIT" + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1043,10 +1092,599 @@ "node": ">=14" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", + "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz", + "integrity": "sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-focus-guards": "1.1.0", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.7" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz", + "integrity": "sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz", + "integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", + "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", + "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", + "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz", + "integrity": "sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz", + "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.1.tgz", + "integrity": "sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-focus-guards": "1.1.0", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.7" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.0.tgz", + "integrity": "sha512-OBzy5WAj641k0AOSpKQtreDMe+isX0MQJ1IVyF03ucdF3DunOnROVrjWs8zsXUxC3zfZ6JL9HFVCUlMghz9dJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", + "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", - "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", "cpu": [ "arm" ], @@ -1058,9 +1696,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", - "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", "cpu": [ "arm64" ], @@ -1072,9 +1710,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", - "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", "cpu": [ "arm64" ], @@ -1086,9 +1724,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", - "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", "cpu": [ "x64" ], @@ -1100,9 +1738,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", - "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", "cpu": [ "arm" ], @@ -1114,9 +1752,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", - "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", "cpu": [ "arm" ], @@ -1128,9 +1766,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", - "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", "cpu": [ "arm64" ], @@ -1142,9 +1780,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", - "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", "cpu": [ "arm64" ], @@ -1156,9 +1794,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", - "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", "cpu": [ "ppc64" ], @@ -1170,9 +1808,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", - "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", "cpu": [ "riscv64" ], @@ -1184,9 +1822,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", - "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", "cpu": [ "s390x" ], @@ -1198,9 +1836,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", - "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", "cpu": [ "x64" ], @@ -1212,9 +1850,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", - "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", "cpu": [ "x64" ], @@ -1226,9 +1864,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", - "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", "cpu": [ "arm64" ], @@ -1240,9 +1878,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", - "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", "cpu": [ "ia32" ], @@ -1254,9 +1892,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", - "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", "cpu": [ "x64" ], @@ -1283,9 +1921,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-1.6.1.tgz", - "integrity": "sha512-2S8WGmkz54Z9WxpaFVbUYsTiwx5OIEmdD5DDWRygX9VhaWwZg0y6DctsUtCRVre9I/Un/hTnmqkhZqPamCEx8A==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-1.6.2.tgz", + "integrity": "sha512-zpfZdxhm20s7d/Uejpg/T3a9sqLVe3Ih2ztINfy8v6iLw9Ohowkb9g+agZffYKlEWfOSpmCy69NFyBLj7OZL0A==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -1299,22 +1937,22 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "1.6.1", - "@tauri-apps/cli-darwin-x64": "1.6.1", - "@tauri-apps/cli-linux-arm-gnueabihf": "1.6.1", - "@tauri-apps/cli-linux-arm64-gnu": "1.6.1", - "@tauri-apps/cli-linux-arm64-musl": "1.6.1", - "@tauri-apps/cli-linux-x64-gnu": "1.6.1", - "@tauri-apps/cli-linux-x64-musl": "1.6.1", - "@tauri-apps/cli-win32-arm64-msvc": "1.6.1", - "@tauri-apps/cli-win32-ia32-msvc": "1.6.1", - "@tauri-apps/cli-win32-x64-msvc": "1.6.1" + "@tauri-apps/cli-darwin-arm64": "1.6.2", + "@tauri-apps/cli-darwin-x64": "1.6.2", + "@tauri-apps/cli-linux-arm-gnueabihf": "1.6.2", + "@tauri-apps/cli-linux-arm64-gnu": "1.6.2", + "@tauri-apps/cli-linux-arm64-musl": "1.6.2", + "@tauri-apps/cli-linux-x64-gnu": "1.6.2", + "@tauri-apps/cli-linux-x64-musl": "1.6.2", + "@tauri-apps/cli-win32-arm64-msvc": "1.6.2", + "@tauri-apps/cli-win32-ia32-msvc": "1.6.2", + "@tauri-apps/cli-win32-x64-msvc": "1.6.2" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.6.1.tgz", - "integrity": "sha512-n+16Z9qQksBmY55Xwful8GGrw2dlyeqKPsjuNcwKUgVB5a4gIq6K6uUsbhwMUMUA3gqewQMBn44QXbSe5qNKfA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.6.2.tgz", + "integrity": "sha512-6mdRyf9DaLqlZvj8kZB09U3rwY+dOHSGzTZ7+GDg665GJb17f4cb30e8dExj6/aghcsOie9EGpgiURcDUvLNSQ==", "cpu": [ "arm64" ], @@ -1329,9 +1967,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.6.1.tgz", - "integrity": "sha512-OHzm6qiywv0GEwBDowlzLSuztKE85NMxp2loVynQ4vDoVk6V0jMtQy/N9YvYA0BetvfNTuuAiz3hsTkMHMYm+g==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.6.2.tgz", + "integrity": "sha512-PLxZY5dn38H3R9VRmBN/l0ZDB5JFanCwlK4rmpzDQPPg3tQmbu5vjSCP6TVj5U6aLKsj79kFyULblPr5Dn9+vw==", "cpu": [ "x64" ], @@ -1346,9 +1984,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.6.1.tgz", - "integrity": "sha512-ZA4ByaiZbrXUbhaoWUVab4lHI2yI1/ucrRO6b9pky6ytgqx37hA/YOWoctD0yaf5giQJQZw160euaBIUOKzRXA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.6.2.tgz", + "integrity": "sha512-xnpj4BLeeGOh5I/ewCQlYJZwHH0CBNBN+4q8BNWNQ9MKkjN9ST366RmHRzl2ANNgWwijOPxyce7GiUmvuH8Atw==", "cpu": [ "arm" ], @@ -1363,9 +2001,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.6.1.tgz", - "integrity": "sha512-VBU4GRJPU9jzzeqaEGLHAJzqQhpl7WnRFyHPR8Qby0D17av3CClJ7nBa+CI3ob3JbIERfJM9kwFHdY9eQpfxnw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.6.2.tgz", + "integrity": "sha512-uaiRE0vE2P+tdsCngfKt+7yKr3VZXIq/t3w01DzSdnBgHSp0zmRsRR4AhZt7ibvoEgA8GzBP+eSHJdFNZsTU9w==", "cpu": [ "arm64" ], @@ -1380,9 +2018,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.1.tgz", - "integrity": "sha512-gyMgNZ8fwQFYzrIiHwhmKECkbuAZtzsRyl+bi1Ua11XVWYVUpY8+cNp7Y5ilMJ9AwNFI/HFKjzzua9r+e9FNzw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.2.tgz", + "integrity": "sha512-o9JunVrMrhqTBLrdvEbS64W0bo1dPm0lxX51Mx+6x9SmbDjlEWGgaAHC3iKLK9khd5Yu1uO1e+8TJltAcScvmw==", "cpu": [ "arm64" ], @@ -1397,9 +2035,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.6.1.tgz", - "integrity": "sha512-aYLjLXEBcOf4GUrLBZRQcoLSL3KgCKHwfAyGmTilH4juAw42ZaAYWIZwa59hp2kC4w1XrlmwAzGpi1RESBr5Mw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.6.2.tgz", + "integrity": "sha512-jL9f+o61DdQmNYKIt2Q3BA8YJ+hyC5+GdNxqDf7j5SoQ85j//YfUWbmp9ZgsPHVBxgSGZVvgGMNvf64Ykp0buQ==", "cpu": [ "x64" ], @@ -1414,9 +2052,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.1.tgz", - "integrity": "sha512-j1M7ovICUrBDbrH8CNUwbMe0zk0/IAR7MXRv5PEanktAZ1w/LG3nlO/AhY5/Cbqqo3ziKTcMpe6x0j3aE8jYOA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.2.tgz", + "integrity": "sha512-xsa4Pu9YMHKAX0J8pIoXfN/uhvAAAoECZDixDhWw8zi57VZ4QX28ycqolS+NscdD9NAGSgHk45MpBZWdvRtvjQ==", "cpu": [ "x64" ], @@ -1431,9 +2069,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.6.1.tgz", - "integrity": "sha512-yCGT1jXHvZtu+yYPDmDOJDfgsj5EKdBPvya+kmN03BmLfOF+8EWHA9s6yXUdk9pSr6M9OQS0SXocbGDOu5AkMw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.6.2.tgz", + "integrity": "sha512-eJtUOx2UFhJpCCkm5M5+4Co9JbjvgIHTdyS/hTSZfOEdT58CNEGVJXMA39FsSZXYoxYPE+9K7Km6haMozSmlxw==", "cpu": [ "arm64" ], @@ -1448,9 +2086,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.6.1.tgz", - "integrity": "sha512-klAt+KNcczC4gxz9vm6tSvFB4iyXVj4r+TtDVhStLCKkAZOVm0ZsFym1kDzltxrB/3xSjgzsgIiEJydN2cP7xw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.6.2.tgz", + "integrity": "sha512-9Jwx3PrhNw3VKOgPISRRXPkvoEAZP+7rFRHXIo49dvlHy2E/o9qpWi1IntE33HWeazP6KhvsCjvXB2Ai4eGooA==", "cpu": [ "ia32" ], @@ -1465,9 +2103,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.6.1.tgz", - "integrity": "sha512-WEzQzBgcaqjZoA5M/KOupHmt8W3QQ20vwETXpGEMPd7spj4eZsRv/2ZDuCz4ELbai1XlIsTITFNe2LlJjzqISA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.6.2.tgz", + "integrity": "sha512-5Z+ZjRFJE8MXghJe1UXvGephY5ZcgVhiTI9yuMi9xgX3CEaAXASatyXllzsvGJ9EDaWMEpa0PHjAzi7LBAWROw==", "cpu": [ "x64" ], @@ -1533,17 +2171,27 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "22.5.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", + "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz", - "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==", + "version": "18.3.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.8.tgz", + "integrity": "sha512-syBUrW3/XpnW4WJ41Pft+I+aPoDVbrBVQGEnbD7NijDGlVC+8gV/XKRY+7vMDlfPpbwYt0l1vd/Sj8bJGMbs9Q==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1555,7 +2203,7 @@ "version": "18.3.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/react": "*" @@ -1582,14 +2230,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", - "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz", + "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -1597,10 +2245,38 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/mocker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz", + "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "^2.1.0-beta.1", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.11" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/spy": "2.1.1", + "msw": "^2.3.5", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, "node_modules/@vitest/pretty-format": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", - "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.1.tgz", + "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1611,13 +2287,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.5.tgz", - "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz", + "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.0.5", + "@vitest/utils": "2.1.1", "pathe": "^1.1.2" }, "funding": { @@ -1625,14 +2301,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.5.tgz", - "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz", + "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.0.5", - "magic-string": "^0.30.10", + "@vitest/pretty-format": "2.1.1", + "magic-string": "^0.30.11", "pathe": "^1.1.2" }, "funding": { @@ -1640,9 +2316,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", - "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz", + "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==", "dev": true, "license": "MIT", "dependencies": { @@ -1653,14 +2329,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", - "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz", + "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.0.5", - "estree-walker": "^3.0.3", + "@vitest/pretty-format": "2.1.1", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -1669,9 +2344,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "license": "MIT", "engines": { "node": ">=12" @@ -1718,6 +2393,18 @@ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "license": "MIT" }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -1858,9 +2545,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001651", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", - "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "version": "1.0.30001662", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz", + "integrity": "sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==", "dev": true, "funding": [ { @@ -1968,6 +2655,36 @@ "node": ">= 6" } }, + "node_modules/class-variance-authority": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", + "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "2.0.0" + }, + "funding": { + "url": "https://joebell.co.uk" + } + }, + "node_modules/class-variance-authority/node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2035,13 +2752,13 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2062,6 +2779,12 @@ "node": ">=6" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2081,9 +2804,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", + "version": "1.5.27", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz", + "integrity": "sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==", "dev": true, "license": "ISC" }, @@ -2133,9 +2856,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -2162,30 +2885,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -2308,17 +3007,13 @@ "node": "*" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "license": "MIT", "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/glob": { @@ -2385,14 +3080,13 @@ "node": ">= 0.4" } }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" } }, "node_modules/is-binary-path": { @@ -2461,19 +3155,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2505,9 +3186,9 @@ } }, "node_modules/jotai": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.9.3.tgz", - "integrity": "sha512-IqMWKoXuEzWSShjd9UhalNsRGbdju5G2FrqNLQJT+Ih6p41VNYe2sav5hnwQx4HJr25jq9wRqvGSWGviGG6Gjw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.10.0.tgz", + "integrity": "sha512-8W4u0aRlOIwGlLQ0sqfl/c6+eExl5D8lZgAUolirZLktyaj4WnxO/8a0HEPmtriQAB6X5LMhXzZVmw02X0P0qQ==", "license": "MIT", "engines": { "node": ">=12.20.0" @@ -2604,6 +3285,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.437.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.437.0.tgz", + "integrity": "sha512-RXQq6tnm1FlXDUtOwLaoXET2TOEGpQULrQlPOjGHgIVsPhicHNat9sWF33OAe2UCLMFiWF1oL+FtAg43BqVY4Q==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, "node_modules/magic-string": { "version": "0.30.11", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", @@ -2614,13 +3304,6 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2643,19 +3326,6 @@ "node": ">=8.6" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -2681,9 +3351,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, @@ -2742,35 +3412,6 @@ "node": ">=0.10.0" } }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2789,22 +3430,6 @@ "node": ">= 6" } }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", @@ -2866,9 +3491,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "license": "ISC" }, "node_modules/picomatch": { @@ -2902,9 +3527,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -2922,8 +3547,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -3121,6 +3746,76 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", + "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.4", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -3170,9 +3865,9 @@ } }, "node_modules/rollup": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", - "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "dev": true, "license": "MIT", "dependencies": { @@ -3186,22 +3881,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.0", - "@rollup/rollup-android-arm64": "4.21.0", - "@rollup/rollup-darwin-arm64": "4.21.0", - "@rollup/rollup-darwin-x64": "4.21.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.0", - "@rollup/rollup-linux-arm-musleabihf": "4.21.0", - "@rollup/rollup-linux-arm64-gnu": "4.21.0", - "@rollup/rollup-linux-arm64-musl": "4.21.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", - "@rollup/rollup-linux-riscv64-gnu": "4.21.0", - "@rollup/rollup-linux-s390x-gnu": "4.21.0", - "@rollup/rollup-linux-x64-gnu": "4.21.0", - "@rollup/rollup-linux-x64-musl": "4.21.0", - "@rollup/rollup-win32-arm64-msvc": "4.21.0", - "@rollup/rollup-win32-ia32-msvc": "4.21.0", - "@rollup/rollup-win32-x64-msvc": "4.21.0", + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", "fsevents": "~2.3.2" } }, @@ -3288,9 +3983,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "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" @@ -3406,19 +4101,6 @@ "node": ">=8" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -3493,9 +4175,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", - "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.12.tgz", + "integrity": "sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -3529,6 +4211,15 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -3557,6 +4248,13 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinypool": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", @@ -3578,9 +4276,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", - "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "license": "MIT", "engines": { @@ -3615,10 +4313,16 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "license": "Apache-2.0" }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3629,6 +4333,13 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -3660,6 +4371,49 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3667,14 +4421,14 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", - "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "version": "5.4.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz", + "integrity": "sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.41", + "postcss": "^8.4.43", "rollup": "^4.20.0" }, "bin": { @@ -3727,16 +4481,15 @@ } }, "node_modules/vite-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.5.tgz", - "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz", + "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.5", + "debug": "^4.3.6", "pathe": "^1.1.2", - "tinyrainbow": "^1.2.0", "vite": "^5.0.0" }, "bin": { @@ -3750,30 +4503,30 @@ } }, "node_modules/vitest": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.5.tgz", - "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz", + "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@vitest/expect": "2.0.5", - "@vitest/pretty-format": "^2.0.5", - "@vitest/runner": "2.0.5", - "@vitest/snapshot": "2.0.5", - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/expect": "2.1.1", + "@vitest/mocker": "2.1.1", + "@vitest/pretty-format": "^2.1.1", + "@vitest/runner": "2.1.1", + "@vitest/snapshot": "2.1.1", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", - "debug": "^4.3.5", - "execa": "^8.0.1", - "magic-string": "^0.30.10", + "debug": "^4.3.6", + "magic-string": "^0.30.11", "pathe": "^1.1.2", "std-env": "^3.7.0", - "tinybench": "^2.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.0", "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.0.5", + "vite-node": "2.1.1", "why-is-node-running": "^2.3.0" }, "bin": { @@ -3788,8 +4541,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.0.5", - "@vitest/ui": "2.0.5", + "@vitest/browser": "2.1.1", + "@vitest/ui": "2.1.1", "happy-dom": "*", "jsdom": "*" }, @@ -3975,9 +4728,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index b80456d..6445785 100644 --- a/package.json +++ b/package.json @@ -14,26 +14,37 @@ "lint:ci": "biome ci ./src" }, "devDependencies": { - "@biomejs/biome": "1.8.3", + "@biomejs/biome": "1.9.2", "@tauri-apps/cli": "^1", + "@types/node": "^22.5.1", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.20", - "postcss": "^8.4.41", + "postcss": "^8.4.42", "tailwindcss": "^3.4.10", "typescript": "^5.2.2", - "vite": "^5.3.1", + "vite": "^5.4.6", "vitest": "^2.0.5" }, "dependencies": { "@phosphor-icons/react": "^2.1.7", + "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.0", "@tauri-apps/api": "^1", "chart.js": "^4.4.4", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", "jotai": "^2.9.3", + "lucide-react": "^0.437.0", "react": "^18.3.1", "react-chartjs-2": "^5.2.0", "react-dom": "^18.3.1", - "tailwind-variants": "^0.2.1" + "tailwind-merge": "^2.5.2", + "tailwind-variants": "^0.2.1", + "tailwindcss-animate": "^1.0.7" } } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 391e700..a36f32f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -23,6 +23,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -74,6 +85,7 @@ version = "0.1.0" dependencies = [ "chrono", "nvapi", + "rust_decimal", "serde", "serde_json", "sysinfo", @@ -84,8 +96,15 @@ dependencies = [ "tracing", "tracing-subscriber", "windows 0.58.0", + "wmi", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "atk" version = "0.15.1" @@ -170,6 +189,18 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block" version = "0.1.6" @@ -185,6 +216,30 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.76", + "syn_derive", +] + [[package]] name = "brotli" version = "6.0.0" @@ -222,6 +277,28 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.17.0" @@ -325,6 +402,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -784,6 +867,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futf" version = "0.1.5" @@ -794,6 +883,21 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -801,6 +905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -837,6 +942,12 @@ dependencies = [ "syn 2.0.76", ] +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + [[package]] name = "futures-task" version = "0.3.30" @@ -849,9 +960,13 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -1061,7 +1176,7 @@ checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" dependencies = [ "anyhow", "heck 0.4.1", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", @@ -1156,7 +1271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" dependencies = [ "anyhow", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", @@ -1168,6 +1283,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" @@ -1695,7 +1813,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -1736,6 +1854,17 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -2038,6 +2167,15 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit 0.22.20", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2077,6 +2215,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quick-xml" version = "0.32.0" @@ -2095,6 +2253,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.7.3" @@ -2266,6 +2430,84 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rfd" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" +dependencies = [ + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "lazy_static", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.37.0", +] + +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2327,6 +2569,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "selectors" version = "0.22.0" @@ -2512,6 +2760,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "0.3.11" @@ -2640,11 +2894,23 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.76", +] + [[package]] name = "sysinfo" -version = "0.31.3" +version = "0.31.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b92e0bdf838cbc1c4c9ba14f9c97a7ec6cdcd1ae66b10e1e42775a25553f45d" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" dependencies = [ "core-foundation-sys", "libc", @@ -2738,6 +3004,12 @@ dependencies = [ "syn 2.0.76", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tar" version = "0.4.41" @@ -2757,9 +3029,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e33e3ba00a3b05eb6c57ef135781717d33728b48acf914bb05629e74d897d29" +checksum = "570a20223602ad990a30a048f2fdb957ae3e38de3ca9582e04cc09d01e8ccfad" dependencies = [ "anyhow", "cocoa", @@ -2776,11 +3048,14 @@ dependencies = [ "heck 0.5.0", "http", "ignore", + "log", "objc", "once_cell", "percent-encoding", + "plist", "rand 0.8.5", "raw-window-handle", + "rfd", "semver", "serde", "serde_json", @@ -2804,9 +3079,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fb5a90a64241ddb7217d3210d844149070a911e87e8a107a707a1d4973f164" +checksum = "586f3e677f940c8bb4f70c52eda05dc59b79e61543f1182de83516810bb8e35d" dependencies = [ "anyhow", "cargo_toml", @@ -3063,9 +3338,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -3377,6 +3652,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.93" @@ -3406,6 +3693,16 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webkit2gtk" version = "0.18.2" @@ -3522,6 +3819,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +dependencies = [ + "windows_aarch64_msvc 0.37.0", + "windows_i686_gnu 0.37.0", + "windows_i686_msvc 0.37.0", + "windows_x86_64_gnu 0.37.0", + "windows_x86_64_msvc 0.37.0", +] + [[package]] name = "windows" version = "0.39.0" @@ -3782,6 +4092,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" + [[package]] name = "windows_aarch64_msvc" version = "0.39.0" @@ -3800,6 +4116,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" + [[package]] name = "windows_i686_gnu" version = "0.39.0" @@ -3824,6 +4146,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" + [[package]] name = "windows_i686_msvc" version = "0.39.0" @@ -3842,6 +4170,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" + [[package]] name = "windows_x86_64_gnu" version = "0.39.0" @@ -3872,6 +4206,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" + [[package]] name = "windows_x86_64_msvc" version = "0.39.0" @@ -3918,6 +4258,21 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wmi" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdda506bdee26ba617bd814538b690e14f59e8185345344cff113a8be21c005" +dependencies = [ + "chrono", + "futures", + "log", + "serde", + "thiserror", + "windows 0.58.0", + "windows-core 0.58.0", +] + [[package]] name = "wry" version = "0.24.10" @@ -3956,6 +4311,15 @@ dependencies = [ "windows-implement 0.39.0", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x11" version = "2.21.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 13813b3..f5c3a3b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -24,19 +24,21 @@ rust-version = "1.60" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "1.5.2", features = [] } +tauri-build = { version = "1.5.5", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.6.5", features = [] } -sysinfo = "0.31.3" +tauri = { version = "1.8.0", features = ["dialog-all"] } +sysinfo = "0.31.4" tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } nvapi = "=0.1.4" -tokio = { version = "1", features = ["full"] } +tokio = { version = "1.40.0", features = ["full"] } tracing = "0.1" tracing-subscriber = { version = "0.3", default-features = true, features = ["env-filter"] } chrono = "0.4" +wmi = "0.14" +rust_decimal = "1.23.0" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/commands/config.rs b/src-tauri/src/commands/config.rs index 5cd00e3..3e8c6d3 100644 --- a/src-tauri/src/commands/config.rs +++ b/src-tauri/src/commands/config.rs @@ -10,8 +10,8 @@ use std::sync::Mutex; const SETTINGS_FILENAME: &str = "settings.json"; trait Config { - fn write_file(&self) {} - fn read_file(&mut self) {} + fn write_file(&self) -> Result<(), String>; + fn read_file(&mut self) -> Result<(), String>; } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -26,52 +26,112 @@ impl Default for Settings { Self { language: "en".to_string(), theme: "dark".to_string(), - display_targets: vec![hardware::HardwareType::CPU, hardware::HardwareType::Memory], + display_targets: vec![ + hardware::HardwareType::CPU, + hardware::HardwareType::Memory, + hardware::HardwareType::GPU, + ], } } } impl Config for Settings { - fn write_file(&self) { + fn write_file(&self) -> Result<(), String> { let config_file = get_app_data_dir(SETTINGS_FILENAME); if !config_file.parent().unwrap().exists() { fs::create_dir_all(config_file.parent().unwrap()).unwrap(); } - let serialized = serde_json::to_string(self).unwrap(); - let mut file = fs::File::create(config_file).unwrap(); - file.write_all(&serialized.as_bytes()).unwrap(); + + match serde_json::to_string(self) { + Ok(serialized) => { + if let Err(e) = fs::File::create(config_file) + .and_then(|mut file| file.write_all(&serialized.as_bytes())) + { + // [TODO] ログの定数化 + log_error!( + "Failed to serialize settings", + "write_file", + Some(e.to_string()) + ); + return Err(format!("Failed to write to settings file: {}", e)); + } + } + Err(e) => { + log_error!( + "Failed to serialize settings", + "write_file", + Some(e.to_string()) + ); + return Err(format!("Failed to serialize settings: {}", e)); + } + } + + Ok(()) } - fn read_file(&mut self) { + fn read_file(&mut self) -> Result<(), String> { let config_file = get_app_data_dir(SETTINGS_FILENAME); - let input = fs::read_to_string(config_file).unwrap(); - let deserialized: Self = serde_json::from_str(&input).unwrap(); - let _ = mem::replace(self, deserialized); + + match fs::read_to_string(config_file) { + Ok(input) => match serde_json::from_str::(&input) { + Ok(deserialized) => { + *self = deserialized; + Ok(()) + } + Err(e) => { + log_error!( + "Failed to deserialize settings", + "read_file", + Some(e.to_string()) + ); + Err(format!("Failed to deserialize settings: {}", e)) + } + }, + Err(e) => { + log_error!( + "Failed to deserialize settings", + "read_file", + Some(e.to_string()) + ); + Err(format!("Failed to read settings file: {}", e)) + } + } } } impl Settings { pub fn new() -> Self { let config_file = get_app_data_dir(SETTINGS_FILENAME); + + let mut settings = Self::default(); + if !config_file.exists() { - Self::default() - } else { - let mut settings = Self::default(); - settings.read_file(); - settings + return settings; } + + if let Err(e) = settings.read_file() { + log_error!("read_config_failed", "read_file", Some(e.to_string())); + } + + settings } - pub fn set_language(&mut self, new_lang: String) { + pub fn set_language(&mut self, new_lang: String) -> Result<(), String> { self.language = new_lang; - self.write_file(); - println!("{:?}", self); + self.write_file() } - pub fn set_theme(&mut self, new_theme: String) { + pub fn set_theme(&mut self, new_theme: String) -> Result<(), String> { self.theme = new_theme; - self.write_file(); - println!("{:?}", self); + self.write_file() + } + + pub fn set_display_targets( + &mut self, + new_targets: Vec, + ) -> Result<(), String> { + self.display_targets = new_targets; + self.write_file() } } @@ -90,32 +150,79 @@ impl AppState { pub mod commands { use super::*; + use serde_json::json; + use tauri::Window; + + const ERROR_TITLE: &str = "設定の更新に失敗しました"; + const ERROR_MESSAGE: &str = "何度も発生する場合は settings.json を削除してください"; + + fn emit_error(window: &Window) -> Result<(), String> { + window + .emit( + "error_event", + json!({ + "title": ERROR_TITLE, + "message": ERROR_MESSAGE + }), + ) + .map_err(|e| format!("Failed to emit event: {}", e))?; + + Ok(()) + } #[tauri::command] + pub async fn get_settings( + state: tauri::State<'_, AppState>, + ) -> Result { + let settings = state.settings.lock().unwrap().clone(); + Ok(settings) + } + + #[tauri::command] + pub async fn set_language( + window: Window, state: tauri::State<'_, AppState>, new_language: String, ) -> Result<(), String> { let mut settings = state.settings.lock().unwrap(); - settings.set_language(new_language); + + if let Err(e) = settings.set_language(new_language) { + emit_error(&window)?; + return Err(e); + } + Ok(()) } #[tauri::command] pub async fn set_theme( + window: Window, state: tauri::State<'_, AppState>, new_theme: String, ) -> Result<(), String> { let mut settings = state.settings.lock().unwrap(); - settings.set_theme(new_theme); + + if let Err(e) = settings.set_theme(new_theme) { + emit_error(&window)?; + return Err(e); + } + Ok(()) } #[tauri::command] - pub async fn get_settings( + pub async fn set_display_targets( + window: Window, state: tauri::State<'_, AppState>, - ) -> Result { - let settings = state.settings.lock().unwrap().clone(); - Ok(settings) + new_targets: Vec, + ) -> Result<(), String> { + let mut settings = state.settings.lock().unwrap(); + + if let Err(e) = settings.set_display_targets(new_targets) { + emit_error(&window)?; + return Err(e); + } + Ok(()) } } diff --git a/src-tauri/src/commands/hardware.rs b/src-tauri/src/commands/hardware.rs index 0de30be..7fc6e08 100644 --- a/src-tauri/src/commands/hardware.rs +++ b/src-tauri/src/commands/hardware.rs @@ -1,11 +1,13 @@ -use crate::{log_debug, log_error, log_internal, log_warn}; -use nvapi; -use nvapi::UtilizationDomain; +use crate::services::graphic_service; +use crate::services::system_info_service; +use crate::{log_debug, log_error, log_info, log_internal, log_warn}; +use serde::{Serialize, Serializer}; +use std::collections::HashMap; use std::collections::VecDeque; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; -use sysinfo::System; +use sysinfo::{Pid, ProcessesToUpdate, System}; use tauri::command; pub struct AppState { @@ -14,6 +16,8 @@ pub struct AppState { pub memory_history: Arc>>, pub gpu_history: Arc>>, pub gpu_usage: Arc>, + pub process_cpu_histories: Arc>>>, + pub process_memory_histories: Arc>>>, } /// @@ -26,6 +30,77 @@ const SYSTEM_INFO_INIT_INTERVAL: u64 = 1; /// const HISTORY_CAPACITY: usize = 60; +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProcessInfo { + pub pid: i32, + pub name: String, + #[serde(serialize_with = "serialize_usage")] + pub cpu_usage: f32, + #[serde(serialize_with = "serialize_usage")] + pub memory_usage: f32, +} + +fn serialize_usage(x: &f32, s: S) -> Result +where + S: Serializer, +{ + if x.fract() == 0.0 { + s.serialize_str(&format!("{:.0}", x)) // 整数のみ + } else { + s.serialize_str(&format!("{:.1}", x)) // 小数点以下1桁まで + } +} + +/// +/// ## プロセスリストを取得 +/// +#[command] +pub fn get_process_list(state: tauri::State<'_, AppState>) -> Vec { + let mut system = state.system.lock().unwrap(); + let process_cpu_histories = state.process_cpu_histories.lock().unwrap(); + let process_memory_histories = state.process_memory_histories.lock().unwrap(); + + system.refresh_processes(ProcessesToUpdate::All); + + system + .processes() + .values() + .map(|process| { + let pid = process.pid(); + + // 5秒間のCPU使用率の平均を計算 + let cpu_usage = if let Some(history) = process_cpu_histories.get(&pid) { + let len = history.len().min(5); // 最大5秒分のデータ + let sum: f32 = history.iter().rev().take(len).sum(); + let avg = sum / len as f32; + + (avg * 10.0).round() / 10.0 + } else { + 0.0 // 履歴がなければ0を返す + }; + + // 5秒間のメモリ使用率の平均を計算 + let memory_usage = if let Some(history) = process_memory_histories.get(&pid) { + let len = history.len().min(5); // 最大5秒分のデータ + let sum: f32 = history.iter().rev().take(len).sum(); + let avg = sum / len as f32; + + (avg * 10.0).round() / 10.0 + } else { + process.memory() as f32 / 1024.0 // 履歴がなければ現在のメモリ使用量を返す + }; + + ProcessInfo { + pid: pid.as_u32() as i32, // プロセスID + name: process.name().to_string_lossy().into_owned(), // プロセス名を取得 + cpu_usage, // 平均CPU使用率 + memory_usage, // 平均メモリ使用率 + } + }) + .collect() +} + /// /// ## CPU使用率(%)を取得 /// @@ -42,6 +117,39 @@ pub fn get_cpu_usage(state: tauri::State<'_, AppState>) -> i32 { usage.round() as i32 } +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SysInfo { + pub cpu: Option, + pub memory: Option, + pub gpus: Option>, +} + +/// +/// ## システム情報を取得 +/// +#[command] +pub async fn get_hardware_info( + state: tauri::State<'_, AppState>, +) -> Result { + let cpu_result = system_info_service::get_cpu_info(state.system.lock().unwrap()); + let memory_result = system_info_service::get_memory_info(); + let gpus_result = graphic_service::get_nvidia_gpu_info().await; + + let sys_info = SysInfo { + cpu: cpu_result.ok(), + memory: memory_result.ok(), + gpus: gpus_result.ok(), + }; + + // すべての情報が失敗した場合にのみエラーメッセージを返す + if sys_info.cpu.is_none() && sys_info.memory.is_none() && sys_info.gpus.is_none() { + Err("Failed to get any hardware info".to_string()) + } else { + Ok(sys_info) + } +} + /// /// ## メモリ使用率(%)を取得 /// @@ -64,9 +172,33 @@ pub fn get_memory_usage(state: tauri::State<'_, AppState>) -> i32 { /// - return: `i32` GPU使用率(%) /// #[command] -pub fn get_gpu_usage(state: tauri::State<'_, AppState>) -> i32 { - let gpu_usage = state.gpu_usage.lock().unwrap(); - *gpu_usage as i32 +pub async fn get_gpu_usage() -> Result { + match graphic_service::get_nvidia_gpu_usage().await { + Ok(usage) => Ok((usage * 100.0).round() as i32), + Err(e) => Err(format!("Failed to get GPU usage: {:?}", e)), + } +} + +/// +/// ## GPU温度を取得 +/// +#[command] +pub async fn get_gpu_temperature() -> Result, String> { + match graphic_service::get_nvidia_gpu_temperature().await { + Ok(temps) => Ok(temps), + Err(e) => Err(format!("Failed to get GPU temperature: {:?}", e)), + } +} + +/// +/// ## GPUのファン回転数を取得 +/// +#[command] +pub async fn get_nvidia_gpu_cooler() -> Result, String> { + match graphic_service::get_nvidia_gpu_cooler_stat().await { + Ok(temps) => Ok(temps), + Err(e) => Err(format!("Failed to get GPU cooler status: {:?}", e)), + } } /// @@ -127,6 +259,8 @@ pub fn initialize_system( memory_history: Arc>>, gpu_usage: Arc>, gpu_history: Arc>>, + process_cpu_histories: Arc>>>, + process_memory_histories: Arc>>>, ) { thread::spawn(move || loop { { @@ -135,8 +269,7 @@ pub fn initialize_system( Err(_) => continue, // エラーハンドリング:ロックが破損している場合はスキップ }; - sys.refresh_cpu_all(); - sys.refresh_memory(); + sys.refresh_all(); let cpu_usage = { let cpus = sys.cpus(); @@ -150,16 +283,6 @@ pub fn initialize_system( (used_memory / total_memory * 100.0).round() as f32 }; - //let gpu_usage_value = match get_gpu_usage() { - // Ok(usage) => usage, - // Err(_) => 0.0, // エラーが発生した場合はデフォルト値として0.0を使用 - //}; - - //{ - // let mut gpu = gpu_usage.lock().unwrap(); - // *gpu = gpu_usage_value; - //} - { let mut cpu_hist = cpu_history.lock().unwrap(); if cpu_hist.len() >= HISTORY_CAPACITY { @@ -176,67 +299,34 @@ pub fn initialize_system( memory_hist.push_back(memory_usage); } - //{ - // let mut gpu_hist = gpu_history.lock().unwrap(); - // if gpu_hist.len() >= HISTORY_CAPACITY { - // gpu_hist.pop_front(); - // } - // gpu_hist.push_back(gpu_usage_value); - //} - } - - thread::sleep(Duration::from_secs(SYSTEM_INFO_INIT_INTERVAL)); - }); - - /// - /// TODO GPU使用率を取得する - /// - #[allow(dead_code)] - fn get_gpu_usage() -> Result { - log_debug!("start", "get_gpu_usage", None::<&str>); - - let gpus = nvapi::PhysicalGpu::enumerate()?; - - print!("{:?}", gpus); - - if gpus.is_empty() { - log_warn!("not found", "get_gpu_usage", Some("gpu is not found")); - tracing::warn!("gpu is not found"); - return Err(nvapi::Status::Error); // GPUが見つからない場合はエラーを返す - } - - let mut total_usage = 0.0; - let mut gpu_count = 0; - - for gpu in gpus.iter() { - let usage = match gpu.usages() { - Ok(usage) => usage, - Err(e) => { - log_error!("usages_failed", "get_gpu_usage", Some(e.to_string())); - return Err(e); + // 各プロセスごとのCPUおよびメモリ使用率を保存 + { + let mut process_cpu_histories = process_cpu_histories.lock().unwrap(); + let mut process_memory_histories = process_memory_histories.lock().unwrap(); + + for (pid, process) in sys.processes() { + // CPU使用率の履歴を更新 + let cpu_usage = process.cpu_usage() as f32; + let cpu_history = process_cpu_histories.entry(*pid).or_insert(VecDeque::new()); + + if cpu_history.len() >= HISTORY_CAPACITY { + cpu_history.pop_front(); + } + cpu_history.push_back(cpu_usage); + + // メモリ使用率の履歴を更新 + let memory_usage = process.memory() as f32 / 1024.0; // KB単位からMB単位に変換 + let memory_history = + process_memory_histories.entry(*pid).or_insert(VecDeque::new()); + + if memory_history.len() >= HISTORY_CAPACITY { + memory_history.pop_front(); + } + memory_history.push_back(memory_usage); } - }; - - if let Some(gpu_usage) = usage.get(&UtilizationDomain::Graphics) { - let usage_f32 = gpu_usage.0 as f32 / 100.0; // Percentage を f32 に変換 - total_usage += usage_f32; - gpu_count += 1; } } - if gpu_count == 0 { - log_warn!( - "no_usage", - "get_gpu_usage", - Some("No GPU usage data collected") - ); - return Err(nvapi::Status::Error); // 使用率が取得できなかった場合のエラーハンドリング - } - - let average_usage = total_usage / gpu_count as f32; - - log_debug!("end", "get_gpu_usage", None::<&str>); - - Ok(average_usage) - } + thread::sleep(Duration::from_secs(SYSTEM_INFO_INIT_INTERVAL)); + }); } diff --git a/src-tauri/src/enums/hardware.rs b/src-tauri/src/enums/hardware.rs index 2fb1c34..3b76c97 100644 --- a/src-tauri/src/enums/hardware.rs +++ b/src-tauri/src/enums/hardware.rs @@ -1,6 +1,6 @@ -use serde::{Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum HardwareType { CPU, Memory, @@ -20,3 +20,21 @@ impl Serialize for HardwareType { serializer.serialize_str(s) } } + +impl<'de> Deserialize<'de> for HardwareType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?.to_lowercase(); + match s.as_str() { + "cpu" => Ok(HardwareType::CPU), + "memory" => Ok(HardwareType::Memory), + "gpu" => Ok(HardwareType::GPU), + _ => Err(serde::de::Error::unknown_variant( + &s, + &["cpu", "memory", "gpu"], + )), + } + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a646829..c375f2c 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,60 +1,79 @@ -// Prevents additional console window on Windows in release, DO NOT REMOVE!! -#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -#[macro_use] - -mod commands; -mod enums; -mod utils; - -use commands::config; -use commands::hardware; - -use std::collections::VecDeque; -use std::sync::{Arc, Mutex}; -use sysinfo::System; - -fn main() { - utils::logger::init(); - - let app_state = config::AppState::new(); - - let system = Arc::new(Mutex::new(System::new_all())); - let cpu_history = Arc::new(Mutex::new(VecDeque::with_capacity(60))); - let memory_history = Arc::new(Mutex::new(VecDeque::with_capacity(60))); - let gpu_usage = Arc::new(Mutex::new(0.0)); - let gpu_history = Arc::new(Mutex::new(VecDeque::with_capacity(60))); - - let state = hardware::AppState { - system: Arc::clone(&system), - cpu_history: Arc::clone(&cpu_history), - memory_history: Arc::clone(&memory_history), - gpu_usage: Arc::clone(&gpu_usage), - gpu_history: Arc::clone(&gpu_history), - }; - - hardware::initialize_system( - system, - cpu_history, - memory_history, - gpu_usage, - gpu_history, - ); - - tauri::Builder::default() - .plugin(tauri_plugin_window_state::Builder::default().build()) - .manage(state) - .manage(app_state) - .invoke_handler(tauri::generate_handler![ - hardware::get_cpu_usage, - hardware::get_memory_usage, - hardware::get_gpu_usage, - hardware::get_cpu_usage_history, - hardware::get_memory_usage_history, - hardware::get_gpu_usage_history, - config::commands::set_language, - config::commands::set_theme, - config::commands::get_settings - ]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +#[macro_use] + +mod commands; +mod enums; +mod services; +mod utils; + +use commands::config; +use commands::hardware; +use services::window_menu_service; + +use std::collections::{HashMap, VecDeque}; +use std::sync::{Arc, Mutex}; +use sysinfo::System; + +fn main() { + utils::logger::init(); + + let app_state = config::AppState::new(); + + let system = Arc::new(Mutex::new(System::new_all())); + let cpu_history = Arc::new(Mutex::new(VecDeque::with_capacity(60))); + let memory_history = Arc::new(Mutex::new(VecDeque::with_capacity(60))); + let gpu_usage = Arc::new(Mutex::new(0.0)); + let gpu_history = Arc::new(Mutex::new(VecDeque::with_capacity(60))); + let process_cpu_histories = Arc::new(Mutex::new(HashMap::new())); + let process_memory_histories = Arc::new(Mutex::new(HashMap::new())); + + let state = hardware::AppState { + system: Arc::clone(&system), + cpu_history: Arc::clone(&cpu_history), + memory_history: Arc::clone(&memory_history), + gpu_usage: Arc::clone(&gpu_usage), + gpu_history: Arc::clone(&gpu_history), + process_cpu_histories: Arc::clone(&process_cpu_histories), + process_memory_histories: Arc::clone(&process_memory_histories), + }; + + let menu = window_menu_service::create_setting(); + + hardware::initialize_system( + system, + cpu_history, + memory_history, + gpu_usage, + gpu_history, + process_cpu_histories, + process_memory_histories, + ); + + tauri::Builder::default() + .plugin(tauri_plugin_window_state::Builder::default().build()) + .manage(state) + .manage(app_state) + .menu(menu) + .invoke_handler(tauri::generate_handler![ + hardware::get_process_list, + hardware::get_cpu_usage, + hardware::get_hardware_info, + hardware::get_memory_usage, + hardware::get_gpu_usage, + hardware::get_gpu_temperature, + hardware::get_nvidia_gpu_cooler, + hardware::get_cpu_usage_history, + hardware::get_memory_usage_history, + hardware::get_gpu_usage_history, + config::commands::set_language, + config::commands::set_theme, + config::commands::set_display_targets, + config::commands::get_settings + ]) + .on_menu_event(|event| { + window_menu_service::handle_menu_event(event); + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/services/graphic_service.rs b/src-tauri/src/services/graphic_service.rs new file mode 100644 index 0000000..4a18b1d --- /dev/null +++ b/src-tauri/src/services/graphic_service.rs @@ -0,0 +1,290 @@ +use crate::utils::{self, formatter}; +use crate::{log_debug, log_error, log_info, log_internal, log_warn}; +use nvapi; +use nvapi::UtilizationDomain; +use serde::Serialize; +use tokio::task::spawn_blocking; +use tokio::task::JoinError; + +/// +/// GPU使用率を取得する(NVAPI を使用) +/// +pub async fn get_nvidia_gpu_usage() -> Result { + let handle = spawn_blocking(|| { + log_debug!("start", "get_nvidia_gpu_usage", None::<&str>); + + let gpus = nvapi::PhysicalGpu::enumerate()?; + + if gpus.is_empty() { + log_warn!( + "not found", + "get_nvidia_gpu_usage", + Some("gpu is not found") + ); + tracing::warn!("gpu is not found"); + return Err(nvapi::Status::Error); // GPUが見つからない場合はエラーを返す + } + + let mut total_usage = 0.0; + let mut gpu_count = 0; + + for gpu in gpus.iter() { + let usage = match gpu.usages() { + Ok(usage) => usage, + Err(e) => { + log_error!("usages_failed", "get_nvidia_gpu_usage", Some(e.to_string())); + return Err(e); + } + }; + + if let Some(gpu_usage) = usage.get(&UtilizationDomain::Graphics) { + let usage_f32 = gpu_usage.0 as f32 / 100.0; // Percentage を f32 に変換 + total_usage += usage_f32; + gpu_count += 1; + } + } + + log_info!( + &format!("gpu_count: {:?}", gpu_count), + "get_nvidia_gpu_usage", + None::<&str> + ); + + if gpu_count == 0 { + log_warn!( + "no_usage", + "get_nvidia_gpu_usage", + Some("No GPU usage data collected") + ); + return Err(nvapi::Status::Error); // 使用率が取得できなかった場合のエラーハンドリング + } + + let average_usage = total_usage / gpu_count as f32; + + log_debug!("end", "get_nvidia_gpu_usage", None::<&str>); + + Ok(average_usage) + }); + + handle.await.map_err(|e: JoinError| { + log_error!("join_error", "get_nvidia_gpu_usage", Some(e.to_string())); + nvapi::Status::Error + })? +} + +#[derive(Debug, Clone, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NameValue { + name: String, + value: f64, // 摂氏温度 +} + +/// +/// ## GPU温度を取得する(NVAPI を使用) +/// +pub async fn get_nvidia_gpu_temperature() -> Result, nvapi::Status> { + let handle = spawn_blocking(|| { + log_debug!("start", "get_nvidia_gpu_temperature", None::<&str>); + + let gpus = nvapi::PhysicalGpu::enumerate()?; + + if gpus.is_empty() { + log_warn!( + "not found", + "get_nvidia_gpu_temperature", + Some("gpu is not found") + ); + tracing::warn!("gpu is not found"); + return Err(nvapi::Status::Error); // GPUが見つからない場合はエラーを返す + } + + let mut temperatures = Vec::new(); + + for gpu in gpus.iter() { + // 温度情報を取得 + let thermal_settings = gpu.thermal_settings(None).map_err(|e| { + log_warn!( + "thermal_settings_failed", + "get_nvidia_gpu_temperature", + Some(&format!("{:?}", e)) + ); + nvapi::Status::Error + })?; + + temperatures.push(NameValue { + name: gpu.full_name().unwrap_or("Unknown".to_string()), + value: thermal_settings[0].current_temperature.0 as f64, // thermal_settings の0番目の温度を f64 に変換 + }); + } + + Ok(temperatures) + }); + + handle.await.map_err(|e: JoinError| { + log_error!( + "join_error", + "get_nvidia_gpu_temperature", + Some(e.to_string()) + ); + nvapi::Status::Error + })? +} + +/// +/// ## GPUのファン回転数を取得する(NVAPI を使用) +/// +pub async fn get_nvidia_gpu_cooler_stat() -> Result, nvapi::Status> { + let handle = spawn_blocking(|| { + log_debug!("start", "get_nvidia_gpu_cooler_stat", None::<&str>); + + let gpus = nvapi::PhysicalGpu::enumerate()?; + + if gpus.is_empty() { + log_warn!( + "not found", + "get_nvidia_gpu_cooler_stat", + Some("gpu is not found") + ); + tracing::warn!("gpu is not found"); + return Err(nvapi::Status::Error); // GPUが見つからない場合はエラーを返す + } + + let mut cooler_infos = Vec::new(); + + print!("{:?}", gpus); + + for gpu in gpus.iter() { + // 温度情報を取得 + let cooler_settings = gpu.cooler_settings(None).map_err(|e| { + log_warn!( + "cooler_settings_failed", + "get_nvidia_gpu_cooler_stat", + Some(&format!("{:?}", e)) + ); + nvapi::Status::Error + })?; + + cooler_infos.push(NameValue { + name: gpu.full_name().unwrap_or("Unknown".to_string()), + value: cooler_settings[0].current_level.0 as f64, + }); + } + + Ok(cooler_infos) + }); + + handle.await.map_err(|e: JoinError| { + log_error!( + "join_error", + "get_nvidia_gpu_cooler_stat", + Some(e.to_string()) + ); + nvapi::Status::Error + })? +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GraphicInfo { + name: String, + vendor_name: String, + clock: u64, + memory_size: String, + memory_size_dedicated: String, +} + +/// +/// GPU情報を取得する +/// +/// - [TODO] AMD GPU の情報も取得する +/// +pub async fn get_nvidia_gpu_info() -> Result, String> { + let handle = spawn_blocking(|| { + log_debug!("start", "get_nvidia_gpu_info", None::<&str>); + + let gpus = match nvapi::PhysicalGpu::enumerate() { + Ok(gpus) => gpus, + Err(e) => { + log_error!( + "enumerate_failed", + "get_nvidia_gpu_info", + Some(e.to_string()) + ); + return Err(e.to_string()); + } + }; + + if gpus.is_empty() { + log_warn!("not found", "get_nvidia_gpu_info", Some("gpu is not found")); + tracing::warn!("gpu is not found"); + } + + let mut gpu_info_list = Vec::new(); + + for gpu in gpus.iter() { + let name = gpu.full_name().unwrap_or("Unknown".to_string()); + + // クロック周波数 (MHz) の取得 + let clock_frequencies = + match gpu.clock_frequencies(nvapi::ClockFrequencyType::Current) { + Ok(freqs) => freqs, + Err(e) => { + log_error!("clock_failed", "get_nvidia_gpu_info", Some(e.to_string())); + continue; + } + }; + + let frequency = match clock_frequencies.get(&nvapi::ClockDomain::Graphics) { + Some(&nvapi::Kilohertz(freq)) => freq as u64, + None => { + log_warn!( + "clock_not_found", + "get_nvidia_gpu_info", + Some("Graphics clock not found") + ); + 0 // デフォルト値として 0 を設定 + } + }; + + // メモリサイズ (MB) の取得 + let memory_info = match gpu.memory_info() { + Ok(info) => info, + Err(e) => { + log_error!( + "memory_info_failed", + "get_nvidia_gpu_info", + Some(e.to_string()) + ); + continue; + } + }; + + let gpu_info = GraphicInfo { + name, + vendor_name: "NVIDIA".to_string(), + clock: frequency, + memory_size: utils::formatter::RoundedKibibytes { + kibibytes: memory_info.shared, + precision: 1, + } + .to_string(), + memory_size_dedicated: utils::formatter::RoundedKibibytes { + kibibytes: memory_info.dedicated, + precision: 1, + } + .to_string(), + }; + + gpu_info_list.push(gpu_info); + } + + log_debug!("end", "get_nvidia_gpu_info", None::<&str>); + + Ok(gpu_info_list) + }); + + handle.await.map_err(|e: JoinError| { + log_error!("join_error", "get_nvidia_gpu_info", Some(e.to_string())); + nvapi::Status::Error.to_string() + })? +} diff --git a/src-tauri/src/services/mod.rs b/src-tauri/src/services/mod.rs new file mode 100644 index 0000000..1218bac --- /dev/null +++ b/src-tauri/src/services/mod.rs @@ -0,0 +1,3 @@ +pub mod graphic_service; +pub mod system_info_service; +pub mod window_menu_service; diff --git a/src-tauri/src/services/system_info_service.rs b/src-tauri/src/services/system_info_service.rs new file mode 100644 index 0000000..3a73517 --- /dev/null +++ b/src-tauri/src/services/system_info_service.rs @@ -0,0 +1,218 @@ +use crate::utils::{self, formatter}; +use crate::{log_debug, log_error, log_info, log_internal}; + +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::sync::MutexGuard; +use std::thread; +use sysinfo::System; +use wmi::{COMLibrary, WMIConnection}; + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CpuInfo { + name: String, + vendor: String, + core_count: usize, + clock: u64, + clock_unit: String, + cpu_name: String, +} + +/// +/// ## CPU情報を取得 +/// +pub fn get_cpu_info(system: MutexGuard<'_, System>) -> Result { + let cpus = system.cpus(); + + if cpus.is_empty() { + return Err("CPU information not available".to_string()); + } + + // CPU情報を収集 + let cpu_info = CpuInfo { + name: cpus[0].brand().to_string(), + vendor: utils::formatter::format_vendor_name(cpus[0].vendor_id()), + core_count: cpus.len(), + clock: cpus[0].frequency(), + clock_unit: "MHz".to_string(), + cpu_name: cpus[0].name().to_string(), + }; + + Ok(cpu_info) +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MemoryInfo { + size: String, + clock: u64, + clock_unit: String, + memory_count: usize, + total_slots: usize, + memory_type: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct Win32PhysicalMemory { + capacity: u64, + speed: u32, + memory_type: Option, + smbios_memory_type: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct Win32PhysicalMemoryArray { + memory_devices: Option, +} + +/// +/// ## メモリ情報を取得 +/// +pub fn get_memory_info() -> Result { + let physical_memory: Vec = get_memory_info_in_thread( + "SELECT Capacity, Speed, MemoryType, SMBIOSMemoryType FROM Win32_PhysicalMemory" + .to_string(), + )?; + + let physical_memory_array: Vec = get_memory_info_in_thread( + "SELECT MemoryDevices FROM Win32_PhysicalMemoryArray".to_string(), + )?; + + log_info!( + &format!("mem info: {:?}", physical_memory), + "get_memory_info", + None::<&str> + ); + + let memory_info = MemoryInfo { + size: formatter::format_size(physical_memory.iter().map(|mem| mem.capacity).sum(), 1), + clock: physical_memory[0].speed as u64, + clock_unit: "MHz".to_string(), + memory_count: physical_memory.len(), + total_slots: physical_memory_array[0].memory_devices.unwrap_or(0) as usize, + memory_type: get_memory_type_with_fallback( + physical_memory[0].memory_type, + physical_memory[0].smbios_memory_type, + ), + }; + + Ok(memory_info) +} + +/// +/// ## MemoryTypeの値に対応するメモリの種類を文字列で返す +/// +/// - [TODO] DDR5に対応する +/// +fn get_memory_type_description(memory_type: Option) -> String { + log_info!( + &format!("mem type: {:?}", memory_type), + "get_memory_type_description", + None::<&str> + ); + + match memory_type { + Some(0) => "Unknown or Unsupported".to_string(), + Some(1) => "Other".to_string(), + Some(2) => "DRAM".to_string(), + Some(3) => "Synchronous DRAM".to_string(), + Some(4) => "Cache DRAM".to_string(), + Some(5) => "EDO".to_string(), + Some(6) => "EDRAM".to_string(), + Some(7) => "VRAM".to_string(), + Some(8) => "SRAM".to_string(), + Some(9) => "RAM".to_string(), + Some(10) => "ROM".to_string(), + Some(11) => "Flash".to_string(), + Some(12) => "EEPROM".to_string(), + Some(13) => "FEPROM".to_string(), + Some(14) => "EPROM".to_string(), + Some(15) => "CDRAM".to_string(), + Some(16) => "3DRAM".to_string(), + Some(17) => "SDRAM".to_string(), + Some(18) => "SGRAM".to_string(), + Some(19) => "RDRAM".to_string(), + Some(20) => "DDR".to_string(), + Some(21) => "DDR2".to_string(), + Some(22) => "DDR2 FB-DIMM".to_string(), + Some(24) => "DDR3".to_string(), + Some(25) => "FBD2".to_string(), + Some(26) => "DDR4".to_string(), + Some(mt) => format!("Other or Unknown Memory Type ({})", mt), + None => "Unknown".to_string(), + } +} + +/// +/// ## MemoryType もしくは SMBIOSMemoryType からメモリの種類を取得 +/// +fn get_memory_type_with_fallback( + memory_type: Option, + smbios_memory_type: Option, +) -> String { + match memory_type { + Some(0) => match smbios_memory_type { + Some(20) => "DDR".to_string(), + Some(21) => "DDR2".to_string(), + Some(24) => "DDR3".to_string(), + Some(26) => "DDR4".to_string(), + Some(34) => "DDR5".to_string(), + Some(mt) => format!("Other SMBIOS Memory Type ({})", mt), + None => "Unknown".to_string(), + }, + Some(mt) => get_memory_type_description(Some(mt)), + None => "Unknown".to_string(), + } +} + +/// +/// ## メモリ情報を別スレッドで取得する(WMIを使用) +/// +fn get_memory_info_in_thread(query: String) -> Result, String> +where + T: DeserializeOwned + std::fmt::Debug + Send + 'static, +{ + let (tx, rx): ( + Sender, String>>, + Receiver, String>>, + ) = channel(); + + // 別スレッドを起動してWMIクエリを実行 + thread::spawn(move || { + let result = (|| { + let com_con = COMLibrary::new() + .map_err(|e| format!("Failed to initialize COM Library: {:?}", e))?; + let wmi_con = WMIConnection::new(com_con) + .map_err(|e| format!("Failed to create WMI connection: {:?}", e))?; + + // WMIクエリを実行してメモリ情報を取得 + let results: Vec = wmi_con + .raw_query(query.clone()) + .map_err(|e| format!("Failed to execute query: {:?}", e))?; + + log_info!( + &format!("mem info: {:?}", results), + "get_memory_info_in_thread", + Some(&format!("query: {}", query)) + ); + + Ok(results) + })(); + + // メインスレッドに結果を送信 + if let Err(err) = tx.send(result) { + log_error!( + "Failed to send data from thread", + "get_wmi_data_in_thread", + Some(err.to_string()) + ); + } + }); + + // メインスレッドで結果を受信 + rx.recv().map_err(|_| "Failed to receive data from thread".to_string())? +} diff --git a/src-tauri/src/services/window_menu_service.rs b/src-tauri/src/services/window_menu_service.rs new file mode 100644 index 0000000..8725476 --- /dev/null +++ b/src-tauri/src/services/window_menu_service.rs @@ -0,0 +1,25 @@ +use crate::{log_debug, log_error, log_info, log_internal, log_warn}; +use tauri::{CustomMenuItem, Menu, MenuItem, Submenu, WindowMenuEvent}; + +pub fn create_setting() -> Menu { + let settings = CustomMenuItem::new("preference".to_string(), "Preference"); + + let submenu = Submenu::new( + "File", + Menu::new().add_item(settings).add_native_item(MenuItem::Quit), + ); + + Menu::new().add_submenu(submenu) +} + +pub fn handle_menu_event(event: WindowMenuEvent) { + match event.menu_item_id() { + "preference" => { + log_debug!("preference", "preference", None::<&str>); + + let window = event.window(); + window.emit("open_settings", {}).unwrap(); + } + _ => {} + } +} diff --git a/src-tauri/src/utils/formatter.rs b/src-tauri/src/utils/formatter.rs new file mode 100644 index 0000000..2e2500d --- /dev/null +++ b/src-tauri/src/utils/formatter.rs @@ -0,0 +1,81 @@ +use crate::utils; +use nvapi::Kibibytes; +use rust_decimal::prelude::{FromPrimitive, ToPrimitive}; +use rust_decimal::Decimal; +use std::fmt; + +pub struct RoundedKibibytes { + pub kibibytes: Kibibytes, + pub precision: usize, +} + +/// +/// ## `Kibibytes` をフォーマット +/// +impl fmt::Display for RoundedKibibytes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let value = self.kibibytes.0; // Kibibytesの内部値を取得 + if value < 1000 { + write!(f, "{} KiB", value) + } else if value < 1000000 { + let value_in_mib = value as f32 / 1024.0; + write!( + f, + "{:.precision$} MiB", + value_in_mib, + precision = self.precision + ) // 指定された桁数でフォーマット + } else { + let value_in_gib = value as f32 / 1048576.0; + write!( + f, + "{:.precision$} GiB", + value_in_gib, + precision = self.precision + ) // 指定された桁数でフォーマット + } + } +} + +/// +/// ## 小数を指定桁数で丸める(四捨五入) +/// +pub fn round(num: f64, precision: usize) -> f64 { + Decimal::from_f64(num) + .map(|d| d.round_dp(precision as u32).to_f64().unwrap_or(0.0)) + .unwrap_or(0.0) +} + +/// +/// ## バイト数を単位付きの文字列に変換 +/// +pub fn format_size(bytes: u64, precision: usize) -> String { + const KILOBYTE: u64 = 1024; + const MEGABYTE: u64 = KILOBYTE * 1024; + const GIGABYTE: u64 = MEGABYTE * 1024; + + if bytes >= GIGABYTE { + format!( + "{:.precision$} GB", + round(bytes as f64 / GIGABYTE as f64, precision) + ) + } else if bytes >= MEGABYTE { + format!( + "{:.precision$} MB", + round(bytes as f64 / MEGABYTE as f64, precision) + ) + } else { + format!("{} bytes", bytes) + } +} + +/// +/// ## ベンダー名をフォーマット +/// +pub fn format_vendor_name(vendor_id: &str) -> String { + match vendor_id { + "GenuineIntel" => "Intel".to_string(), + "AuthenticAMD" => "AMD".to_string(), + _ => vendor_id.to_string(), + } +} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index 4ccd0f8..38e5ece 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -1,2 +1,3 @@ pub mod file; +pub mod formatter; pub mod logger; diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e6d9789..601f116 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,67 +1,75 @@ -{ - "$schema": "../node_modules/@tauri-apps/cli/schema.json", - "build": { - "beforeBuildCommand": "npm run build", - "beforeDevCommand": "npm run dev", - "devPath": "http://localhost:1520", - "distDir": "../dist" - }, - "package": { - "productName": "hardware-monitor", - "version": "0.1.0" - }, - "tauri": { - "allowlist": { - "all": false - }, - "bundle": { - "active": true, - "category": "DeveloperTool", - "copyright": "", - "deb": { - "depends": [] - }, - "externalBin": [], - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "identifier": "shm11C3.HardwareMonitor", - "longDescription": "", - "macOS": { - "entitlements": null, - "exceptionDomain": "", - "frameworks": [], - "providerShortName": null, - "signingIdentity": null - }, - "resources": [], - "shortDescription": "", - "targets": "all", - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "", - "webviewInstallMode": { - "type": "downloadBootstrapper" - } - } - }, - "security": { - "csp": null - }, - "updater": { - "active": false - }, - "windows": [ - { - "resizable": true, - "title": "hardware-monitor", - "decorations": true - } - ] - } -} +{ + "$schema": "../node_modules/@tauri-apps/cli/schema.json", + "build": { + "beforeBuildCommand": "npm run build", + "beforeDevCommand": "npm run dev", + "devPath": "http://localhost:1520", + "distDir": "../dist" + }, + "package": { + "productName": "hardware-monitor", + "version": "0.1.0" + }, + "tauri": { + "allowlist": { + "all": false, + "dialog": { + "all": true, + "ask": true, + "confirm": true, + "message": true, + "open": true, + "save": true + } + }, + "bundle": { + "active": true, + "category": "DeveloperTool", + "copyright": "", + "deb": { + "depends": [] + }, + "externalBin": [], + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "identifier": "shm11C3.HardwareMonitor", + "longDescription": "", + "macOS": { + "entitlements": null, + "exceptionDomain": "", + "frameworks": [], + "providerShortName": null, + "signingIdentity": null + }, + "resources": [], + "shortDescription": "", + "targets": "all", + "windows": { + "certificateThumbprint": null, + "digestAlgorithm": "sha256", + "timestampUrl": "", + "webviewInstallMode": { + "type": "downloadBootstrapper" + } + } + }, + "security": { + "csp": null + }, + "updater": { + "active": false + }, + "windows": [ + { + "resizable": true, + "title": "hardware-monitor", + "decorations": true + } + ] + } +} diff --git a/src/App.tsx b/src/App.tsx index 930dd13..1eb0bdd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,54 +1,52 @@ -import { useEffect, useState } from "react"; -import TestTemplate from "./components/Sample"; -import ChartTemplate from "./template/Chart"; +import { useEffect } from "react"; +import Dashboard from "./template/Dashboard"; +import ChartTemplate from "./template/Usage"; import "./index.css"; +import { useHardwareUpdater, useUsageUpdater } from "@/hooks/useHardwareData"; +import { + useErrorModalListener, + useSettingsModalListener, +} from "@/hooks/useTauriEventListener"; +import SettingsSheet from "@/template/SettingsSheet"; import { useAtom } from "jotai"; -import { settingsAtom } from "./atom/main"; +import { selectedMenuAtom } from "./atom/ui"; +import { useSettingsAtom } from "./atom/useSettingsAtom"; import { useDarkMode } from "./hooks/useDarkMode"; -import { getSettings } from "./services/settingService"; - -type ButtonState = "chart" | "raw"; - -const useLoadSettings = () => { - const [, setSettings] = useAtom(settingsAtom); - - useEffect(() => { - const loadSettings = async () => { - const setting = await getSettings(); - setSettings(setting); - }; - loadSettings(); - }, [setSettings]); -}; +import SideMenu from "./template/SideMenu"; +import type { SelectedMenuType } from "./types/ui"; const Page = () => { - const [buttonState, setButtonState] = useState("chart"); - const [settings] = useAtom(settingsAtom); - const { toggle } = useDarkMode(); - - useLoadSettings(); - - const handleShowData = () => { - setButtonState(buttonState === "raw" ? "chart" : "raw"); - }; - - useEffect(() => { - if (settings?.theme) { - toggle(settings.theme === "dark"); - } - }, [settings?.theme, toggle]); - - return ( -
-

Hardware Monitor Proto

-
- -
- {buttonState === "raw" ? : } -
- ); + const { settings } = useSettingsAtom(); + const [selectedMenu] = useAtom(selectedMenuAtom); + const { toggle } = useDarkMode(); + + useSettingsModalListener(); + useErrorModalListener(); + useUsageUpdater("cpu"); + useUsageUpdater("memory"); + useUsageUpdater("gpu"); + useHardwareUpdater("gpu", "temp"); + useHardwareUpdater("gpu", "fan"); + + useEffect(() => { + if (settings?.theme) { + toggle(settings.theme === "dark"); + } + }, [settings?.theme, toggle]); + + const displayTargets: Record = { + dashboard: , + usage: , + settings:
TODO
, + }; + + return ( +
+ + {displayTargets[selectedMenu]} + +
+ ); }; export default Page; diff --git a/src/atom/chart.ts b/src/atom/chart.ts new file mode 100644 index 0000000..9c30400 --- /dev/null +++ b/src/atom/chart.ts @@ -0,0 +1,10 @@ +import type { NameValues } from "@/types/hardwareDataType"; +import { atom } from "jotai"; + +export const cpuUsageHistoryAtom = atom([]); +export const memoryUsageHistoryAtom = atom([]); +export const graphicUsageHistoryAtom = atom([]); +export const cpuTempAtom = atom([]); +export const cpuFanSpeedAtom = atom([]); +export const gpuTempAtom = atom([]); +export const gpuFanSpeedAtom = atom([]); diff --git a/src/atom/main.ts b/src/atom/main.ts deleted file mode 100644 index 8cbeb36..0000000 --- a/src/atom/main.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { atom } from "jotai"; -import type { Settings } from "../types/settingsType"; - -export const settingsAtom = atom(null); diff --git a/src/atom/ui.ts b/src/atom/ui.ts new file mode 100644 index 0000000..fa195d8 --- /dev/null +++ b/src/atom/ui.ts @@ -0,0 +1,8 @@ +import type { SelectedMenuType } from "@/types/ui"; +import { atom } from "jotai"; + +export const modalAtoms = { + showSettingsModal: atom(false), +}; + +export const selectedMenuAtom = atom("dashboard"); diff --git a/src/atom/useHardwareInfoAtom.ts b/src/atom/useHardwareInfoAtom.ts new file mode 100644 index 0000000..52d2037 --- /dev/null +++ b/src/atom/useHardwareInfoAtom.ts @@ -0,0 +1,33 @@ +import { getHardwareInfo } from "@/services/hardwareService"; +import type { HardwareInfo } from "@/types/hardwareDataType"; +import { atom, useAtom } from "jotai"; +import { useEffect } from "react"; + +const hardInfoAtom = atom({ + isFetched: false, +}); + +export const useHardwareInfoAtom = () => { + const [hardwareInfo, setHardInfo] = useAtom(hardInfoAtom); + + useEffect(() => { + if (!hardwareInfo.isFetched) { + const init = async () => { + try { + const fetchedHardwareInfo = await getHardwareInfo(); + + setHardInfo({ + ...fetchedHardwareInfo, + isFetched: true, + }); + } catch (e) { + console.error(e); + } + }; + + init(); + } + }, [hardwareInfo.isFetched, setHardInfo]); + + return { hardwareInfo }; +}; diff --git a/src/atom/useSettingsAtom.ts b/src/atom/useSettingsAtom.ts new file mode 100644 index 0000000..f8f71f7 --- /dev/null +++ b/src/atom/useSettingsAtom.ts @@ -0,0 +1,51 @@ +import { + getSettings, + setDisplayTargets, + setTheme, +} from "@/services/settingService"; +import type { ChartDataType } from "@/types/hardwareDataType"; +import type { Settings } from "@/types/settingsType"; +import { atom, useAtom } from "jotai"; +import { useEffect } from "react"; +const settingsAtom = atom({ + language: "en", + theme: "light", + display_targets: [], +}); + +export const useSettingsAtom = () => { + const [settings, setSettings] = useAtom(settingsAtom); + + useEffect(() => { + const loadSettings = async () => { + const setting = await getSettings(); + setSettings(setting); + }; + loadSettings(); + }, [setSettings]); + + const toggleDisplayTarget = async (target: ChartDataType) => { + const newTargets = settings.display_targets.includes(target) + ? settings.display_targets.filter((t) => t !== target) + : [...settings.display_targets, target]; + + try { + // [TODO] Result型を作りたい + await setDisplayTargets(newTargets); + setSettings((prev) => ({ ...prev, display_targets: newTargets })); + } catch (e) { + console.error(e); + } + }; + + const toggleTheme = async (theme: "light" | "dark") => { + try { + await setTheme(theme); + setSettings((prev) => ({ ...prev, theme })); + } catch (e) { + console.error(e); + } + }; + + return { settings, toggleDisplayTarget, toggleTheme }; +}; diff --git a/src/components/CustomLegend.tsx b/src/components/CustomLegend.tsx deleted file mode 100644 index 08140c5..0000000 --- a/src/components/CustomLegend.tsx +++ /dev/null @@ -1,25 +0,0 @@ -export type LegendItem = { - label: string; - icon: JSX.Element; - datasetIndex: number; -}; - -const CustomLegend = ({ - item, -}: { - item: LegendItem; -}) => { - return ( -
-
- {item.icon} - {item.label} -
-
- ); -}; - -export default CustomLegend; diff --git a/src/components/LineChart.tsx b/src/components/LineChart.tsx deleted file mode 100644 index 14cd87c..0000000 --- a/src/components/LineChart.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { Cpu, GraphicsCard, Memory } from "@phosphor-icons/react"; -import { - CategoryScale, - Chart as ChartJS, - type ChartOptions, - Legend, - LineElement, - LinearScale, - PointElement, - Title, - Tooltip, -} from "chart.js"; -import type { Chart, ChartData } from "chart.js"; -import { useRef } from "react"; -import { Line } from "react-chartjs-2"; -import type { ChartDataType } from "../types/chartType"; -import CustomLegend, { type LegendItem } from "./CustomLegend"; - -ChartJS.register( - CategoryScale, - LinearScale, - PointElement, - LineElement, - Title, - Tooltip, - Legend, -); - -const LineChart = ({ - labels, - chartData, - dataType, -}: { - labels: string[]; - chartData: number[]; - dataType: ChartDataType; -}) => { - const chartRef = useRef>(null); - - const options: ChartOptions<"line"> = { - responsive: true, - animation: false, - scales: { - x: { display: false }, - y: { - display: true, - suggestedMin: 0, - suggestedMax: 100, - grid: { color: "rgba(255, 255, 255, 0.2)" }, - ticks: { color: "#fff" }, - }, - }, - elements: { - point: { radius: 0, hoverRadius: 0 }, - line: { tension: 0.4 }, - }, - plugins: { - legend: { display: false }, - tooltip: { - backgroundColor: "rgba(0, 0, 0, 0.7)", - titleColor: "#fff", - bodyColor: "#fff", - }, - }, - }; - - const data: Record> = { - cpu: { - labels, - datasets: [ - { - label: "CPU Usage (%)", - data: chartData, - borderColor: "rgb(75, 192, 192)", - backgroundColor: "rgba(75, 192, 192, 0.3)", - fill: true, - }, - ], - }, - memory: { - labels, - datasets: [ - { - label: "Memory Usage (%)", - data: chartData, - borderColor: "rgb(255, 99, 132)", - backgroundColor: "rgba(255, 99, 132, 0.3)", - fill: true, - }, - ], - }, - gpu: { - labels, - datasets: [ - { - label: "GPU Usage (%)", - data: chartData, - borderColor: "rgb(255, 206, 86)", - backgroundColor: "rgba(255, 206, 86, 0.3)", - fill: true, - }, - ], - }, - }; - - const legendItems: Record = { - cpu: { - label: "CPU Usage", - icon: ( - - ), - datasetIndex: 0, - }, - memory: { - label: "Memory Usage", - icon: ( - - ), - datasetIndex: 1, - }, - gpu: { - label: "GPU Usage", - icon: ( - - ), - datasetIndex: 2, - }, - }; - - return ( -
- -
- -
-
- ); -}; - -export default LineChart; diff --git a/src/components/Sample.tsx b/src/components/Sample.tsx deleted file mode 100644 index eebd461..0000000 --- a/src/components/Sample.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useEffect, useState } from "react"; -import { - getCpuMemoryHistory, - getCpuUsage, - getCpuUsageHistory, - getGpuUsage, - getGpuUsageHistory, - getMemoryUsage, -} from "../services/hardwareService"; - -const Sample = () => { - const [cpuUsage, setCpuUsage] = useState(0); - const [memoryUsage, setMemoryUsage] = useState(0); - const [gpuUsage, setGpuUsage] = useState(0); - - const [cpuHistory, setCpuHistory] = useState([]); - const [memoryHistory, setMemoryHistory] = useState([]); - const [gpuHistory, setGpuHistory] = useState([]); - - useEffect(() => { - const interval = setInterval(async () => { - setCpuUsage(await getCpuUsage()); - setMemoryUsage(await getMemoryUsage()); - setCpuHistory(await getCpuUsageHistory(30)); - setMemoryHistory(await getCpuMemoryHistory(30)); - setGpuUsage(await getGpuUsage()); - setGpuHistory(await getGpuUsageHistory(30)); - }, 1000); - - return () => clearInterval(interval); - }, []); - - return ( -
-

CPU: {cpuUsage}%

-

MEMORY: {memoryUsage}%

-

GPU: {gpuUsage}%

- -
-

CPU History

-

Count: {cpuHistory.length}

-
    - {cpuHistory.map((item, index) => ( -
  • {item}
  • - ))} -
-

MEMORY History

-

Count: {memoryHistory.length}

-
    - {memoryHistory.map((item, index) => ( -
  • {item}
  • - ))} -
-

GPU History

-

Count: {gpuHistory.length}

-
    - {gpuHistory.map((item, index) => ( -
  • {item}
  • - ))} -
-
-
- ); -}; - -export default Sample; diff --git a/src/components/charts/CustomLegend.tsx b/src/components/charts/CustomLegend.tsx new file mode 100644 index 0000000..3645724 --- /dev/null +++ b/src/components/charts/CustomLegend.tsx @@ -0,0 +1,22 @@ +export type LegendItem = { + label: string; + icon: JSX.Element; + datasetIndex: number; +}; + +const CustomLegend = ({ + item, +}: { + item: LegendItem; +}) => { + return ( +
+
+ {item.icon} + {item.label} +
+
+ ); +}; + +export default CustomLegend; diff --git a/src/components/charts/DoughnutChart.tsx b/src/components/charts/DoughnutChart.tsx new file mode 100644 index 0000000..0f0fcb1 --- /dev/null +++ b/src/components/charts/DoughnutChart.tsx @@ -0,0 +1,79 @@ +import { displayDataType, displayHardType } from "@/consts/chart"; +import type { ChartDataType, HardwareDataType } from "@/types/hardwareDataType"; +import { Lightning, Speedometer, Thermometer } from "@phosphor-icons/react"; +import { + ArcElement, + Chart as ChartJS, + type ChartOptions, + Legend, + Tooltip, +} from "chart.js"; +import { Doughnut } from "react-chartjs-2"; + +ChartJS.register(ArcElement, Tooltip, Legend); + +const DoughnutChart = ({ + chartData, + hardType, + dataType, + showTitle, +}: { + chartData: number; + hardType: ChartDataType; + dataType: HardwareDataType; + showTitle: boolean; +}) => { + const data = { + datasets: [ + { + data: [chartData, 100 - chartData], + backgroundColor: ["#888", "#222"], + borderWidth: 0, + }, + ], + }; + + const options: ChartOptions<"doughnut"> = { + cutout: "85%", + plugins: { + tooltip: { enabled: false }, + }, + }; + + const dataTypeIcons: Record = { + usage: , + temp: , + clock: , + }; + + const dataTypeUnits: Record = { + usage: "%", + temp: "℃", + clock: "MHz", + }; + + return ( +
+

+ { + showTitle + ? displayHardType[hardType] + : " " /** [TODO] タイトルはコンポーネント外のほうが使いやすそう */ + } +

+ +
+ + {chartData} + {dataTypeUnits[dataType]} + +
+ + {dataTypeIcons[dataType]} + {displayDataType[dataType]} + +
+ ); +}; + +export default DoughnutChart; diff --git a/src/components/charts/LineChart.tsx b/src/components/charts/LineChart.tsx new file mode 100644 index 0000000..5ed3acf --- /dev/null +++ b/src/components/charts/LineChart.tsx @@ -0,0 +1,151 @@ +import type { ChartDataType } from "@/types/hardwareDataType"; +import { Cpu, GraphicsCard, Memory } from "@phosphor-icons/react"; +import { + CategoryScale, + Chart as ChartJS, + type ChartOptions, + Filler, + Legend, + LineElement, + LinearScale, + PointElement, + Title, + Tooltip, +} from "chart.js"; +import type { Chart, ChartData } from "chart.js"; +import { useRef } from "react"; +import { Line } from "react-chartjs-2"; +import CustomLegend, { type LegendItem } from "./CustomLegend"; + +ChartJS.register( + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + Tooltip, + Legend, + Filler, +); + +const LineChart = ({ + labels, + chartData, + dataType, +}: { + labels: string[]; + chartData: number[]; + dataType: ChartDataType; +}) => { + const chartRef = useRef>(null); + + const options: ChartOptions<"line"> = { + responsive: true, + animation: false, + scales: { + x: { display: false }, + y: { + display: false, + suggestedMin: 0, + suggestedMax: 100, + grid: { color: "rgba(255, 255, 255, 0.2)" }, + ticks: { color: "#fff" }, + }, + }, + elements: { + point: { radius: 0, hoverRadius: 0 }, + line: { tension: 0.4 }, + }, + plugins: { + legend: { display: false }, + tooltip: { + backgroundColor: "rgba(0, 0, 0, 0.7)", + titleColor: "#fff", + bodyColor: "#fff", + }, + }, + }; + + const data: Record> = { + cpu: { + labels, + datasets: [ + { + label: "CPU Usage (%)", + data: chartData, + borderColor: "rgb(75, 192, 192)", + backgroundColor: "rgba(75, 192, 192, 0.3)", + fill: true, + }, + ], + }, + memory: { + labels, + datasets: [ + { + label: "Memory Usage (%)", + data: chartData, + borderColor: "rgb(255, 99, 132)", + backgroundColor: "rgba(255, 99, 132, 0.3)", + fill: true, + }, + ], + }, + gpu: { + labels, + datasets: [ + { + label: "GPU Usage (%)", + data: chartData, + borderColor: "rgb(255, 206, 86)", + backgroundColor: "rgba(255, 206, 86, 0.3)", + fill: true, + }, + ], + }, + }; + + const legendItems: Record = { + cpu: { + label: "CPU", + icon: ( + + ), + datasetIndex: 0, + }, + memory: { + label: "Memory", + icon: ( + + ), + datasetIndex: 1, + }, + gpu: { + label: "GPU", + icon: ( + + ), + datasetIndex: 2, + }, + }; + + return ( +
+ +
+ +
+
+ ); +}; + +export default LineChart; diff --git a/src/components/charts/ProcessTable.tsx b/src/components/charts/ProcessTable.tsx new file mode 100644 index 0000000..5631359 --- /dev/null +++ b/src/components/charts/ProcessTable.tsx @@ -0,0 +1,127 @@ +import { getProcesses } from "@/services/hardwareService"; +import type { ProcessInfo } from "@/types/hardwareDataType"; +import { CaretDown } from "@phosphor-icons/react"; +import { atom, useAtom, useSetAtom } from "jotai"; +import { useEffect, useState } from "react"; + +const processesAtom = atom([]); + +const ProcessesTable = ({ + defaultItemLength, +}: { defaultItemLength: number }) => { + const [processes] = useAtom(processesAtom); + const setAtom = useSetAtom(processesAtom); + const [showAllItem, setShowAllItem] = useState(false); + const [sortConfig, setSortConfig] = useState<{ + key: keyof ProcessInfo; + direction: "ascending" | "descending"; + } | null>(null); + + useEffect(() => { + const fetchProcesses = async () => { + try { + const processesData = await getProcesses(); + console.log(processesData); + setAtom(processesData); + } catch (error) { + console.error("Failed to fetch processes:", error); + } + }; + + fetchProcesses(); + + const interval = setInterval(fetchProcesses, 3000); + + return () => clearInterval(interval); + }, [setAtom]); + + const sortedProcesses = [...processes]; + if (sortConfig !== null) { + sortedProcesses.sort((a, b) => { + if (a[sortConfig.key] < b[sortConfig.key]) { + return sortConfig.direction === "ascending" ? -1 : 1; + } + if (a[sortConfig.key] > b[sortConfig.key]) { + return sortConfig.direction === "ascending" ? 1 : -1; + } + return 0; + }); + } + + const requestSort = (key: keyof ProcessInfo) => { + let direction: "ascending" | "descending" = "ascending"; + if ( + sortConfig && + sortConfig.key === key && + sortConfig.direction === "ascending" + ) { + direction = "descending"; + } + setSortConfig({ key, direction }); + }; + + return ( +
+

Process

+
+ + + + + + + + + + + {sortedProcesses + .slice(0, showAllItem ? processes.length : defaultItemLength) + .map((process) => ( + + + + + + + ))} + +
requestSort("pid")} + onKeyDown={() => requestSort("pid")} + > + PID + requestSort("name")} + onKeyDown={() => requestSort("name")} + > + Name + requestSort("cpuUsage")} + onKeyDown={() => requestSort("cpuUsage")} + > + CPU Usage + requestSort("memoryUsage")} + onKeyDown={() => requestSort("memoryUsage")} + > + Memory Usage +
{process.pid}{process.name}{process.cpuUsage}%{process.memoryUsage} MB
+ {!showAllItem && ( + + )} +
+
+ ); +}; + +export default ProcessesTable; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..7e2c0c4 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,58 @@ +import { Slot } from "@radix-ui/react-slot"; +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300", + { + variants: { + variant: { + default: + "bg-neutral-900 text-neutral-50 hover:bg-neutral-900/90 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90", + destructive: + "bg-red-500 text-neutral-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-neutral-50 dark:hover:bg-red-900/90", + outline: + "border border-neutral-200 bg-white hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-50", + secondary: + "bg-neutral-100 text-neutral-900 hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80", + ghost: + "hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-800 dark:hover:text-neutral-50", + link: "text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..0bfebd2 --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ); + }, +); +Input.displayName = "Input"; + +export { Input }; diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..a115d28 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,24 @@ +import * as LabelPrimitive from "@radix-ui/react-label"; +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", +); + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)); +Label.displayName = LabelPrimitive.Root.displayName; + +export { Label }; diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx new file mode 100644 index 0000000..4b7f44a --- /dev/null +++ b/src/components/ui/sheet.tsx @@ -0,0 +1,141 @@ +import * as SheetPrimitive from "@radix-ui/react-dialog"; +import { type VariantProps, cva } from "class-variance-authority"; +import { X } from "lucide-react"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Sheet = SheetPrimitive.Root; + +const SheetTrigger = SheetPrimitive.Trigger; + +const SheetClose = SheetPrimitive.Close; + +const SheetPortal = SheetPrimitive.Portal; + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName; + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-white p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-neutral-950", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + }, +); + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +SheetContent.displayName = SheetPrimitive.Content.displayName; + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +SheetHeader.displayName = "SheetHeader"; + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +SheetFooter.displayName = "SheetFooter"; + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetTitle.displayName = SheetPrimitive.Title.displayName; + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetDescription.displayName = SheetPrimitive.Description.displayName; + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +}; diff --git a/src/components/ui/switch.tsx b/src/components/ui/switch.tsx new file mode 100644 index 0000000..6ebabad --- /dev/null +++ b/src/components/ui/switch.tsx @@ -0,0 +1,27 @@ +import * as SwitchPrimitives from "@radix-ui/react-switch"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Switch = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +Switch.displayName = SwitchPrimitives.Root.displayName; + +export { Switch }; diff --git a/src/consts/chart.ts b/src/consts/chart.ts new file mode 100644 index 0000000..33d1c28 --- /dev/null +++ b/src/consts/chart.ts @@ -0,0 +1,20 @@ +import type { ChartDataType, HardwareDataType } from "@/types/hardwareDataType"; + +export const chartConfig = { + /** + * グラフの履歴の長さ(秒) + */ + historyLengthSec: 60, +} as const; + +export const displayHardType: Record = { + cpu: "CPU", + memory: "RAM", + gpu: "GPU", +} as const; + +export const displayDataType: Record = { + temp: "Temperature", + usage: "Usage", + clock: "Clock", +} as const; diff --git a/src/hooks/useDarkMode.ts b/src/hooks/useDarkMode.ts index 107aa92..31da57b 100644 --- a/src/hooks/useDarkMode.ts +++ b/src/hooks/useDarkMode.ts @@ -1,28 +1,28 @@ import { useCallback, useEffect, useState } from "react"; type UseSimpleDarkMode = (isDark?: boolean) => { - isDarkMode: boolean; - toggle: (isDark?: boolean) => void; + isDarkMode: boolean; + toggle: (isDark?: boolean) => void; }; export const useDarkMode: UseSimpleDarkMode = (isInitialDark = false) => { - const [isDarkMode, toggleTheme] = useState(isInitialDark); - const toggle = useCallback((isDark?: boolean) => { - if (typeof isDark === "undefined") { - toggleTheme((state) => !state); - return; - } + const [isDarkMode, toggleTheme] = useState(isInitialDark); + const toggle = useCallback((isDark?: boolean) => { + if (typeof isDark === "undefined") { + toggleTheme((state) => !state); + return; + } - toggleTheme(isDark); - }, []); + toggleTheme(isDark); + }, []); - useEffect(() => { - if (isDarkMode) { - document.documentElement.classList.add("dark"); - } else { - document.documentElement.classList.remove("dark"); - } - }, [isDarkMode]); + useEffect(() => { + if (isDarkMode) { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + }, [isDarkMode]); - return { isDarkMode, toggle }; + return { isDarkMode, toggle }; }; diff --git a/src/hooks/useHardwareData.ts b/src/hooks/useHardwareData.ts new file mode 100644 index 0000000..d4a94b1 --- /dev/null +++ b/src/hooks/useHardwareData.ts @@ -0,0 +1,127 @@ +import { + cpuFanSpeedAtom, + cpuTempAtom, + cpuUsageHistoryAtom, + gpuFanSpeedAtom, + gpuTempAtom, + graphicUsageHistoryAtom, + memoryUsageHistoryAtom, +} from "@/atom/chart"; +import { chartConfig } from "@/consts/chart"; +import { + getCpuUsage, + getGpuFanSpeed, + getGpuTemperature, + getGpuUsage, + getMemoryUsage, +} from "@/services/hardwareService"; +import type { ChartDataType, NameValues } from "@/types/hardwareDataType"; +import { type PrimitiveAtom, useSetAtom } from "jotai"; +import { useEffect } from "react"; + +/** + * ハードウェア使用率の履歴を更新する + */ +export const useUsageUpdater = (dataType: ChartDataType) => { + type AtomActionMapping = { + atom: PrimitiveAtom; + action: () => Promise; + }; + + const mapping: Record = { + cpu: { + atom: cpuUsageHistoryAtom, + action: getCpuUsage, + }, + memory: { + atom: memoryUsageHistoryAtom, + action: getMemoryUsage, + }, + gpu: { + atom: graphicUsageHistoryAtom, + action: getGpuUsage, + }, + }; + + const setHistory = useSetAtom(mapping[dataType].atom); + const getUsage = mapping[dataType].action; + + useEffect(() => { + const intervalId = setInterval(async () => { + const usage = await getUsage(); + setHistory((prev) => { + const newHistory = [...prev, usage]; + + // 履歴保持数に満たない場合は0で埋める + const paddedHistory = Array( + Math.max(chartConfig.historyLengthSec - newHistory.length, 0), + ) + .fill(null) + .concat(newHistory); + return paddedHistory.slice(-chartConfig.historyLengthSec); + }); + }, 1000); + + return () => clearInterval(intervalId); + }, [setHistory, getUsage]); +}; + +export const useHardwareUpdater = ( + hardType: Exclude, + dataType: "temp" | "fan", +) => { + type AtomActionMapping = { + atom: PrimitiveAtom; + action: () => Promise; + }; + + const mapping: Record< + Exclude, + Record<"temp" | "fan", AtomActionMapping> + > = { + cpu: { + temp: { + atom: cpuTempAtom, + action: () => { + console.error("Not implemented"); + return Promise.resolve([]); + }, + }, + fan: { + atom: cpuFanSpeedAtom, + action: () => { + console.error("Not implemented"); + return Promise.resolve([]); + }, + }, + }, + gpu: { + fan: { + atom: gpuFanSpeedAtom, + action: getGpuFanSpeed, + }, + temp: { + atom: gpuTempAtom, + action: getGpuTemperature, + }, + }, + }; + + const setData = useSetAtom(mapping[hardType][dataType].atom); + const getData = mapping[hardType][dataType].action; + + useEffect(() => { + const fetchData = async () => { + const temp = await getData(); + setData(temp); + }; + + fetchData(); + + const intervalId = setInterval(async () => { + fetchData; + }, 10000); + + return () => clearInterval(intervalId); + }, [setData, getData]); +}; diff --git a/src/hooks/useTauriEventListener.ts b/src/hooks/useTauriEventListener.ts new file mode 100644 index 0000000..4705fd2 --- /dev/null +++ b/src/hooks/useTauriEventListener.ts @@ -0,0 +1,56 @@ +import { message } from "@tauri-apps/api/dialog"; +import { listen } from "@tauri-apps/api/event"; +import { useSetAtom } from "jotai"; +import { useEffect } from "react"; +import { modalAtoms } from "../atom/ui"; + +const useTauriEventListener = (event: string, callback: () => void) => { + useEffect(() => { + const unListen = listen(event, callback); + + return () => { + unListen.then((unListen) => unListen()); + }; + }, [event, callback]); +}; + +/** + * モーダルを開くイベントをリッスンして、モーダルを表示する + * + * @returns closeModal + */ +export const useSettingsModalListener = () => { + const setShowSettingsModal = useSetAtom(modalAtoms.showSettingsModal); + + // モーダルを開くイベントをリッスン + useTauriEventListener("open_settings", () => { + setShowSettingsModal(true); + }); + + const closeModal = () => setShowSettingsModal(false); + + return { closeModal }; +}; + +/** + * バックエンド側のエラーイベントをリッスンして、エラーダイヤログを表示する + */ +export const useErrorModalListener = () => { + useEffect(() => { + const unListen = listen("error_event", (event) => { + const { title, message: errorMessage } = event.payload as { + title: string; + message: string; + }; + + message(errorMessage, { + title: title, + type: "error", + }); + }); + + return () => { + unListen.then((off) => off()); + }; + }, []); +}; diff --git a/src/index.css b/src/index.css index b5c61c9..95d8cab 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,81 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; + + --ring: 215 20.2% 65.1%; + + --radius: 0.5rem; + } + + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; + + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; + + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; + + --popover: 224 71% 4%; + --popover-foreground: 215 20.2% 65.1%; + + --border: 216 34% 17%; + --input: 216 34% 17%; + + --card: 224 71% 4%; + --card-foreground: 213 31% 91%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; + + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; + + --destructive: 0 63% 31%; + --destructive-foreground: 210 40% 98%; + + --ring: 216 34% 17%; + + --radius: 0.5rem; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + font-feature-settings: "rlig" 1, "calt" 1; + } +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..365058c --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/main.tsx b/src/main.tsx index c08eb09..2be325e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client"; import App from "./App"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - - - , + + + , ); diff --git a/src/services/hardwareService.ts b/src/services/hardwareService.ts index 7988453..d3b2916 100644 --- a/src/services/hardwareService.ts +++ b/src/services/hardwareService.ts @@ -1,25 +1,48 @@ +import type { + HardwareInfo, + NameValues, + ProcessInfo, +} from "@/types/hardwareDataType"; import { invoke } from "@tauri-apps/api/tauri"; +export const getProcesses = async (): Promise => { + return await invoke("get_process_list"); +}; + export const getCpuUsage = async (): Promise => { - return await invoke("get_cpu_usage"); + return await invoke("get_cpu_usage"); +}; + +export const getHardwareInfo = async (): Promise< + Exclude +> => { + return await invoke("get_hardware_info"); }; export const getMemoryUsage = async (): Promise => { - return await invoke("get_memory_usage"); + return await invoke("get_memory_usage"); }; export const getCpuUsageHistory = (seconds: number): Promise => { - return invoke("get_cpu_usage_history", { seconds: seconds }); + return invoke("get_cpu_usage_history", { seconds: seconds }); }; export const getCpuMemoryHistory = (seconds: number): Promise => { - return invoke("get_memory_usage_history", { seconds: seconds }); + return invoke("get_memory_usage_history", { seconds: seconds }); }; export const getGpuUsage = async (): Promise => { - return await invoke("get_gpu_usage"); + return await invoke("get_gpu_usage"); }; export const getGpuUsageHistory = (seconds: number): Promise => { - return invoke("get_gpu_usage_history", { seconds: seconds }); + return invoke("get_gpu_usage_history", { seconds: seconds }); +}; + +export const getGpuTemperature = async (): Promise => { + return await invoke("get_gpu_temperature"); +}; + +export const getGpuFanSpeed = async (): Promise => { + return await invoke("get_nvidia_gpu_cooler"); }; diff --git a/src/services/settingService.ts b/src/services/settingService.ts index d858d88..3eec3ad 100644 --- a/src/services/settingService.ts +++ b/src/services/settingService.ts @@ -1,6 +1,16 @@ +import type { Settings } from "@/types/settingsType"; import { invoke } from "@tauri-apps/api/tauri"; -import type { Settings } from "../types/settingsType"; export const getSettings = async (): Promise => { - return await invoke("get_settings"); + return await invoke("get_settings"); +}; + +export const setTheme = async (theme: Settings["theme"]): Promise => { + return await invoke("set_theme", { newTheme: theme }); +}; + +export const setDisplayTargets = async ( + targets: Settings["display_targets"], +): Promise => { + return await invoke("set_display_targets", { newTargets: targets }); }; diff --git a/src/template/Chart.tsx b/src/template/Chart.tsx deleted file mode 100644 index 61317bd..0000000 --- a/src/template/Chart.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useAtom } from "jotai"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { settingsAtom } from "../atom/main"; -import LineChart from "../components/LineChart"; -import { - getCpuMemoryHistory, - getCpuUsageHistory, - getGpuUsageHistory, -} from "../services/hardwareService"; - -const ChartTemplate = () => { - const [cpuData, setCpuData] = useState([]); - const [memoryData, setMemoryData] = useState([]); - const [gpuData, setGpuData] = useState([]); - const [labels, setLabels] = useState([]); - - const [settings] = useAtom(settingsAtom); - - const fetchData = useCallback(async () => { - const seconds = 60; - - const newCpuDataPromise = settings?.display_targets.includes("cpu") - ? getCpuUsageHistory(seconds) - : Promise.resolve([]); - const newMemoryDataPromise = settings?.display_targets.includes("memory") - ? getCpuMemoryHistory(seconds) - : Promise.resolve([]); - const newGpuDataPromise = settings?.display_targets.includes("gpu") - ? getGpuUsageHistory(seconds) - : Promise.resolve([]); - - const [newCpuData, newMemoryData, newGpuData] = await Promise.all([ - newCpuDataPromise, - newMemoryDataPromise, - newGpuDataPromise, - ]); - - if (cpuData.length < seconds) { - setCpuData([...cpuData, newCpuData[newCpuData.length - 1]]); - setMemoryData([...memoryData, newMemoryData[newMemoryData.length - 1]]); - setGpuData([...gpuData, newGpuData[newGpuData.length - 1]]); - setLabels([...labels, ""]); - } else { - setCpuData([...cpuData.slice(1), newCpuData[newCpuData.length - 1]]); - setMemoryData([ - ...memoryData.slice(1), - newMemoryData[newMemoryData.length - 1], - ]); - setGpuData([...gpuData.slice(1), newGpuData[newGpuData.length - 1]]); - setLabels([...labels.slice(1), ""]); - } - }, [settings, cpuData, memoryData, gpuData, labels]); - - useEffect(() => { - const interval = setInterval(fetchData, 1000); - return () => clearInterval(interval); - }, [fetchData]); - - const renderedCharts = useMemo(() => { - return ( - <> - {settings?.display_targets.includes("cpu") && ( - - )} - {settings?.display_targets.includes("memory") && ( - - )} - {settings?.display_targets.includes("gpu") && ( - - )} - - ); - }, [labels, cpuData, memoryData, gpuData, settings]); - - return
{renderedCharts}
; -}; - -export default ChartTemplate; diff --git a/src/template/Dashboard.tsx b/src/template/Dashboard.tsx new file mode 100644 index 0000000..fa24444 --- /dev/null +++ b/src/template/Dashboard.tsx @@ -0,0 +1,180 @@ +import { + cpuUsageHistoryAtom, + gpuFanSpeedAtom, + gpuTempAtom, + graphicUsageHistoryAtom, + memoryUsageHistoryAtom, +} from "@/atom/chart"; +import { useHardwareInfoAtom } from "@/atom/useHardwareInfoAtom"; +import DoughnutChart from "@/components/charts/DoughnutChart"; +import ProcessesTable from "@/components/charts/ProcessTable"; +import type { NameValues } from "@/types/hardwareDataType"; +import { useAtom } from "jotai"; + +const InfoTable = ({ + title, + data, +}: { + title?: string; + data: { [key: string]: string | number }; +}) => { + return ( +
+ {title &&

{title}

} + + + {Object.keys(data).map((key) => ( + + + + + ))} + +
{key}{data[key]}
+
+ ); +}; + +const DataArea = ({ children }: { children: React.ReactNode }) => { + return ( +
+
+
{children}
+
+
+ ); +}; + +const CPUInfo = () => { + const [cpuUsageHistory] = useAtom(cpuUsageHistoryAtom); + const { hardwareInfo } = useHardwareInfoAtom(); + + return ( + hardwareInfo.cpu && ( + <> + + + + ) + ); +}; + +const GPUInfo = () => { + const [graphicUsageHistory] = useAtom(graphicUsageHistoryAtom); + const [gpuTemp] = useAtom(gpuTempAtom); + const [gpuFan] = useAtom(gpuFanSpeedAtom); + const { hardwareInfo } = useHardwareInfoAtom(); + + const getTargetInfo = (data: NameValues) => { + return data.find( + (x) => hardwareInfo.gpus && x.name === hardwareInfo.gpus[0].name, + )?.value; + }; + + const targetTemperature = getTargetInfo(gpuTemp); + const targetFanSpeed = getTargetInfo(gpuFan); + + return ( + hardwareInfo.gpus && ( + <> +
+ + {targetTemperature && ( + + )} + {targetFanSpeed && ( + + )} +
+ + + + ) + ); +}; + +const MemoryInfo = () => { + const [memoryUsageHistory] = useAtom(memoryUsageHistoryAtom); + const { hardwareInfo } = useHardwareInfoAtom(); + + return ( + hardwareInfo.memory && ( + <> + + + + ) + ); +}; + +const Dashboard = () => { + return ( +
+
+ + + + + + +
+ +
+ + + + + + +
+
+ ); +}; + +export default Dashboard; diff --git a/src/template/SettingsSheet.tsx b/src/template/SettingsSheet.tsx new file mode 100644 index 0000000..2e0d142 --- /dev/null +++ b/src/template/SettingsSheet.tsx @@ -0,0 +1,111 @@ +import { modalAtoms } from "@/atom/ui"; +import { useSettingsAtom } from "@/atom/useSettingsAtom"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet"; +import { useSettingsModalListener } from "@/hooks/useTauriEventListener"; +import type { ChartDataType } from "@/types/hardwareDataType"; +import { useAtom } from "jotai"; + +const SettingGraphType = () => { + const { settings, toggleDisplayTarget } = useSettingsAtom(); + const selectedGraphTypes = settings.display_targets; + + const toggleGraphType = async (type: ChartDataType) => { + await toggleDisplayTarget(type); + }; + + return ( +
+ +
+ + + +
+
+ ); +}; + +const SettingColorMode = () => { + const { settings, toggleTheme } = useSettingsAtom(); + + const toggleDarkMode = async (mode: "light" | "dark") => { + await toggleTheme(mode); + }; + + return ( +
+ +
+ + +
+
+ ); +}; + +const SettingsSheet = () => { + const [showSettingsModal] = useAtom(modalAtoms.showSettingsModal); + const { closeModal } = useSettingsModalListener(); + + return ( + + + + Edit Preference + + Make changes to your preferences here. Click save when you're done. + + +
+ + +
+
+
+ ); +}; + +export default SettingsSheet; diff --git a/src/template/SideMenu.tsx b/src/template/SideMenu.tsx new file mode 100644 index 0000000..d6b8b51 --- /dev/null +++ b/src/template/SideMenu.tsx @@ -0,0 +1,101 @@ +import { selectedMenuAtom } from "@/atom/ui"; +import type { SelectedMenuType } from "@/types/ui"; +import { CaretDoubleLeft, CaretDoubleRight } from "@phosphor-icons/react"; +import { useAtom } from "jotai"; +import { useState } from "react"; +import { memo, useCallback } from "react"; +import { tv } from "tailwind-variants"; + +const buttonClasses = tv({ + base: "fixed top-0 rounded-xl hover:bg-gray-700 p-2 transition-all", + variants: { + open: { + true: "left-64", + false: "left-0", + }, + }, +}); + +const sideMenuClasses = tv({ + base: "fixed top-0 left-0 h-full bg-gray-800 text-white w-64 transform transition-transform duration-300 ease-in-out", + variants: { + open: { + true: "translate-x-0", + false: "-translate-x-full", + }, + }, +}); + +const menuItemClasses = tv({ + base: "mb-2 rounded-lg transition-colors", + variants: { + selected: { + true: "text-white font-bold", + false: "text-slate-400 hover:text-slate-100", + }, + }, +}); + +const menuTitles: Record = { + dashboard: "Dashboard", + usage: "Usage", + settings: "Settings", +}; + +const SideMenu = () => { + const [isOpen, setIsOpen] = useState(false); + const [selectedMenu, setSelectedMenu] = useAtom(selectedMenuAtom); + + const toggleMenu = useCallback(() => { + setIsOpen((prev) => !prev); + }, []); + + const handleMenuClick = useCallback( + (type: SelectedMenuType) => { + setSelectedMenu(type); + }, + [setSelectedMenu], + ); + + const MenuItem = memo(({ type }: { type: SelectedMenuType }) => { + return ( +
  • + +
  • + ); + }); + + return ( +
    +
    + +
    +
      +
    • +

      Hardware Monitor

      +
    • + + + +
    +
    +
    +
    + ); +}; + +export default SideMenu; diff --git a/src/template/Usage.tsx b/src/template/Usage.tsx new file mode 100644 index 0000000..23a8be2 --- /dev/null +++ b/src/template/Usage.tsx @@ -0,0 +1,58 @@ +import { + cpuUsageHistoryAtom, + graphicUsageHistoryAtom, + memoryUsageHistoryAtom, +} from "@/atom/chart"; +import { useSettingsAtom } from "@/atom/useSettingsAtom"; +import LineChart from "@/components/charts/LineChart"; +import { chartConfig } from "@/consts/chart"; +import { useAtom } from "jotai"; +import { useMemo } from "react"; + +const labels = Array(chartConfig.historyLengthSec).fill(""); + +const CpuUsageChart = () => { + const [cpuUsageHistory] = useAtom(cpuUsageHistoryAtom); + + return ( + + ); +}; + +const MemoryUsageChart = () => { + const [memoryUsageHistory] = useAtom(memoryUsageHistoryAtom); + + return ( + + ); +}; + +const GpuUsageChart = () => { + const [graphicUsageHistory] = useAtom(graphicUsageHistoryAtom); + + return ( + + ); +}; + +const ChartTemplate = () => { + const { settings } = useSettingsAtom(); + + const renderedCharts = useMemo(() => { + return ( + <> + {settings?.display_targets.includes("cpu") && } + {settings?.display_targets.includes("memory") && } + {settings?.display_targets.includes("gpu") && } + + ); + }, [settings]); + + return
    {renderedCharts}
    ; +}; + +export default ChartTemplate; diff --git a/src/types/chartType.ts b/src/types/chartType.ts deleted file mode 100644 index 74f5da6..0000000 --- a/src/types/chartType.ts +++ /dev/null @@ -1 +0,0 @@ -export type ChartDataType = "cpu" | "memory" | "gpu"; diff --git a/src/types/hardwareDataType.ts b/src/types/hardwareDataType.ts new file mode 100644 index 0000000..1f2ced7 --- /dev/null +++ b/src/types/hardwareDataType.ts @@ -0,0 +1,48 @@ +export type ChartDataType = "cpu" | "memory" | "gpu"; + +export type HardwareDataType = "temp" | "usage" | "clock"; + +export type ProcessInfo = { + pid: number; + name: string; + cpuUsage: number; + memoryUsage: number; +}; + +export type CpuInfo = { + name: string; + clock: number; + clockUnit: string; + vendor: string; + coreCount: number; + cpuName: string; +}; + +export type MemoryInfo = { + size: string; + clock: number; + clockUnit: string; + memoryCount: number; + totalSlots: number; + memoryType: string; +}; + +export type GraphicInfo = { + clock: number; + name: string; + vendorName: string; + memorySize: string; + memorySizeDedicated: string; +}; + +export type HardwareInfo = { + cpu?: CpuInfo; + memory?: MemoryInfo; + gpus?: GraphicInfo[]; + isFetched: boolean; +}; + +export type NameValues = Array<{ + name: string; + value: number; +}>; diff --git a/src/types/settingsType.ts b/src/types/settingsType.ts index 7296922..0bf75b4 100644 --- a/src/types/settingsType.ts +++ b/src/types/settingsType.ts @@ -1,5 +1,7 @@ +import type { ChartDataType } from "./hardwareDataType"; + export type Settings = { - language: string; - theme: "light" | "dark"; - display_targets: "cpu" | "memory" | "gpu"; + language: string; + theme: "light" | "dark"; + display_targets: Array; }; diff --git a/src/types/tauriType.ts b/src/types/tauriType.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/types/ui.ts b/src/types/ui.ts new file mode 100644 index 0000000..44ca148 --- /dev/null +++ b/src/types/ui.ts @@ -0,0 +1 @@ +export type SelectedMenuType = "dashboard" | "usage" | "settings"; diff --git a/tailwind.config.js b/tailwind.config.js index e871354..8a7b6f9 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,9 +1,84 @@ +const { fontFamily } = require("tailwindcss/defaultTheme"); + /** @type {import('tailwindcss').Config} */ -export default { +module.exports = { + darkMode: ["class"], content: ["./src/**/*.{js,jsx,ts,tsx}"], theme: { - extend: {}, + container: { + center: "true", + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + fontFamily: { + sans: ["var(--font-sans)", ...fontFamily.sans], + }, + keyframes: { + "accordion-down": { + from: { + height: "0", + }, + to: { + height: "var(--radix-accordion-content-height)", + }, + }, + "accordion-up": { + from: { + height: "var(--radix-accordion-content-height)", + }, + to: { + height: "0", + }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, }, - plugins: [], - darkMode: "class", + plugins: [require("tailwindcss-animate")], }; diff --git a/tsconfig.json b/tsconfig.json index a7fc6fb..7814c8a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,8 +18,14 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + } }, "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] + "references": [{ "path": "./tsconfig.node.json" }], } diff --git a/vite.config.ts b/vite.config.ts index e1eb191..6ae1b59 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,21 +1,27 @@ -import react from "@vitejs/plugin-react"; -import { defineConfig } from "vite"; - -// https://vitejs.dev/config/ -export default defineConfig(async () => ({ - plugins: [react()], - - // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` - // - // 1. prevent vite from obscuring rust errors - clearScreen: false, - // 2. tauri expects a fixed port, fail if that port is not available - server: { - port: 1520, - strictPort: true, - watch: { - // 3. tell vite to ignore watching `src-tauri` - ignored: ["**/src-tauri/**"], - }, - }, -})); +import path from "node:path"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +// https://vitejs.dev/config/ +export default defineConfig(async () => ({ + plugins: [react()], + + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // + // 1. prevent vite from obscuring rust errors + clearScreen: false, + // 2. tauri expects a fixed port, fail if that port is not available + server: { + port: 1520, + strictPort: true, + watch: { + // 3. tell vite to ignore watching `src-tauri` + ignored: ["**/src-tauri/**"], + }, + }, + resolve: { + alias: { + "@": path.resolve(__dirname, "src"), + }, + }, +}));