diff --git a/package.json b/package.json index d5422f6..a4d7dcf 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "fileflow", + "version": "1.0.2", "private": true, "type": "module", "scripts": { @@ -9,25 +10,26 @@ "tauri": "tauri" }, "dependencies": { - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-checkbox": "^1.1.5", + "@radix-ui/react-dialog": "^1.1.7", + "@radix-ui/react-dropdown-menu": "^2.1.7", "@radix-ui/react-icons": "^1.3.2", - "@radix-ui/react-label": "^2.1.2", - "@radix-ui/react-menubar": "^1.1.6", - "@radix-ui/react-popover": "^1.1.6", - "@radix-ui/react-radio-group": "^1.2.3", - "@radix-ui/react-select": "^2.1.6", - "@radix-ui/react-separator": "^1.1.2", - "@radix-ui/react-slot": "^1.1.2", - "@radix-ui/react-switch": "^1.1.3", - "@radix-ui/react-tabs": "^1.1.3", - "@radix-ui/react-tooltip": "^1.1.8", - "@tauri-apps/api": "^2.3.0", - "@tauri-apps/plugin-dialog": "~2.2.0", + "@radix-ui/react-label": "^2.1.3", + "@radix-ui/react-menubar": "^1.1.7", + "@radix-ui/react-popover": "^1.1.7", + "@radix-ui/react-radio-group": "^1.2.4", + "@radix-ui/react-select": "^2.1.7", + "@radix-ui/react-separator": "^1.1.3", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-switch": "^1.1.4", + "@radix-ui/react-tabs": "^1.1.4", + "@radix-ui/react-tooltip": "^1.2.0", + "@tauri-apps/api": "^2.4.1", + "@tauri-apps/plugin-dialog": "~2.2.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "1.0.0", - "framer-motion": "^12.5.0", + "framer-motion": "^12.6.5", "lucide-react": "^0.441.0", "next-themes": "^0.4.6", "react": "^18.3.1", @@ -38,15 +40,15 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "@tauri-apps/cli": "^2.3.1", - "@types/node": "^22.13.10", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", + "@tauri-apps/cli": "^2.4.1", + "@types/node": "^22.14.1", + "@types/react": "^18.3.20", + "@types/react-dom": "^18.3.6", "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.21", "postcss": "^8.5.3", "tailwindcss": "^3.4.17", - "typescript": "^5.8.2", - "vite": "^5.4.14" + "typescript": "^5.8.3", + "vite": "^5.4.18" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0aaa9de..36feba7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,51 +8,54 @@ importers: .: dependencies: + '@radix-ui/react-checkbox': + specifier: ^1.1.5 + version: 1.1.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dialog': - specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.7 + version: 1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dropdown-menu': - specifier: ^2.1.6 - version: 2.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.1.7 + version: 2.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-icons': specifier: ^1.3.2 version: 1.3.2(react@18.3.1) '@radix-ui/react-label': - specifier: ^2.1.2 - version: 2.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.1.3 + version: 2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-menubar': - specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.7 + version: 1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-popover': - specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.7 + version: 1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-radio-group': - specifier: ^1.2.3 - version: 1.2.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.2.4 + version: 1.2.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-select': - specifier: ^2.1.6 - version: 2.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.1.7 + version: 2.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-separator': - specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.3 + version: 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': - specifier: ^1.1.2 - version: 1.1.2(@types/react@18.3.18)(react@18.3.1) + specifier: ^1.2.0 + version: 1.2.0(@types/react@18.3.20)(react@18.3.1) '@radix-ui/react-switch': - specifier: ^1.1.3 - version: 1.1.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.4 + version: 1.1.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tabs': - specifier: ^1.1.3 - version: 1.1.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.1.4 + version: 1.1.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tooltip': - specifier: ^1.1.8 - version: 1.1.8(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.2.0 + version: 1.2.0(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tauri-apps/api': - specifier: ^2.3.0 - version: 2.3.0 + specifier: ^2.4.1 + version: 2.4.1 '@tauri-apps/plugin-dialog': - specifier: ~2.2.0 - version: 2.2.0 + specifier: ~2.2.1 + version: 2.2.1 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -61,10 +64,10 @@ importers: version: 2.1.1 cmdk: specifier: 1.0.0 - version: 1.0.0(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.0.0(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) framer-motion: - specifier: ^12.5.0 - version: 12.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^12.6.5 + version: 12.6.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) lucide-react: specifier: ^0.441.0 version: 0.441.0(react@18.3.1) @@ -91,20 +94,20 @@ importers: version: 1.0.7(tailwindcss@3.4.17) devDependencies: '@tauri-apps/cli': - specifier: ^2.3.1 - version: 2.3.1 + specifier: ^2.4.1 + version: 2.4.1 '@types/node': - specifier: ^22.13.10 - version: 22.13.10 + specifier: ^22.14.1 + version: 22.14.1 '@types/react': - specifier: ^18.3.18 - version: 18.3.18 + specifier: ^18.3.20 + version: 18.3.20 '@types/react-dom': - specifier: ^18.3.5 - version: 18.3.5(@types/react@18.3.18) + specifier: ^18.3.6 + version: 18.3.6(@types/react@18.3.20) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.3.4(vite@5.4.14(@types/node@22.13.10)) + version: 4.3.4(vite@5.4.18(@types/node@22.14.1)) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.3) @@ -115,11 +118,11 @@ importers: specifier: ^3.4.17 version: 3.4.17 typescript: - specifier: ^5.8.2 - version: 5.8.2 + specifier: ^5.8.3 + version: 5.8.3 vite: - specifier: ^5.4.14 - version: 5.4.14(@types/node@22.13.10) + specifier: ^5.4.18 + version: 5.4.18(@types/node@22.14.1) packages: @@ -143,12 +146,12 @@ packages: resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.10': - resolution: {integrity: sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==} + '@babel/generator@7.27.0': + resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.26.5': - resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} + '@babel/helper-compilation-targets@7.27.0': + resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.25.9': @@ -177,12 +180,12 @@ packages: resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.10': - resolution: {integrity: sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==} + '@babel/helpers@7.27.0': + resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.10': - resolution: {integrity: sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==} + '@babel/parser@7.27.0': + resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} engines: {node: '>=6.0.0'} hasBin: true @@ -198,20 +201,20 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.26.10': - resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} + '@babel/runtime@7.27.0': + resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} engines: {node: '>=6.9.0'} - '@babel/template@7.26.9': - resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} + '@babel/template@7.27.0': + resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.10': - resolution: {integrity: sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==} + '@babel/traverse@7.27.0': + resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.10': - resolution: {integrity: sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==} + '@babel/types@7.27.0': + resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} engines: {node: '>=6.9.0'} '@esbuild/aix-ppc64@0.21.5': @@ -405,17 +408,17 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@radix-ui/number@1.1.0': - resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + '@radix-ui/number@1.1.1': + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} '@radix-ui/primitive@1.0.1': resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} - '@radix-ui/primitive@1.1.1': - resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + '@radix-ui/primitive@1.1.2': + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} - '@radix-ui/react-arrow@1.1.2': - resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==} + '@radix-ui/react-arrow@1.1.3': + resolution: {integrity: sha512-2dvVU4jva0qkNZH6HHWuSz5FN5GeU5tymvCgutF8WaXz9WnD1NgUhy73cqzkjkN4Zkn8lfTPv5JIfrC221W+Nw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -427,8 +430,21 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.2': - resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==} + '@radix-ui/react-checkbox@1.1.5': + resolution: {integrity: sha512-B0gYIVxl77KYDR25AY9EGe/G//ef85RVBIxQvK+m5pxAC7XihAc/8leMHhDvjvhDu02SBSb6BuytlWr/G7F3+g==} + 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 + + '@radix-ui/react-collection@1.1.3': + resolution: {integrity: sha512-mM2pxoQw5HJ49rkzwOs7Y6J4oYH22wS8BfK2/bBxROlI4xuR0c4jEenQP63LlTlDkO6Buj2Vt+QYAYcOgqtrXA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -449,8 +465,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-compose-refs@1.1.1': - resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -467,8 +483,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.1': - resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -489,8 +505,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dialog@1.1.6': - resolution: {integrity: sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==} + '@radix-ui/react-dialog@1.1.7': + resolution: {integrity: sha512-EIdma8C0C/I6kL6sO02avaCRqi3fmWJpxH6mqbVScorW6nNktzKJT/le7VPho3o/7wCsyRg3z0+Q+Obr0Gy/VQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -502,8 +518,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.0': - resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -524,8 +540,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dismissable-layer@1.1.5': - resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==} + '@radix-ui/react-dismissable-layer@1.1.6': + resolution: {integrity: sha512-7gpgMT2gyKym9Jz2ZhlRXSg2y6cNQIK8d/cqBZ0RBCaps8pFryCWXiUKI+uHGFrhMrbGUP7U6PWgiXzIxoyF3Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -537,8 +553,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.6': - resolution: {integrity: sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==} + '@radix-ui/react-dropdown-menu@2.1.7': + resolution: {integrity: sha512-7/1LiuNZuCQE3IzdicGoHdQOHkS2Q08+7p8w6TXZ6ZjgAULaCI85ZY15yPl4o4FVgoKLRT43/rsfNVN8osClQQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -559,8 +575,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-guards@1.1.1': - resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + '@radix-ui/react-focus-guards@1.1.2': + resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -581,8 +597,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-scope@1.1.2': - resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==} + '@radix-ui/react-focus-scope@1.1.3': + resolution: {integrity: sha512-4XaDlq0bPt7oJwR+0k0clCiCO/7lO7NKZTAaJBYxDNQT/vj4ig0/UvctrRscZaFREpRvUTkpKR96ov1e6jptQg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -608,8 +624,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-id@1.1.0': - resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -617,8 +633,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-label@2.1.2': - resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==} + '@radix-ui/react-label@2.1.3': + resolution: {integrity: sha512-zwSQ1NzSKG95yA0tvBMgv6XPHoqapJCcg9nsUBaQQ66iRBhZNhlpaQG2ERYYX4O4stkYFK5rxj5NsWfO9CS+Hg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -630,8 +646,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.6': - resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==} + '@radix-ui/react-menu@2.1.7': + resolution: {integrity: sha512-tBODsrk68rOi1/iQzbM54toFF+gSw/y+eQgttFflqlGekuSebNqvFNHjJgjqPhiMb4Fw9A0zNFly1QT6ZFdQ+Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -643,8 +659,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menubar@1.1.6': - resolution: {integrity: sha512-FHq7+3DlXwh/7FOM4i0G4bC4vPjiq89VEEvNF4VMLchGnaUuUbE5uKXMUCjdKaOghEEMeiKa5XCa2Pk4kteWmg==} + '@radix-ui/react-menubar@1.1.7': + resolution: {integrity: sha512-YB2zFhGdZ5SWEgRS+PgrF7EkwpsjEHntIFB/LRbT49LJdnIeK/xQQyuwLiRcOCgTDN+ALlPXQ08f0P0+TfR41g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -656,8 +672,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.6': - resolution: {integrity: sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==} + '@radix-ui/react-popover@1.1.7': + resolution: {integrity: sha512-I38OYWDmJF2kbO74LX8UsFydSHWOJuQ7LxPnTefjxxvdvPLempvAnmsyX9UsBlywcbSGpRH7oMLfkUf+ij4nrw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -669,8 +685,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.2': - resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==} + '@radix-ui/react-popper@1.2.3': + resolution: {integrity: sha512-iNb9LYUMkne9zIahukgQmHlSBp9XWGeQQ7FvUGNk45ywzOb6kQa+Ca38OphXlWDiKvyneo9S+KSJsLfLt8812A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -695,8 +711,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.4': - resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==} + '@radix-ui/react-portal@1.1.5': + resolution: {integrity: sha512-ps/67ZqsFm+Mb6lSPJpfhRLrVL2i2fntgCmGMqqth4eaGUf+knAuuRtWVJrNjUhExgmdRqftSgzpf0DF0n6yXA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -721,8 +737,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.2': - resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + '@radix-ui/react-presence@1.1.3': + resolution: {integrity: sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -747,8 +763,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.0.2': - resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==} + '@radix-ui/react-primitive@2.0.3': + resolution: {integrity: sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -760,8 +776,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-radio-group@1.2.3': - resolution: {integrity: sha512-xtCsqt8Rp09FK50ItqEqTJ7Sxanz8EM8dnkVIhJrc/wkMMomSmXHvYbhv3E7Zx4oXh98aaLt9W679SUYXg4IDA==} + '@radix-ui/react-radio-group@1.2.4': + resolution: {integrity: sha512-oLz7ATfKgVTUbpr5OBu6Q7hQcnV22uPT306bmG0QwgnKqBStR98RfWfJGCfW/MmhL4ISmrmmBPBW+c77SDwV9g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -773,8 +789,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.2': - resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==} + '@radix-ui/react-roving-focus@1.1.3': + resolution: {integrity: sha512-ufbpLUjZiOg4iYgb2hQrWXEPYX6jOLBbR27bDyAff5GYMRrCzcze8lukjuXVUQvJ6HZe8+oL+hhswDcjmcgVyg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -786,8 +802,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-select@2.1.6': - resolution: {integrity: sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==} + '@radix-ui/react-select@2.1.7': + resolution: {integrity: sha512-exzGIRtc7S8EIM2KjFg+7lJZsH7O7tpaBaJbBNVDnOZNhtoQ2iV+iSNfi2Wth0m6h3trJkMVvzAehB3c6xj/3Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -799,8 +815,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-separator@1.1.2': - resolution: {integrity: sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ==} + '@radix-ui/react-separator@1.1.3': + resolution: {integrity: sha512-2omrWKJvxR0U/tkIXezcc1nFMwtLU0+b/rDK40gnzJqTLWQ/TD/D5IYVefp9sC3QWfeQbpSbEA6op9MQKyaALQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -821,8 +837,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-slot@1.1.2': - resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} + '@radix-ui/react-slot@1.2.0': + resolution: {integrity: sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -830,8 +846,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-switch@1.1.3': - resolution: {integrity: sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ==} + '@radix-ui/react-switch@1.1.4': + resolution: {integrity: sha512-zGP6W8plLeogoeGMiTHJ/uvf+TE1C2chVsEwfP8YlvpQKJHktG+iCkUtCLGPAuDV8/qDSmIRPm4NggaTxFMVBQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -843,8 +859,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tabs@1.1.3': - resolution: {integrity: sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==} + '@radix-ui/react-tabs@1.1.4': + resolution: {integrity: sha512-fuHMHWSf5SRhXke+DbHXj2wVMo+ghVH30vhX3XVacdXqDl+J4XWafMIGOOER861QpBx1jxgwKXL2dQnfrsd8MQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -856,8 +872,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tooltip@1.1.8': - resolution: {integrity: sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==} + '@radix-ui/react-tooltip@1.2.0': + resolution: {integrity: sha512-b1Sdc75s7zN9B8ONQTGBSHL3XS8+IcjcOIY51fhM4R1Hx8s0YbgqgyNZiri4qcYMVZK8hfCZVBiyCm7N9rs0rw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -878,8 +894,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-callback-ref@1.1.0': - resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -896,8 +912,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-controllable-state@1.1.0': - resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + '@radix-ui/react-use-controllable-state@1.1.1': + resolution: {integrity: sha512-YnEXIy8/ga01Y1PN0VfaNH//MhA91JlEGVBDxDzROqwrAtG5Yr2QGEPz8A/rJA3C7ZAHryOYGaUv8fLSW2H/mg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -914,8 +930,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-escape-keydown@1.1.0': - resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -932,8 +948,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-layout-effect@1.1.0': - resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -941,8 +957,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-previous@1.1.0': - resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -950,8 +966,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-rect@1.1.0': - resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -959,8 +975,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-size@1.1.0': - resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -968,8 +984,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-visually-hidden@1.1.2': - resolution: {integrity: sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==} + '@radix-ui/react-visually-hidden@1.1.3': + resolution: {integrity: sha512-oXSF3ZQRd5fvomd9hmUCb2EHSZbPp3ZSHAHJJU/DlF9XoFkJBBW8RHU/E8WEH+RbSfJd/QFA0sl8ClJXknBwHQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -981,207 +997,218 @@ packages: '@types/react-dom': optional: true - '@radix-ui/rect@1.1.0': - resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} '@remix-run/router@1.23.0': resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} engines: {node: '>=14.0.0'} - '@rollup/rollup-android-arm-eabi@4.35.0': - resolution: {integrity: sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==} + '@rollup/rollup-android-arm-eabi@4.40.0': + resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.35.0': - resolution: {integrity: sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==} + '@rollup/rollup-android-arm64@4.40.0': + resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.35.0': - resolution: {integrity: sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==} + '@rollup/rollup-darwin-arm64@4.40.0': + resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.35.0': - resolution: {integrity: sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==} + '@rollup/rollup-darwin-x64@4.40.0': + resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.35.0': - resolution: {integrity: sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==} + '@rollup/rollup-freebsd-arm64@4.40.0': + resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.35.0': - resolution: {integrity: sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==} + '@rollup/rollup-freebsd-x64@4.40.0': + resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.35.0': - resolution: {integrity: sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==} + '@rollup/rollup-linux-arm-gnueabihf@4.40.0': + resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.35.0': - resolution: {integrity: sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==} + '@rollup/rollup-linux-arm-musleabihf@4.40.0': + resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.35.0': - resolution: {integrity: sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==} + '@rollup/rollup-linux-arm64-gnu@4.40.0': + resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.35.0': - resolution: {integrity: sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==} + '@rollup/rollup-linux-arm64-musl@4.40.0': + resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.35.0': - resolution: {integrity: sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==} + '@rollup/rollup-linux-loongarch64-gnu@4.40.0': + resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': - resolution: {integrity: sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==} + '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': + resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.35.0': - resolution: {integrity: sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==} + '@rollup/rollup-linux-riscv64-gnu@4.40.0': + resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.40.0': + resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.35.0': - resolution: {integrity: sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==} + '@rollup/rollup-linux-s390x-gnu@4.40.0': + resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.35.0': - resolution: {integrity: sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==} + '@rollup/rollup-linux-x64-gnu@4.40.0': + resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.35.0': - resolution: {integrity: sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==} + '@rollup/rollup-linux-x64-musl@4.40.0': + resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.35.0': - resolution: {integrity: sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==} + '@rollup/rollup-win32-arm64-msvc@4.40.0': + resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.35.0': - resolution: {integrity: sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==} + '@rollup/rollup-win32-ia32-msvc@4.40.0': + resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.35.0': - resolution: {integrity: sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==} + '@rollup/rollup-win32-x64-msvc@4.40.0': + resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==} cpu: [x64] os: [win32] - '@tauri-apps/api@2.3.0': - resolution: {integrity: sha512-33Z+0lX2wgZbx1SPFfqvzI6su63hCBkbzv+5NexeYjIx7WA9htdOKoRR7Dh3dJyltqS5/J8vQFyybiRoaL0hlA==} + '@tauri-apps/api@2.4.1': + resolution: {integrity: sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg==} - '@tauri-apps/cli-darwin-arm64@2.3.1': - resolution: {integrity: sha512-TOhSdsXYt+f+asRU+Dl+Wufglj/7+CX9h8RO4hl5k7D6lR4L8yTtdhpS7btaclOMmjYC4piNfJE70GoxhOoYWw==} + '@tauri-apps/cli-darwin-arm64@2.4.1': + resolution: {integrity: sha512-QME7s8XQwy3LWClTVlIlwXVSLKkeJ/z88pr917Mtn9spYOjnBfsgHAgGdmpWD3NfJxjg7CtLbhH49DxoFL+hLg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tauri-apps/cli-darwin-x64@2.3.1': - resolution: {integrity: sha512-LDwGg3AuBQ3aCeMAFaFwt0MSGOVFoXuXEe0z4QxQ7jZE5tdAOhKABaq4i569V5lShCgQZ6nLD/tmA5+GipvHnA==} + '@tauri-apps/cli-darwin-x64@2.4.1': + resolution: {integrity: sha512-/r89IcW6Ya1sEsFUEH7wLNruDTj7WmDWKGpPy7gATFtQr5JEY4heernqE82isjTUimnHZD8SCr0jA3NceI4ybw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tauri-apps/cli-linux-arm-gnueabihf@2.3.1': - resolution: {integrity: sha512-hu3HpbbtJBvHXw5i54QHwLxOUoXWqhf7CL2YYSPOrWEEQo10NKddulP61L5gfr5z+bSSaitfLwqgTidgnaNJCA==} + '@tauri-apps/cli-linux-arm-gnueabihf@2.4.1': + resolution: {integrity: sha512-9tDijkRB+CchAGjXxYdY9l/XzFpLp1yihUtGXJz9eh+3qIoRI043n3e+6xmU8ZURr7XPnu+R4sCmXs6HD+NCEQ==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tauri-apps/cli-linux-arm64-gnu@2.3.1': - resolution: {integrity: sha512-mEGgwkiGSKYXWHhGodo7zU9PCd2I/d6KkR+Wp1nzK+DxsCrEK6yJ5XxYLSQSDcKkM4dCxpVEPUiVMbDhmn08jg==} + '@tauri-apps/cli-linux-arm64-gnu@2.4.1': + resolution: {integrity: sha512-pnFGDEXBAzS4iDYAVxTRhAzNu3K2XPGflYyBc0czfHDBXopqRgMyj5Q9Wj7HAwv6cM8BqzXINxnb2ZJFGmbSgA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-arm64-musl@2.3.1': - resolution: {integrity: sha512-tqQkafikGfnc7ISnGjSYkbpnzJKEyO8XSa0YOXTAL3J8R5Pss5ZIZY7G8kq1mwQSR/dPVR1ZLTVXgZGuysjP8w==} + '@tauri-apps/cli-linux-arm64-musl@2.4.1': + resolution: {integrity: sha512-Hp0zXgeZNKmT+eoJSCxSBUm2QndNuRxR55tmIeNm3vbyUMJN/49uW7nurZ5fBPsacN4Pzwlx1dIMK+Gnr9A69w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-x64-gnu@2.3.1': - resolution: {integrity: sha512-I3puDJ2wGEauXlXbzIHn2etz78TaWs1cpN6zre02maHr6ZR7nf7euTCOGPhhfoMG0opA5mT/eLuYpVw648/VAA==} + '@tauri-apps/cli-linux-riscv64-gnu@2.4.1': + resolution: {integrity: sha512-3T3bo2E4fdYRvzcXheWUeQOVB+LunEEi92iPRgOyuSVexVE4cmHYl+MPJF+EUV28Et0hIVTsHibmDO0/04lAFg==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@tauri-apps/cli-linux-x64-gnu@2.4.1': + resolution: {integrity: sha512-kLN0FdNONO+2i+OpU9+mm6oTGufRC00e197TtwjpC0N6K2K8130w7Q3FeODIM2CMyg0ov3tH+QWqKW7GNhHFzg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-linux-x64-musl@2.3.1': - resolution: {integrity: sha512-rbWiCOBuQN7tPySkUyBs914uUikE3mEUOqV/IFospvKESw4UC3G1DL5+ybfXH7Orb8/in3JpJuVzYQjo+OSbBA==} + '@tauri-apps/cli-linux-x64-musl@2.4.1': + resolution: {integrity: sha512-a8exvA5Ub9eg66a6hsMQKJIkf63QAf9OdiuFKOsEnKZkNN2x0NLgfvEcqdw88VY0UMs9dBoZ1AGbWMeYnLrLwQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-win32-arm64-msvc@2.3.1': - resolution: {integrity: sha512-PdTmUzSeTHjJuBpCV7L+V29fPhPtToU+NZU46slHKSA1aT38MiFDXBZ/6P5Zudrt9QPMfIubqnJKbK8Ivvv7Ww==} + '@tauri-apps/cli-win32-arm64-msvc@2.4.1': + resolution: {integrity: sha512-4JFrslsMCJQG1c573T9uqQSAbF3j/tMKkMWzsIssv8jvPiP++OG61A2/F+y9te9/Q/O95cKhDK63kaiO5xQaeg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tauri-apps/cli-win32-ia32-msvc@2.3.1': - resolution: {integrity: sha512-K/Xa97kspWT4UWj3t26lL2D3QsopTAxS7kWi5kObdqtAGn3qD52qBi24FH38TdvHYz4QlnLIb30TukviCgh4gw==} + '@tauri-apps/cli-win32-ia32-msvc@2.4.1': + resolution: {integrity: sha512-9eXfFORehYSCRwxg2KodfmX/mhr50CI7wyBYGbPLePCjr5z0jK/9IyW6r0tC+ZVjwpX48dkk7hKiUgI25jHjzA==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@tauri-apps/cli-win32-x64-msvc@2.3.1': - resolution: {integrity: sha512-RgwzXbP8gAno3kQEsybMtgLp6D1Z1Nec2cftryYbPTJmoMJs6e4qgtxuTSbUz5SKnHe8rGgMiFSvEGoHvbG72Q==} + '@tauri-apps/cli-win32-x64-msvc@2.4.1': + resolution: {integrity: sha512-60a4Ov7Jrwqz2hzDltlS7301dhSAmM9dxo+IRBD3xz7yobKrgaHXYpWvnRomYItHcDd51VaKc9292H8/eE/gsw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tauri-apps/cli@2.3.1': - resolution: {integrity: sha512-xewcw/ZsCqgilTy2h7+pp2Baxoy7zLR2wXOV7SZLzkb6SshHVbm1BFAjn8iFATURRW85KLzl6wSGJ2dQHjVHqw==} + '@tauri-apps/cli@2.4.1': + resolution: {integrity: sha512-9Ta81jx9+57FhtU/mPIckDcOBtPTUdKM75t4+aA0X84b8Sclb0jy1xA8NplmcRzp2fsfIHNngU2NiRxsW5+yOQ==} engines: {node: '>= 10'} hasBin: true - '@tauri-apps/plugin-dialog@2.2.0': - resolution: {integrity: sha512-6bLkYK68zyK31418AK5fNccCdVuRnNpbxquCl8IqgFByOgWFivbiIlvb79wpSXi0O+8k8RCSsIpOquebusRVSg==} + '@tauri-apps/plugin-dialog@2.2.1': + resolution: {integrity: sha512-wZmCouo4PgTosh/UoejPw9DPs6RllS5Pp3fuOV2JobCu36mR5AXU2MzU9NZiVaFi/5Zfc8RN0IhcZHnksJ1o8A==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.6': - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/babel__traverse@7.20.7': + resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - '@types/node@22.13.10': - resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} + '@types/node@22.14.1': + resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==} '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - '@types/react-dom@18.3.5': - resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==} + '@types/react-dom@18.3.6': + resolution: {integrity: sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==} peerDependencies: '@types/react': ^18.0.0 - '@types/react@18.3.18': - resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==} + '@types/react@18.3.20': + resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==} '@vitejs/plugin-react@4.3.4': resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} @@ -1249,8 +1276,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001704: - resolution: {integrity: sha512-+L2IgBbV6gXB4ETf0keSvLr7JUrRVbIaB/lrQ1+z8mRcQiisG5k+lG6O4n6Y5q6f5EuNfaYXKgymucphlEXQew==} + caniuse-lite@1.0.30001713: + resolution: {integrity: sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} @@ -1316,8 +1343,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.116: - resolution: {integrity: sha512-mufxTCJzLBQVvSdZzX1s5YAuXsN1M4tTyYxOOL1TcSKtIzQ9rjIrm7yFK80rN5dwGTePgdoABDSHpuVtRQh0Zw==} + electron-to-chromium@1.5.136: + resolution: {integrity: sha512-kL4+wUTD7RSA5FHx5YwWtjDnEEkIIikFgWHR4P6fqjw1PPLlqYkxeOb++wAauAssat0YClCy8Y3C5SxgSkjibQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1352,8 +1379,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@12.5.0: - resolution: {integrity: sha512-buPlioFbH9/W7rDzYh1C09AuZHAk2D1xTA1BlounJ2Rb9aRg84OXexP0GLd+R83v0khURdMX7b5MKnGTaSg5iA==} + framer-motion@12.6.5: + resolution: {integrity: sha512-MKvnWov0paNjvRJuIy6x418w23tFqRfS6CXHhZrCiSEpXVlo/F+usr8v4/3G6O0u7CpsaO1qop+v4Ip7PRCBqQ==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -1487,11 +1514,11 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - motion-dom@12.5.0: - resolution: {integrity: sha512-uH2PETDh7m+Hjd1UQQ56yHqwn83SAwNjimNPE/kC+Kds0t4Yh7+29rfo5wezVFpPOv57U4IuWved5d1x0kNhbQ==} + motion-dom@12.6.5: + resolution: {integrity: sha512-jpM9TQLXzYMWMJ7Ec7sAj0iis8oIuu6WvjI3yNKJLdrZyrsI/b2cRInDVL8dCl683zQQq19DpL9cSMP+k8T1NA==} - motion-utils@12.5.0: - resolution: {integrity: sha512-+hFFzvimn0sBMP9iPxBa9OtRX35ZQ3py0UHnb8U29VD+d8lQ8zH3dTygJWqK7av2v6yhg7scj9iZuvTS0f4+SA==} + motion-utils@12.6.5: + resolution: {integrity: sha512-IsOeKsOF+FWBhxQEDFBO6ZYC8/jlidmVbbLpe9/lXSA9j9kzGIMUuIBx2SZY+0reAS0DjZZ1i7dJp4NHrjocPw==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1499,8 +1526,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.9: - resolution: {integrity: sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -1554,8 +1581,8 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} postcss-import@15.1.0: @@ -1687,8 +1714,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.35.0: - resolution: {integrity: sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==} + rollup@4.40.0: + resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1779,13 +1806,13 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - typescript@5.8.2: - resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.20.0: - resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} @@ -1816,8 +1843,8 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@5.4.14: - resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + vite@5.4.18: + resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -1863,8 +1890,8 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} engines: {node: '>= 14'} hasBin: true @@ -1889,14 +1916,14 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.10 - '@babel/helper-compilation-targets': 7.26.5 + '@babel/generator': 7.27.0 + '@babel/helper-compilation-targets': 7.27.0 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) - '@babel/helpers': 7.26.10 - '@babel/parser': 7.26.10 - '@babel/template': 7.26.9 - '@babel/traverse': 7.26.10 - '@babel/types': 7.26.10 + '@babel/helpers': 7.27.0 + '@babel/parser': 7.27.0 + '@babel/template': 7.27.0 + '@babel/traverse': 7.27.0 + '@babel/types': 7.27.0 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -1905,15 +1932,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.26.10': + '@babel/generator@7.27.0': dependencies: - '@babel/parser': 7.26.10 - '@babel/types': 7.26.10 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.26.5': + '@babel/helper-compilation-targets@7.27.0': dependencies: '@babel/compat-data': 7.26.8 '@babel/helper-validator-option': 7.25.9 @@ -1923,8 +1950,8 @@ snapshots: '@babel/helper-module-imports@7.25.9': dependencies: - '@babel/traverse': 7.26.10 - '@babel/types': 7.26.10 + '@babel/traverse': 7.27.0 + '@babel/types': 7.27.0 transitivePeerDependencies: - supports-color @@ -1933,7 +1960,7 @@ snapshots: '@babel/core': 7.26.10 '@babel/helper-module-imports': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.10 + '@babel/traverse': 7.27.0 transitivePeerDependencies: - supports-color @@ -1945,14 +1972,14 @@ snapshots: '@babel/helper-validator-option@7.25.9': {} - '@babel/helpers@7.26.10': + '@babel/helpers@7.27.0': dependencies: - '@babel/template': 7.26.9 - '@babel/types': 7.26.10 + '@babel/template': 7.27.0 + '@babel/types': 7.27.0 - '@babel/parser@7.26.10': + '@babel/parser@7.27.0': dependencies: - '@babel/types': 7.26.10 + '@babel/types': 7.27.0 '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.10)': dependencies: @@ -1964,29 +1991,29 @@ snapshots: '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.26.5 - '@babel/runtime@7.26.10': + '@babel/runtime@7.27.0': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.26.9': + '@babel/template@7.27.0': dependencies: '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.10 - '@babel/types': 7.26.10 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 - '@babel/traverse@7.26.10': + '@babel/traverse@7.27.0': dependencies: '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.10 - '@babel/parser': 7.26.10 - '@babel/template': 7.26.9 - '@babel/types': 7.26.10 + '@babel/generator': 7.27.0 + '@babel/parser': 7.27.0 + '@babel/template': 7.27.0 + '@babel/types': 7.27.0 debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.26.10': + '@babel/types@7.27.0': dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 @@ -2118,743 +2145,766 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@radix-ui/number@1.1.0': {} + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.0.1': dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.27.0 - '@radix-ui/primitive@1.1.1': {} + '@radix-ui/primitive@1.1.2': {} - '@radix-ui/react-arrow@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-arrow@1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-collection@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-checkbox@1.1.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-collection@1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.0(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-compose-refs@1.1.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.20)(react@18.3.1)': dependencies: + '@babel/runtime': 7.27.0 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-context@1.0.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-context@1.1.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-context@1.0.1(@types/react@18.3.20)(react@18.3.1)': dependencies: + '@babel/runtime': 7.27.0 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-context@1.1.2(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.20 + + '@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.0 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.0.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.0.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.0.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.0.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.0.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.20)(react@18.3.1) aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.5.5(@types/react@18.3.18)(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - - '@radix-ui/react-dialog@1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) + react-remove-scroll: 2.5.5(@types/react@18.3.20)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) + + '@radix-ui/react-dialog@1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.6(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-portal': 1.1.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.0(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.6.3(@types/react@18.3.18)(react@18.3.1) + react-remove-scroll: 2.6.3(@types/react@18.3.20)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-direction@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-direction@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.27.0 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.1.6(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-dropdown-menu@2.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dropdown-menu@2.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-menu': 2.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-menu': 2.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.27.0 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-focus-guards@1.1.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-focus-guards@1.1.2(@types/react@18.3.20)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.18)(react@18.3.1) + '@babel/runtime': 7.27.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-focus-scope@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-focus-scope@1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) '@radix-ui/react-icons@1.3.2(react@18.3.1)': dependencies: react: 18.3.1 - '@radix-ui/react-id@1.0.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-id@1.0.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.18)(react@18.3.1) + '@babel/runtime': 7.27.0 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-id@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-id@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-label@2.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-label@2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - - '@radix-ui/react-menu@2.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) + + '@radix-ui/react-menu@2.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.6(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-popper': 1.2.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.0(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1) aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.6.3(@types/react@18.3.18)(react@18.3.1) + react-remove-scroll: 2.6.3(@types/react@18.3.20)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-menubar@1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-menubar@1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-menu': 2.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-menu': 2.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - - '@radix-ui/react-popover@1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) + + '@radix-ui/react-popover@1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.6(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-popper': 1.2.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.0(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.6.3(@types/react@18.3.18)(react@18.3.1) + react-remove-scroll: 2.6.3(@types/react@18.3.20)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-popper@1.2.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-popper@1.2.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-arrow': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/rect': 1.1.0 + '@radix-ui/react-arrow': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/rect': 1.1.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@babel/runtime': 7.27.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-portal@1.1.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.1.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.18)(react@18.3.1) + '@babel/runtime': 7.27.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-presence@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-presence@1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 - '@radix-ui/react-slot': 1.0.2(@types/react@18.3.18)(react@18.3.1) + '@babel/runtime': 7.27.0 + '@radix-ui/react-slot': 1.0.2(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-primitive@2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-slot': 1.2.0(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-radio-group@1.2.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-radio-group@1.2.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-roving-focus@1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - - '@radix-ui/react-select@2.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/number': 1.1.0 - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) + + '@radix-ui/react-select@2.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.6(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-popper': 1.2.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.0(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.6.3(@types/react@18.3.18)(react@18.3.1) + react-remove-scroll: 2.6.3(@types/react@18.3.20)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-separator@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-separator@1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-slot@1.0.2(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-slot@1.0.2(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.18)(react@18.3.1) + '@babel/runtime': 7.27.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-slot@1.1.2(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-slot@1.2.0(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-switch@1.1.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-switch@1.1.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-tabs@1.1.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-tabs@1.1.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-tooltip@1.1.8(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-tooltip@1.2.0(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.6(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-popper': 1.2.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.0(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@18.3.20)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.27.0 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.18)(react@18.3.1) + '@babel/runtime': 7.27.0 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-controllable-state@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.18)(react@18.3.1) + '@babel/runtime': 7.27.0 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.27.0 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@radix-ui/rect': 1.1.0 + '@radix-ui/rect': 1.1.1 react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-use-size@1.1.0(@types/react@18.3.18)(react@18.3.1)': + '@radix-ui/react-use-size@1.1.1(@types/react@18.3.20)(react@18.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@radix-ui/react-visually-hidden@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-visually-hidden@1.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@types/react': 18.3.20 + '@types/react-dom': 18.3.6(@types/react@18.3.20) - '@radix-ui/rect@1.1.0': {} + '@radix-ui/rect@1.1.1': {} '@remix-run/router@1.23.0': {} - '@rollup/rollup-android-arm-eabi@4.35.0': + '@rollup/rollup-android-arm-eabi@4.40.0': optional: true - '@rollup/rollup-android-arm64@4.35.0': + '@rollup/rollup-android-arm64@4.40.0': optional: true - '@rollup/rollup-darwin-arm64@4.35.0': + '@rollup/rollup-darwin-arm64@4.40.0': optional: true - '@rollup/rollup-darwin-x64@4.35.0': + '@rollup/rollup-darwin-x64@4.40.0': optional: true - '@rollup/rollup-freebsd-arm64@4.35.0': + '@rollup/rollup-freebsd-arm64@4.40.0': optional: true - '@rollup/rollup-freebsd-x64@4.35.0': + '@rollup/rollup-freebsd-x64@4.40.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.35.0': + '@rollup/rollup-linux-arm-gnueabihf@4.40.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.35.0': + '@rollup/rollup-linux-arm-musleabihf@4.40.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.35.0': + '@rollup/rollup-linux-arm64-gnu@4.40.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.35.0': + '@rollup/rollup-linux-arm64-musl@4.40.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.35.0': + '@rollup/rollup-linux-loongarch64-gnu@4.40.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.35.0': + '@rollup/rollup-linux-riscv64-gnu@4.40.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.35.0': + '@rollup/rollup-linux-riscv64-musl@4.40.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.35.0': + '@rollup/rollup-linux-s390x-gnu@4.40.0': optional: true - '@rollup/rollup-linux-x64-musl@4.35.0': + '@rollup/rollup-linux-x64-gnu@4.40.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.35.0': + '@rollup/rollup-linux-x64-musl@4.40.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.35.0': + '@rollup/rollup-win32-arm64-msvc@4.40.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.35.0': + '@rollup/rollup-win32-ia32-msvc@4.40.0': optional: true - '@tauri-apps/api@2.3.0': {} + '@rollup/rollup-win32-x64-msvc@4.40.0': + optional: true + + '@tauri-apps/api@2.4.1': {} + + '@tauri-apps/cli-darwin-arm64@2.4.1': + optional: true - '@tauri-apps/cli-darwin-arm64@2.3.1': + '@tauri-apps/cli-darwin-x64@2.4.1': optional: true - '@tauri-apps/cli-darwin-x64@2.3.1': + '@tauri-apps/cli-linux-arm-gnueabihf@2.4.1': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@2.3.1': + '@tauri-apps/cli-linux-arm64-gnu@2.4.1': optional: true - '@tauri-apps/cli-linux-arm64-gnu@2.3.1': + '@tauri-apps/cli-linux-arm64-musl@2.4.1': optional: true - '@tauri-apps/cli-linux-arm64-musl@2.3.1': + '@tauri-apps/cli-linux-riscv64-gnu@2.4.1': optional: true - '@tauri-apps/cli-linux-x64-gnu@2.3.1': + '@tauri-apps/cli-linux-x64-gnu@2.4.1': optional: true - '@tauri-apps/cli-linux-x64-musl@2.3.1': + '@tauri-apps/cli-linux-x64-musl@2.4.1': optional: true - '@tauri-apps/cli-win32-arm64-msvc@2.3.1': + '@tauri-apps/cli-win32-arm64-msvc@2.4.1': optional: true - '@tauri-apps/cli-win32-ia32-msvc@2.3.1': + '@tauri-apps/cli-win32-ia32-msvc@2.4.1': optional: true - '@tauri-apps/cli-win32-x64-msvc@2.3.1': + '@tauri-apps/cli-win32-x64-msvc@2.4.1': optional: true - '@tauri-apps/cli@2.3.1': + '@tauri-apps/cli@2.4.1': optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.3.1 - '@tauri-apps/cli-darwin-x64': 2.3.1 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.3.1 - '@tauri-apps/cli-linux-arm64-gnu': 2.3.1 - '@tauri-apps/cli-linux-arm64-musl': 2.3.1 - '@tauri-apps/cli-linux-x64-gnu': 2.3.1 - '@tauri-apps/cli-linux-x64-musl': 2.3.1 - '@tauri-apps/cli-win32-arm64-msvc': 2.3.1 - '@tauri-apps/cli-win32-ia32-msvc': 2.3.1 - '@tauri-apps/cli-win32-x64-msvc': 2.3.1 + '@tauri-apps/cli-darwin-arm64': 2.4.1 + '@tauri-apps/cli-darwin-x64': 2.4.1 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.4.1 + '@tauri-apps/cli-linux-arm64-gnu': 2.4.1 + '@tauri-apps/cli-linux-arm64-musl': 2.4.1 + '@tauri-apps/cli-linux-riscv64-gnu': 2.4.1 + '@tauri-apps/cli-linux-x64-gnu': 2.4.1 + '@tauri-apps/cli-linux-x64-musl': 2.4.1 + '@tauri-apps/cli-win32-arm64-msvc': 2.4.1 + '@tauri-apps/cli-win32-ia32-msvc': 2.4.1 + '@tauri-apps/cli-win32-x64-msvc': 2.4.1 - '@tauri-apps/plugin-dialog@2.2.0': + '@tauri-apps/plugin-dialog@2.2.1': dependencies: - '@tauri-apps/api': 2.3.0 + '@tauri-apps/api': 2.4.1 '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.26.10 - '@babel/types': 7.26.10 - '@types/babel__generator': 7.6.8 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 + '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.6 + '@types/babel__traverse': 7.20.7 - '@types/babel__generator@7.6.8': + '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.26.10 + '@babel/types': 7.27.0 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.26.10 - '@babel/types': 7.26.10 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 - '@types/babel__traverse@7.20.6': + '@types/babel__traverse@7.20.7': dependencies: - '@babel/types': 7.26.10 + '@babel/types': 7.27.0 - '@types/estree@1.0.6': {} + '@types/estree@1.0.7': {} - '@types/node@22.13.10': + '@types/node@22.14.1': dependencies: - undici-types: 6.20.0 + undici-types: 6.21.0 '@types/prop-types@15.7.14': {} - '@types/react-dom@18.3.5(@types/react@18.3.18)': + '@types/react-dom@18.3.6(@types/react@18.3.20)': dependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - '@types/react@18.3.18': + '@types/react@18.3.20': dependencies: '@types/prop-types': 15.7.14 csstype: 3.1.3 - '@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@22.13.10))': + '@vitejs/plugin-react@4.3.4(vite@5.4.18(@types/node@22.14.1))': dependencies: '@babel/core': 7.26.10 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.14(@types/node@22.13.10) + vite: 5.4.18(@types/node@22.14.1) transitivePeerDependencies: - supports-color @@ -2884,7 +2934,7 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.3): dependencies: browserslist: 4.24.4 - caniuse-lite: 1.0.30001704 + caniuse-lite: 1.0.30001713 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -2905,14 +2955,14 @@ snapshots: browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001704 - electron-to-chromium: 1.5.116 + caniuse-lite: 1.0.30001713 + electron-to-chromium: 1.5.136 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.4) camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001704: {} + caniuse-lite@1.0.30001713: {} chokidar@3.6.0: dependencies: @@ -2932,10 +2982,10 @@ snapshots: clsx@2.1.1: {} - cmdk@1.0.0(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + cmdk@1.0.0(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -2974,7 +3024,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.116: {} + electron-to-chromium@1.5.136: {} emoji-regex@8.0.0: {} @@ -3031,10 +3081,10 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@12.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + framer-motion@12.6.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - motion-dom: 12.5.0 - motion-utils: 12.5.0 + motion-dom: 12.6.5 + motion-utils: 12.6.5 tslib: 2.8.1 optionalDependencies: react: 18.3.1 @@ -3137,11 +3187,11 @@ snapshots: minipass@7.1.2: {} - motion-dom@12.5.0: + motion-dom@12.6.5: dependencies: - motion-utils: 12.5.0 + motion-utils: 12.6.5 - motion-utils@12.5.0: {} + motion-utils@12.6.5: {} ms@2.1.3: {} @@ -3151,7 +3201,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.9: {} + nanoid@3.3.11: {} next-themes@0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: @@ -3185,7 +3235,7 @@ snapshots: pify@2.3.0: {} - pirates@4.0.6: {} + pirates@4.0.7: {} postcss-import@15.1.0(postcss@8.5.3): dependencies: @@ -3202,7 +3252,7 @@ snapshots: postcss-load-config@4.0.2(postcss@8.5.3): dependencies: lilconfig: 3.1.3 - yaml: 2.7.0 + yaml: 2.7.1 optionalDependencies: postcss: 8.5.3 @@ -3220,7 +3270,7 @@ snapshots: postcss@8.5.3: dependencies: - nanoid: 3.3.9 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -3234,35 +3284,35 @@ snapshots: react-refresh@0.14.2: {} - react-remove-scroll-bar@2.3.8(@types/react@18.3.18)(react@18.3.1): + react-remove-scroll-bar@2.3.8(@types/react@18.3.20)(react@18.3.1): dependencies: react: 18.3.1 - react-style-singleton: 2.2.3(@types/react@18.3.18)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.20)(react@18.3.1) tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - react-remove-scroll@2.5.5(@types/react@18.3.18)(react@18.3.1): + react-remove-scroll@2.5.5(@types/react@18.3.20)(react@18.3.1): dependencies: react: 18.3.1 - react-remove-scroll-bar: 2.3.8(@types/react@18.3.18)(react@18.3.1) - react-style-singleton: 2.2.3(@types/react@18.3.18)(react@18.3.1) + react-remove-scroll-bar: 2.3.8(@types/react@18.3.20)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.20)(react@18.3.1) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@18.3.18)(react@18.3.1) - use-sidecar: 1.1.3(@types/react@18.3.18)(react@18.3.1) + use-callback-ref: 1.3.3(@types/react@18.3.20)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.20)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - react-remove-scroll@2.6.3(@types/react@18.3.18)(react@18.3.1): + react-remove-scroll@2.6.3(@types/react@18.3.20)(react@18.3.1): dependencies: react: 18.3.1 - react-remove-scroll-bar: 2.3.8(@types/react@18.3.18)(react@18.3.1) - react-style-singleton: 2.2.3(@types/react@18.3.18)(react@18.3.1) + react-remove-scroll-bar: 2.3.8(@types/react@18.3.20)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.20)(react@18.3.1) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@18.3.18)(react@18.3.1) - use-sidecar: 1.1.3(@types/react@18.3.18)(react@18.3.1) + use-callback-ref: 1.3.3(@types/react@18.3.20)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.20)(react@18.3.1) optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 react-router-dom@6.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: @@ -3276,13 +3326,13 @@ snapshots: '@remix-run/router': 1.23.0 react: 18.3.1 - react-style-singleton@2.2.3(@types/react@18.3.18)(react@18.3.1): + react-style-singleton@2.2.3(@types/react@18.3.20)(react@18.3.1): dependencies: get-nonce: 1.0.1 react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 react@18.3.1: dependencies: @@ -3306,29 +3356,30 @@ snapshots: reusify@1.1.0: {} - rollup@4.35.0: - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.35.0 - '@rollup/rollup-android-arm64': 4.35.0 - '@rollup/rollup-darwin-arm64': 4.35.0 - '@rollup/rollup-darwin-x64': 4.35.0 - '@rollup/rollup-freebsd-arm64': 4.35.0 - '@rollup/rollup-freebsd-x64': 4.35.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.35.0 - '@rollup/rollup-linux-arm-musleabihf': 4.35.0 - '@rollup/rollup-linux-arm64-gnu': 4.35.0 - '@rollup/rollup-linux-arm64-musl': 4.35.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.35.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.35.0 - '@rollup/rollup-linux-riscv64-gnu': 4.35.0 - '@rollup/rollup-linux-s390x-gnu': 4.35.0 - '@rollup/rollup-linux-x64-gnu': 4.35.0 - '@rollup/rollup-linux-x64-musl': 4.35.0 - '@rollup/rollup-win32-arm64-msvc': 4.35.0 - '@rollup/rollup-win32-ia32-msvc': 4.35.0 - '@rollup/rollup-win32-x64-msvc': 4.35.0 + rollup@4.40.0: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.40.0 + '@rollup/rollup-android-arm64': 4.40.0 + '@rollup/rollup-darwin-arm64': 4.40.0 + '@rollup/rollup-darwin-x64': 4.40.0 + '@rollup/rollup-freebsd-arm64': 4.40.0 + '@rollup/rollup-freebsd-x64': 4.40.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.0 + '@rollup/rollup-linux-arm-musleabihf': 4.40.0 + '@rollup/rollup-linux-arm64-gnu': 4.40.0 + '@rollup/rollup-linux-arm64-musl': 4.40.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0 + '@rollup/rollup-linux-riscv64-gnu': 4.40.0 + '@rollup/rollup-linux-riscv64-musl': 4.40.0 + '@rollup/rollup-linux-s390x-gnu': 4.40.0 + '@rollup/rollup-linux-x64-gnu': 4.40.0 + '@rollup/rollup-linux-x64-musl': 4.40.0 + '@rollup/rollup-win32-arm64-msvc': 4.40.0 + '@rollup/rollup-win32-ia32-msvc': 4.40.0 + '@rollup/rollup-win32-x64-msvc': 4.40.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -3383,7 +3434,7 @@ snapshots: glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.6 + pirates: 4.0.7 ts-interface-checker: 0.1.13 supports-preserve-symlinks-flag@1.0.0: {} @@ -3437,9 +3488,9 @@ snapshots: tslib@2.8.1: {} - typescript@5.8.2: {} + typescript@5.8.3: {} - undici-types@6.20.0: {} + undici-types@6.21.0: {} update-browserslist-db@1.1.3(browserslist@4.24.4): dependencies: @@ -3447,30 +3498,30 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - use-callback-ref@1.3.3(@types/react@18.3.18)(react@18.3.1): + use-callback-ref@1.3.3(@types/react@18.3.20)(react@18.3.1): dependencies: react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 - use-sidecar@1.1.3(@types/react@18.3.18)(react@18.3.1): + use-sidecar@1.1.3(@types/react@18.3.20)(react@18.3.1): dependencies: detect-node-es: 1.1.0 react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.20 util-deprecate@1.0.2: {} - vite@5.4.14(@types/node@22.13.10): + vite@5.4.18(@types/node@22.14.1): dependencies: esbuild: 0.21.5 postcss: 8.5.3 - rollup: 4.35.0 + rollup: 4.40.0 optionalDependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.1 fsevents: 2.3.3 which@2.0.2: @@ -3491,4 +3542,4 @@ snapshots: yallist@3.1.1: {} - yaml@2.7.0: {} + yaml@2.7.1: {} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 6064f60..15c6ad5 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "FileFlow" -version = "1.0.1" +version = "1.0.2" dependencies = [ "csv", "serde", @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.87" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", @@ -202,9 +202,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.2" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8faa168b8c4ffca39c2699e772943af41ec2b75fb1683dda07b28a6d285c53dc" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bitflags" @@ -355,9 +355,9 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbd1fe9db3ebf71b89060adaf7b0504c2d6a425cf061313099547e382c2e472" +checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" dependencies = [ "serde", "toml", @@ -365,9 +365,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.16" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ "shlex", ] @@ -550,9 +550,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -642,9 +642,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -652,9 +652,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", @@ -666,9 +666,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", @@ -688,9 +688,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -910,9 +910,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -942,9 +942,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ "event-listener", "pin-project-lite", @@ -977,9 +977,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -1004,9 +1004,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foreign-types" @@ -1302,22 +1302,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1635,29 +1633,11 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.27.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -1665,6 +1645,7 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -1674,16 +1655,17 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core 0.61.0", ] [[package]] @@ -1746,9 +1728,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -1770,9 +1752,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -1791,9 +1773,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -1863,9 +1845,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -2078,9 +2060,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" @@ -2100,9 +2082,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "mac" @@ -2163,9 +2145,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -2572,15 +2554,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl" -version = "0.10.71" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ "bitflags 2.9.0", "cfg-if", @@ -2610,9 +2592,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.106" +version = "0.9.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" dependencies = [ "cc", "libc", @@ -2880,12 +2862,12 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" dependencies = [ "base64 0.22.1", - "indexmap 2.8.0", + "indexmap 2.9.0", "quick-xml", "serde", "time", @@ -3001,58 +2983,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "quinn" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" -dependencies = [ - "bytes", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.12", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" -dependencies = [ - "bytes", - "getrandom 0.2.15", - "rand 0.8.5", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.12", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", -] - [[package]] name = "quote" version = "1.0.40" @@ -3062,6 +2992,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.7.3" @@ -3152,7 +3088,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", ] [[package]] @@ -3181,9 +3117,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags 2.9.0", ] @@ -3230,9 +3166,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.14" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e327e510263980e231de548a33e63d34962d29ae61b467389a1a09627a254" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64 0.22.1", "bytes", @@ -3242,7 +3178,6 @@ dependencies = [ "http-body", "http-body-util", "hyper", - "hyper-rustls", "hyper-util", "ipnet", "js-sys", @@ -3251,16 +3186,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "quinn", - "rustls", - "rustls-pemfile", - "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-rustls", "tokio-util", "tower", "tower-service", @@ -3269,7 +3199,6 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", "windows-registry", ] @@ -3298,20 +3227,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.15", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - [[package]] name = "rsa" version = "0.9.8" @@ -3338,12 +3253,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - [[package]] name = "rustc_version" version = "0.4.1" @@ -3355,9 +3264,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags 2.9.0", "errno", @@ -3366,49 +3275,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "rustls" -version = "0.23.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" -dependencies = [ - "web-time", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.20" @@ -3620,7 +3486,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.8.0", + "indexmap 2.9.0", "serde", "serde_derive", "serde_json", @@ -3748,18 +3614,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" dependencies = [ "serde", ] [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3862,7 +3728,7 @@ dependencies = [ "futures-util", "hashbrown 0.15.2", "hashlink", - "indexmap 2.8.0", + "indexmap 2.9.0", "log", "memchr", "native-tls", @@ -4034,9 +3900,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "string_cache" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot", @@ -4204,9 +4070,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.3.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be747b26bf28674977fac47bdf6963fd9c7578271c3fbeb25d8686de6596f35" +checksum = "4d08db1ff9e011e04014e737ec022610d756c0eae0b3b3a9037bccaf3003173a" dependencies = [ "anyhow", "bytes", @@ -4254,9 +4120,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.0.6" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a2e96f3c0baa0581656bb58e6fdd0f7c9c31eaf6721a0c08689d938fe85f2d" +checksum = "0fd20e4661c2cce65343319e6e8da256958f5af958cafc47c0d0af66a55dcd17" dependencies = [ "anyhow", "cargo_toml", @@ -4276,9 +4142,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.0.5" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e357ec3daf8faad1029bc7109e7f5b308ceb63b6073d110d7388923a4cce5e55" +checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4" dependencies = [ "base64 0.22.1", "brotli", @@ -4303,9 +4169,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.5" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447ee4dd94690d77f1422f2b57e783c654ba75c535ad6f6e727887330804fff2" +checksum = "d402813d3b9c773a0fa58697c457c771f10e735498fdcb7b343264d18e5a601f" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4317,9 +4183,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.0.5" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad3021d8e60ec7672f51ecb67c5e1a514a4d7a9a5ffc9d85090739378047502" +checksum = "a4190775d6ff73fe66d9af44c012739a2659720efd9c0e1e56a918678038699d" dependencies = [ "anyhow", "glob", @@ -4334,9 +4200,9 @@ dependencies = [ [[package]] name = "tauri-plugin-dialog" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b59fd750551b1066744ab956a1cd6b1ea3e1b3763b0b9153ac27a044d596426" +checksum = "bcaf6e5d6062423a0f711a23c2a573ccba222b6a16a9322d8499928f27e41376" dependencies = [ "log", "raw-window-handle", @@ -4352,9 +4218,9 @@ dependencies = [ [[package]] name = "tauri-plugin-fs" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a1edf18000f02903a7c2e5997fb89aca455ecbc0acc15c6535afbb883be223" +checksum = "88371e340ad2f07409a3b68294abe73f20bc9c1bc1b631a31dc37a3d0161f682" dependencies = [ "anyhow", "dunce", @@ -4390,10 +4256,11 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e758a405ab39e25f4d1235c5f06fe563f44b01ee18bbe38ddec5356d4f581908" +checksum = "00ada7ac2f9276f09b8c3afffd3215fd5d9bff23c22df8a7c70e7ef67cacd532" dependencies = [ + "cookie", "dpi", "gtk", "http", @@ -4409,9 +4276,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.4.1" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2beb90decade4c71e8b09c9e4a9245837a8a97693f945b77e32baf13f51fec" +checksum = "cf2e5842c57e154af43a20a49c7efee0ce2578c20b4c2bdf266852b422d2e421" dependencies = [ "gtk", "http", @@ -4436,10 +4303,11 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "107a959dbd5ff53d89a98f6f2e3e987c611334141a43630caae1d80e79446dd6" +checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447" dependencies = [ + "anyhow", "brotli", "cargo_metadata", "ctor", @@ -4483,13 +4351,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.18.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.2", "once_cell", "rustix", "windows-sys 0.59.0", @@ -4554,9 +4421,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.39" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa 1.0.15", @@ -4569,15 +4436,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -4610,9 +4477,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.1" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -4637,16 +4504,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "tokio-rustls" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" -dependencies = [ - "rustls", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.17" @@ -4698,7 +4555,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "toml_datetime", "winnow 0.5.40", ] @@ -4709,7 +4566,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "toml_datetime", "winnow 0.5.40", ] @@ -4720,11 +4577,11 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.4", + "winnow 0.7.6", ] [[package]] @@ -4911,12 +4768,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "url" version = "2.5.4" @@ -4961,11 +4812,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.15.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", "serde", ] @@ -5040,9 +4891,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -5147,16 +4998,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "webkit2gtk" version = "2.0.1" @@ -5201,15 +5042,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "webpki-roots" -version = "0.26.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "webview2-com" version = "0.36.0" @@ -5220,7 +5052,7 @@ dependencies = [ "webview2-com-sys", "windows", "windows-core 0.60.1", - "windows-implement", + "windows-implement 0.59.0", "windows-interface", ] @@ -5248,9 +5080,9 @@ dependencies = [ [[package]] name = "whoami" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ "redox_syscall", "wasite", @@ -5326,24 +5158,28 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.52.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" dependencies = [ - "windows-targets 0.52.6", + "windows-implement 0.59.0", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.3.1", ] [[package]] name = "windows-core" -version = "0.60.1" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement", + "windows-implement 0.60.0", "windows-interface", "windows-link", "windows-result", - "windows-strings", + "windows-strings 0.4.0", ] [[package]] @@ -5367,11 +5203,22 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "windows-interface" -version = "0.59.0" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", @@ -5380,9 +5227,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-numerics" @@ -5401,15 +5248,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", - "windows-strings", + "windows-strings 0.3.1", "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] @@ -5423,6 +5270,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -5523,9 +5379,9 @@ dependencies = [ [[package]] name = "windows-version" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bfbcc4996dd183ff1376a20ade1242da0d2dcaff83cc76710a588d24fd4c5db" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" dependencies = [ "windows-link", ] @@ -5721,9 +5577,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] @@ -5740,9 +5596,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.9.0", ] @@ -5761,9 +5617,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.50.4" +version = "0.50.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804a7d1613bd699beccaa60f3b3c679acee21cebba1945a693f5eab95c08d1fa" +checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730" dependencies = [ "base64 0.22.1", "block2 0.6.0", @@ -5881,7 +5737,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow 0.7.4", + "winnow 0.7.6", "xdg-home", "zbus_macros", "zbus_names", @@ -5911,24 +5767,24 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.4", + "winnow 0.7.6", "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", @@ -5995,7 +5851,7 @@ dependencies = [ "serde", "static_assertions", "url", - "winnow 0.7.4", + "winnow 0.7.6", "zvariant_derive", "zvariant_utils", ] @@ -6024,5 +5880,5 @@ dependencies = [ "serde", "static_assertions", "syn 2.0.100", - "winnow 0.7.4", + "winnow 0.7.6", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2da748f..c4aa586 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "FileFlow" -version = "1.0.1" +version = "1.0.2" description = "An app to manipulate DBMS tables" authors = ["Maxime-Cllt"] edition = "2021" diff --git a/src-tauri/src/fileflow/action/actions.rs b/src-tauri/src/fileflow/action/actions.rs index 32976bf..6bb16ea 100644 --- a/src-tauri/src/fileflow/action/actions.rs +++ b/src-tauri/src/fileflow/action/actions.rs @@ -1,12 +1,12 @@ -use crate::fileflow::action::insertion_mode::{fast_insert, optimized_insert}; +use crate::fileflow::action::database_command::{fast_insert, optimized_insert}; use crate::fileflow::database::connection::Connection; use crate::fileflow::enumeration::insertion_type::InsertionType; use crate::fileflow::stuct::insert_config::InsertConfig; use crate::fileflow::stuct::save_config::SaveConfig; use crate::fileflow::utils::constants::DATABASE_CONFIG_FILE; -use crate::fileflow::utils::fileflowlib::{ - find_separator, get_all_saved_configs, get_formated_column_names, read_first_line, save_config, -}; +use crate::fileflow::utils::csv_utils::{find_separator, read_first_line}; +use crate::fileflow::utils::fileflowlib::{get_all_saved_configs, save_config}; +use crate::fileflow::utils::string_formater::{get_formated_column_names, sanitize_column}; use csv::{Reader, ReaderBuilder}; use std::fs::{File, Metadata}; use std::sync::Arc; @@ -20,57 +20,76 @@ pub struct DatabaseState(pub Mutex>); pub async fn insert_csv_data( state: State<'_, Arc>, csv: InsertConfig, -) -> Result { +) -> Result { let conn_guard = state.0.lock().await; if conn_guard.is_none() { - eprint!("Error: Connection is not established"); - return Err(false); + return Err("Error: Connection is not established".into()); } let connection: &Connection = conn_guard.as_ref().unwrap(); + let mut total_lines: u64 = 0; // Counter for the total number of lines inserted + let start: Instant = Instant::now(); // Timer for the insertion process + let file: File = File::open(&csv.file_path).expect("Failed to open file"); - let first_line: String = read_first_line(&csv.file_path).expect("Failed to read first line"); - let separator: char = find_separator(&first_line).expect("Failed to find separator"); - let start: Instant = Instant::now(); + let first_line: String = read_first_line(&csv.file_path).expect("Failed to read first line"); // Read the first line of the file to detect the separator + let separator: char = find_separator(&first_line).expect("Failed to find separator"); // Separator detection of the file let final_columns_name: Vec = get_formated_column_names( - first_line + &first_line .split(separator) - .map(|s| s.replace("\"", "")) - .collect(), + .map(|s| sanitize_column(s)) + .collect::>(), ); let mut reader: Reader = ReaderBuilder::new() - .delimiter(separator as u8) + .delimiter(u8::try_from(separator).unwrap()) .has_headers(true) .from_reader(file); - let line_count: u32 = match csv.mode { - InsertionType::Fast => fast_insert( - connection, - &mut reader, - &final_columns_name, - &csv.table_name, - &csv.db_driver, - ) - .await - .map_err(|_| false)?, - InsertionType::Optimized => optimized_insert( - connection, - &mut reader, - &final_columns_name, - &csv.table_name, - &csv.db_driver, - ) - .await - .map_err(|_| false)?, + match csv.mode { + InsertionType::Fast => { + match fast_insert( + connection, + &mut reader, + &final_columns_name, + &csv.table_name, + &csv.db_driver, + ) + .await + { + Ok(lines) => { + total_lines += u64::from(lines); + } + Err(e) => { + return Err(format!("Error: Failed to insert data: {}", e.to_string())); + } + } + } + InsertionType::Optimized => { + match optimized_insert( + connection, + &mut reader, + &final_columns_name, + &csv.table_name, + &csv.db_driver, + ) + .await + { + Ok(lines) => { + total_lines += u64::from(lines); + } + Err(e) => { + return Err(format!("Error: Failed to insert data: {}", e.to_string())); + } + } + } }; Ok(format!( - "Data inserted successfully in {:.2?}, {line_count} rows inserted in table {}", + "Inserted {total_lines} lines into {} tables in {:?} seconds", + csv.table_name, start.elapsed(), - csv.table_name )) } @@ -85,10 +104,9 @@ pub async fn save_database_config(save: SaveConfig) -> Result { } existing_configs.push(save); - match save_config(&existing_configs, DATABASE_CONFIG_FILE) { - Ok(_) => Ok(true), - Err(_) => Err(false), - } + save_config(&existing_configs, DATABASE_CONFIG_FILE) + .map(|_| true) + .map_err(|_| false) } #[command] @@ -96,8 +114,7 @@ pub async fn get_all_database_configs_name() -> Result { let configs: Vec = get_all_saved_configs(DATABASE_CONFIG_FILE); // Get all saved configs let configs_names: Vec = configs.iter().map(|c| c.config_name.clone()).collect(); // Get only the names let configs_json: String = match serde_json::to_string(&configs_names) { - // Convert to json string - Ok(json) => json, + Ok(json) => json, // Convert to json string Err(_) => return Err(false), }; Ok(configs_json) @@ -110,8 +127,7 @@ pub async fn load_database_config_by_name(name: String) -> Result // Find the config with the given name if config.config_name == name { return match serde_json::to_string(&config) { - // Convert to json string - Ok(json) => Ok(json), + Ok(json) => Ok(json), // Convert to json string Err(_) => Err(false), }; } @@ -135,13 +151,12 @@ pub async fn delete_database_config(name: String) -> Result { } if !found { - Err(false)?; + return Err(false); } - match save_config(&new_configs, DATABASE_CONFIG_FILE) { - Ok(_) => Ok(true), - Err(_) => Err(false), - } + save_config(&new_configs, DATABASE_CONFIG_FILE) + .map(|_| true) + .map_err(|_| false) } #[command] diff --git a/src-tauri/src/fileflow/action/database_actions.rs b/src-tauri/src/fileflow/action/database_actions.rs deleted file mode 100644 index 01f22c4..0000000 --- a/src-tauri/src/fileflow/action/database_actions.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::fileflow::action::actions::DatabaseState; -use crate::fileflow::database::connection::{Connection, QueryResult}; -use crate::fileflow::stuct::combo_item::ComboItem; -use crate::fileflow::stuct::db_config::DbConfig; -use crate::fileflow::stuct::download_config::DownloadConfig; -use crate::fileflow::utils::sql::{export_table, build_query_all_tables}; -use serde_json::{json, Value}; -use sqlx::Row; -use std::sync::Arc; -use std::time::Instant; -use tauri::{command, State}; - -#[command] -pub async fn connect_to_database( - state: State<'_, Arc>, - config: DbConfig, -) -> Result { - let mut conn_guard = state.0.lock().await; - - if conn_guard.is_some() { - return Err("Already connected to the database.".into()); - } - - match Connection::connect(&config).await { - Ok(connection) => { - *conn_guard = Some(connection); - Ok(true) - } - Err(err) => Err(format!("Failed to connect to the database: {err}")), - } -} - -#[command] -pub async fn disconnect_from_database( - state: State<'_, Arc>, -) -> Result { - let mut conn_guard = state.0.lock().await; - - if conn_guard.is_none() { - return Err("No active database connection to disconnect.".into()); - } - - let conn: Connection = match conn_guard.take() { - Some(conn) => conn, - None => return Err("No active database connection to disconnect.".into()), - }; - - conn.disconnect(); - - Ok(true) -} - -#[command] -pub async fn is_connected(state: State<'_, Arc>) -> Result { - let conn_guard = state.0.lock().await; - - if conn_guard.is_none() { - return Ok(String::new()); - } - - let connection: &Connection = conn_guard.as_ref().unwrap(); - let db_config: &DbConfig = connection.get_db_config(); - - // Return the database configuration as JSON string - match serde_json::to_string(db_config) { - Ok(json) => Ok(json), - Err(_) => Err(false), - } -} - -#[command] -pub async fn get_table_list(state: State<'_, Arc>) -> Result { - let conn_guard = state.0.lock().await; - - if conn_guard.is_none() { - return Err(false); - } - - let connection: &Connection = match conn_guard.as_ref() { - Some(conn) => conn, - None => return Err(false), - }; - - let db_config: &DbConfig = connection.get_db_config(); - let sql: &str = &build_query_all_tables(&db_config.db_driver, &db_config.db_name); - - let result: QueryResult = connection - .query_many_with_result(sql) - .await - .map_err(|_| false)?; - - let mut vec: Vec = Vec::new(); - - match result { - QueryResult::MySQL(rows) => { - for row in rows { - vec.push(ComboItem { - value: row.get("TABLE_NAME"), - label: row.get("TABLE_NAME"), - }); - } - } - QueryResult::Postgres(rows) => { - for row in rows { - vec.push(ComboItem { - value: row.get("table_name"), - label: row.get("table_name"), - }); - } - } - QueryResult::SQLite(rows) => { - for row in rows { - vec.push(ComboItem { - value: row.get("name"), - label: row.get("name"), - }); - } - } - } - - Ok(json!(vec)) -} - -#[command] -pub async fn download_table( - config: DownloadConfig, - state: State<'_, Arc>, -) -> Result { - let conn_guard = state.0.lock().await; - - let start: Instant = Instant::now(); - - if conn_guard.is_none() { - return Err("No active database connection.".into()); - } - - if config.table_name.is_empty() || config.location.is_empty() { - return Err("Some required fields are missing.".into()); - } - - let connection: &Connection = match conn_guard.as_ref() { - Some(conn) => conn, - None => return Err("No active database connection.".into()), - }; - - if let Err(err) = export_table(connection, config).await { - return Err(format!("Failed to export table: {err}")); - } - - Ok(format!( - "Table downloaded successfully in {:?} seconds.", - start.elapsed() - )) -} diff --git a/src-tauri/src/fileflow/action/database_command.rs b/src-tauri/src/fileflow/action/database_command.rs new file mode 100644 index 0000000..c631ec7 --- /dev/null +++ b/src-tauri/src/fileflow/action/database_command.rs @@ -0,0 +1,341 @@ +use crate::fileflow::action::actions::DatabaseState; +use crate::fileflow::database::connection::{Connection, QueryResult}; +use crate::fileflow::database::database_actions::{ + batch_insert, create_and_copy_final_table, drop_existing_tables, drop_table_if_exists, + execute_query, export_table, +}; +use crate::fileflow::database::sql_builder::{ + build_create_table_sql, build_prepared_statement_sql, build_query_all_tables, +}; +use crate::fileflow::enumeration::database_engine::DatabaseEngine; +use crate::fileflow::stuct::combo_item::ComboItem; +use crate::fileflow::stuct::db_config::DbConfig; +use crate::fileflow::stuct::download_config::DownloadConfig; +use crate::fileflow::utils::string_formater::{escaped_record, sanitize_value}; +use csv::{Reader, StringRecord}; +use serde_json::{json, Value}; +use sqlx::Row; +use std::collections::HashMap; +use std::fs::File; +use std::sync::Arc; +use std::time::Instant; +use tauri::{command, State}; + +#[command] +pub async fn connect_to_database( + state: State<'_, Arc>, + config: DbConfig, +) -> Result { + let mut conn_guard = state.0.lock().await; + + if conn_guard.is_some() { + return Err("Already connected to the database.".into()); + } + + match Connection::connect(&config).await { + Ok(connection) => { + *conn_guard = Some(connection); + Ok(true) + } + Err(err) => Err(format!("Failed to connect to the database: {err}")), + } +} + +#[command] +pub async fn disconnect_from_database( + state: State<'_, Arc>, +) -> Result { + let mut conn_guard = state.0.lock().await; + + if conn_guard.is_none() { + return Err("No active database connection to disconnect.".into()); + } + + let conn: Connection = match conn_guard.take() { + Some(conn) => conn, + None => return Err("No active database connection to disconnect.".into()), + }; + + conn.disconnect(); + + Ok(true) +} + +#[command] +pub async fn is_connected(state: State<'_, Arc>) -> Result { + let conn_guard = state.0.lock().await; + + if conn_guard.is_none() { + return Ok(String::new()); + } + + let connection: &Connection = conn_guard.as_ref().unwrap(); + let db_config: &DbConfig = connection.get_db_config(); + + // Return the database configuration as JSON string + match serde_json::to_string(db_config) { + Ok(json) => Ok(json), + Err(_) => Err(false), + } +} + +#[command] +pub async fn get_table_list(state: State<'_, Arc>) -> Result { + let conn_guard = state.0.lock().await; + + if conn_guard.is_none() { + return Err(false); + } + + let connection: &Connection = match conn_guard.as_ref() { + Some(conn) => conn, + None => return Err(false), + }; + + let db_config: &DbConfig = connection.get_db_config(); + let sql: &str = &build_query_all_tables(&db_config.db_driver, &db_config.db_name); + + let result: QueryResult = connection + .query_many_with_result(sql) + .await + .map_err(|_| false)?; + + let mut vec: Vec = Vec::new(); + + match result { + QueryResult::MySQL(rows) => { + for row in rows { + vec.push(ComboItem { + value: row.get("TABLE_NAME"), + label: row.get("TABLE_NAME"), + }); + } + } + QueryResult::Postgres(rows) => { + for row in rows { + vec.push(ComboItem { + value: row.get("table_name"), + label: row.get("table_name"), + }); + } + } + QueryResult::SQLite(rows) => { + for row in rows { + vec.push(ComboItem { + value: row.get("name"), + label: row.get("name"), + }); + } + } + } + + Ok(json!(vec)) +} + +#[command] +pub async fn download_table( + config: DownloadConfig, + state: State<'_, Arc>, +) -> Result { + let conn_guard = state.0.lock().await; + + let start: Instant = Instant::now(); + + if conn_guard.is_none() { + return Err("No active database connection.".into()); + } + + if config.table_name_list.is_empty() || config.location.is_empty() { + return Err("Some required fields are missing.".into()); + } + + let connection: &Connection = match conn_guard.as_ref() { + Some(conn) => conn, + None => return Err("No active database connection.".into()), + }; + + let mut exported_table: u32 = 0; + for table_name in config.table_name_list.iter() { + if let Err(err) = export_table(connection, &config, table_name).await { + println!("{err}"); + continue; + } + exported_table += 1; + } + + let response: String = if exported_table == 0 { + "No tables were exported.".into() + } else { + format!( + "Exported {exported_table} out of {} tables successfully in {:?} seconds.", + config.table_name_list.len(), + start.elapsed() + ) + }; + + Ok(response) +} + +/// Fast insert data the csv file into the database table +pub async fn fast_insert( + connection: &Connection, + reader: &mut Reader, + final_columns_name: &[String], + final_table_name: &str, + db_driver: &DatabaseEngine, +) -> Result { + // Drop the table if it exists + if let Err(err) = drop_table_if_exists(connection, db_driver, final_table_name).await { + eprintln!("Error: {err}"); + return Err(err); + } + + let build_create_table_statement: String = + build_create_table_sql(db_driver, final_table_name, final_columns_name); + + // Create the table + if let Err(err) = execute_query( + connection, + &build_create_table_statement, + "Failed to create table", + ) + .await + { + eprintln!("Error: {err}"); + return Err(err); + } + + const MAX_BATCH_SIZE: usize = 5_000; + let mut line_count: u32 = 0; + let mut batch: Vec = Vec::with_capacity(MAX_BATCH_SIZE); + + // Prepare the insert query + let insert_query_base: &str = + &build_prepared_statement_sql(db_driver, final_table_name, &final_columns_name); + + for result in reader.records() { + let values: String = match result { + Ok(record) => escaped_record(record), + Err(_) => continue, + }; + + batch.push(format!("({values})")); + + if batch.len() >= MAX_BATCH_SIZE { + line_count += insert_batch(connection, insert_query_base, &batch).await; + batch.clear(); + } + } + + // Insert the remaining records if any + line_count += insert_batch(connection, insert_query_base, &batch).await; + + Ok(line_count) +} + +/// Insert data into the database using the optimized table creation and insertion method +pub async fn optimized_insert( + connection: &Connection, + reader: &mut Reader, + final_columns_name: &[String], + final_table_name: &str, + db_driver: &DatabaseEngine, +) -> Result { + // Drop existing tables + let temporary_table_name: String = format!("{final_table_name}_temporary"); + drop_existing_tables( + connection, + &[&temporary_table_name, final_table_name], + db_driver, + ) + .await + .expect("Failed to drop existing tables"); + + // Create the temporary table + let create_temp_table_query: String = + build_create_table_sql(db_driver, &temporary_table_name, final_columns_name); + + execute_query( + connection, + &create_temp_table_query, + "Failed to create temporary table", + ) + .await + .expect("Failed to create temporary table query"); + + // Initialize variables + const MAX_BATCH_SIZE: usize = 5_000; + let insert_query_base: String = + build_prepared_statement_sql(db_driver, &temporary_table_name, &final_columns_name); + + let mut columns_size_map: HashMap<&str, usize> = final_columns_name + .iter() + .map(|col| (col.as_str(), 0)) + .collect(); // Initialize column size map for each column -> 0 (id,size) + let mut line_count: u32 = 0; + let mut batch: Vec = Vec::with_capacity(MAX_BATCH_SIZE); + + for result in reader.records() { + let record: StringRecord = match result { + Ok(record) => record, + Err(_) => continue, + }; + + let mut values: Vec = Vec::with_capacity(record.len()); + + for (i, value) in record.iter().enumerate() { + let sanitized_value: String = sanitize_value(value); + let max_length: &mut usize = columns_size_map + .get_mut(final_columns_name[i].as_str()) + .ok_or("Column name mismatch") + .expect("Column name mismatch"); + *max_length = (*max_length).max(sanitized_value.len() + 1); + values.push(format!("'{sanitized_value}'")); + } + + batch.push(format!("({})", values.join(", "))); + + if batch.len() >= MAX_BATCH_SIZE { + line_count += insert_batch(connection, &insert_query_base, &batch).await; + batch.clear(); + } + } + + // Insert remaining records + if !batch.is_empty() { + line_count += insert_batch(connection, &insert_query_base, &batch).await; + } + + // Create final table and copy data + create_and_copy_final_table( + connection, + db_driver, + final_table_name, + &temporary_table_name, + &columns_size_map, + final_columns_name, + ) + .await?; + + drop_table_if_exists(connection, db_driver, &temporary_table_name).await?; // Drop the temporary table + + Ok(line_count) +} + +/// Insert a batch of records into the database +async fn insert_batch(connection: &Connection, insert_query_base: &str, batch: &[String]) -> u32 { + match batch_insert( + connection, + insert_query_base, + batch, + "Failed to insert batch data", + ) + .await + { + Ok(_) => u32::try_from(batch.len()).unwrap_or(5_000), + Err(err) => { + eprintln!("Error inserting batch: {err}"); + 0 + } + } +} diff --git a/src-tauri/src/fileflow/action/insertion_mode.rs b/src-tauri/src/fileflow/action/insertion_mode.rs deleted file mode 100644 index b0cf01f..0000000 --- a/src-tauri/src/fileflow/action/insertion_mode.rs +++ /dev/null @@ -1,228 +0,0 @@ -use crate::fileflow::database::connection::Connection; -use crate::fileflow::enumeration::database_engine::DatabaseEngine; -use crate::fileflow::utils::fileflowlib::{escaped_values, sanitize_value}; -use crate::fileflow::utils::sql::{ - batch_insert, create_and_copy_final_table, drop_table_if_exists, execute_query, - get_create_statement, get_insert_into_statement, -}; -use csv::{Reader, StringRecord}; -use std::collections::HashMap; -use std::fs::File; - -/// Insert data into the database using the optimized table creation and insertion method -pub async fn optimized_insert( - connection: &Connection, - reader: &mut Reader, - final_columns_name: &[String], - final_table_name: &str, - db_driver: &DatabaseEngine, -) -> Result { - let temporary_table_name: &str = &format!("{final_table_name}_temporary"); - - // Drop existing tables - drop_existing_tables( - connection, - &[temporary_table_name, final_table_name], - db_driver, - ) - .await - .expect("Failed to drop existing tables"); - - // Create the temporary table - let create_temp_table_query: String = - get_create_statement(db_driver, temporary_table_name, final_columns_name) - .expect("Failed to create temporary table query"); - - execute_query( - connection, - &create_temp_table_query, - "Failed to create temporary table", - ) - .await - .expect("Failed to create temporary table query"); - - // Initialize variables - const MAX_BATCH_SIZE: usize = 4000; - let mut batch: Vec = Vec::with_capacity(MAX_BATCH_SIZE); - let mut columns_size_map: HashMap<&str, usize> = - initialize_columns_size_map(final_columns_name); - let insert_query_base: String = get_insert_into_statement( - db_driver, - temporary_table_name, - &final_columns_name.join(", "), - ) - .expect("Failed to insert into temporary table"); - let mut line_count: u32 = 0; - - // Read and process records from the CSV - for result in reader.records() { - let record: StringRecord = match result { - Ok(record) => record, - Err(_) => continue, - }; - - process_record( - &record, - &mut batch, - &mut columns_size_map, - final_columns_name, - ) - .expect("Failed to process record"); - - if batch.len() >= MAX_BATCH_SIZE { - line_count += insert_batch(connection, &insert_query_base, &batch).await?; - batch.clear(); - } - } - - // Insert remaining records - if !batch.is_empty() { - line_count += insert_batch(connection, &insert_query_base, &batch).await?; - } - - // Create final table and copy data - create_and_copy_final_table( - connection, - db_driver, - final_table_name, - temporary_table_name, - &columns_size_map, - final_columns_name, - ) - .await?; - - // Drop the temporary table - drop_table_if_exists(connection, db_driver, temporary_table_name).await?; - - Ok(line_count) -} - -/// Drop a list of tables if they exist -async fn drop_existing_tables( - connection: &Connection, - table_names: &[&str], - db_driver: &DatabaseEngine, -) -> Result<(), String> { - for table_name in table_names.iter() { - if let Err(e) = drop_table_if_exists(connection, db_driver, table_name).await { - eprintln!("Error: {e}"); - } - } - Ok(()) -} - -/// Initialize the column size map with default sizes -fn initialize_columns_size_map(final_columns_name: &[String]) -> HashMap<&str, usize> { - final_columns_name - .iter() - .map(|col| (col.as_str(), 0)) - .collect() -} - -/// Sanitize and process a CSV record, updating batch and column size map -fn process_record( - record: &StringRecord, - batch: &mut Vec, - columns_size_map: &mut HashMap<&str, usize>, - final_columns_name: &[String], -) -> Result<(), String> { - let mut values: Vec = Vec::with_capacity(record.len()); - - for (i, value) in record.iter().enumerate() { - let sanitized_value: String = sanitize_value(value); - let max_length: &mut usize = columns_size_map - .get_mut(final_columns_name[i].as_str()) - .ok_or("Column name mismatch") - .expect("Column name mismatch"); - *max_length = (*max_length).max(sanitized_value.len() + 1); - values.push(format!("'{sanitized_value}'")); - } - - batch.push(format!("({})", values.join(", "))); - Ok(()) -} - -/// Insert a batch of records into the database -async fn insert_batch( - connection: &Connection, - insert_query_base: &str, - batch: &[String], -) -> Result { - batch_insert( - connection, - insert_query_base, - batch, - "Failed to insert batch data", - ) - .await - .map(|_| u32::try_from(batch.len()).expect("Failed to convert batch len")) -} - -/// Fast insert data the csv file into the database table -pub async fn fast_insert( - connection: &Connection, - reader: &mut Reader, - final_columns_name: &[String], - final_table_name: &str, - db_driver: &DatabaseEngine, -) -> Result { - // Drop the table if it exists - if let Err(err) = drop_table_if_exists(connection, db_driver, final_table_name).await { - eprintln!("Error: {err}"); - return Err(err); - } - - // Create the table - if let Err(err) = execute_query( - connection, - get_create_statement(db_driver, final_table_name, final_columns_name)?.as_str(), - "Failed to create table {final_table_name}", - ) - .await - { - eprintln!("Error: {err}"); - return Err(err); - } - - const MAX_BATCH_SIZE: usize = 10_000; - let columns: &str = &final_columns_name.join(", "); - let mut line_count: u32 = 0; - let mut batch: Vec = Vec::with_capacity(MAX_BATCH_SIZE); - - // Prepare the insert query - let insert_query_base: &str = &get_insert_into_statement(db_driver, final_table_name, columns) - .expect("Failed to insert batch data"); - - for result in reader.records() { - let record: StringRecord = result.unwrap(); - let values: String = escaped_values(record); - batch.push(format!("({values})")); - - if batch.len() == MAX_BATCH_SIZE { - batch_insert( - connection, - insert_query_base, - &batch, - "Failed to insert batch data", - ) - .await - .expect("Failed to insert batch data"); - line_count += u32::try_from(batch.len()).expect("Failed to convert batch len"); - batch.clear(); - } - } - - // Insert the remaining records if any - batch_insert( - connection, - insert_query_base, - &batch, - "Failed to insert batch data", - ) - .await - .map_err(|e| format!("Failed to insert batch data: {e}"))?; - - line_count += u32::try_from(batch.len()).expect("Failed to convert batch len"); - - Ok(line_count) -} diff --git a/src-tauri/src/fileflow/action/mod.rs b/src-tauri/src/fileflow/action/mod.rs index f693f22..339c2ac 100644 --- a/src-tauri/src/fileflow/action/mod.rs +++ b/src-tauri/src/fileflow/action/mod.rs @@ -1,3 +1,2 @@ pub mod actions; -pub mod database_actions; -pub mod insertion_mode; +pub mod database_command; \ No newline at end of file diff --git a/src-tauri/src/fileflow/database/connection.rs b/src-tauri/src/fileflow/database/connection.rs index 67cccb5..85ff25e 100644 --- a/src-tauri/src/fileflow/database/connection.rs +++ b/src-tauri/src/fileflow/database/connection.rs @@ -21,7 +21,7 @@ pub struct Connection { impl Connection { pub async fn connect(config: &DbConfig) -> Result { - let connection_str: String = Self::get_connection_url(config)?; + let connection_str: String = Self::get_connection_url(config); let connection_enum: ConnectionEnum = match config.db_driver { DatabaseEngine::Postgres => { @@ -81,9 +81,9 @@ impl Connection { Ok(()) } - pub(crate) fn get_connection_url(config: &DbConfig) -> Result { + pub(crate) fn get_connection_url(config: &DbConfig) -> String { match config.db_driver { - DatabaseEngine::Postgres => Ok(format!( + DatabaseEngine::Postgres => format!( "postgres://{}{}@{}:{}/{}", config.username, if config.password.is_empty() { @@ -94,8 +94,8 @@ impl Connection { config.db_host, config.port, config.db_name - )), - DatabaseEngine::MySQL | DatabaseEngine::MariaDB => Ok(format!( + ), + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => format!( "mysql://{}{}@{}:{}/{}", config.username, if config.password.is_empty() { @@ -106,8 +106,8 @@ impl Connection { config.db_host, config.port, config.db_name - )), - DatabaseEngine::SQLite => Ok(config.sqlite_file_path.clone()), + ), + DatabaseEngine::SQLite => config.sqlite_file_path.clone(), } } diff --git a/src-tauri/src/fileflow/utils/sql.rs b/src-tauri/src/fileflow/database/database_actions.rs similarity index 52% rename from src-tauri/src/fileflow/utils/sql.rs rename to src-tauri/src/fileflow/database/database_actions.rs index 226d07a..d2a0749 100644 --- a/src-tauri/src/fileflow/utils/sql.rs +++ b/src-tauri/src/fileflow/database/database_actions.rs @@ -1,4 +1,7 @@ use crate::fileflow::database::connection::{Connection, QueryResult}; +use crate::fileflow::database::sql_builder::{ + build_copy_table_sql, build_create_with_fixed_size_sql, build_drop_statement_sql, +}; use crate::fileflow::enumeration::database_engine::DatabaseEngine; use crate::fileflow::enumeration::separator::SeparatorType; use crate::fileflow::stuct::download_config::DownloadConfig; @@ -7,240 +10,16 @@ use sqlx::{Column, Row}; use std::collections::HashMap; use std::fs::File; -/// This function is used to generate the DROP TABLE statement for different database drivers. -pub fn get_drop_statement( - db_driver: &DatabaseEngine, - final_table_name: &str, -) -> Result { - match db_driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => { - Ok(format!("DROP TABLE IF EXISTS \"{final_table_name}\"")) - } - DatabaseEngine::MariaDB | DatabaseEngine::MySQL => { - Ok(format!("DROP TABLE IF EXISTS `{final_table_name}`")) - } - } -} - -/// This function is used to generate the INSERT INTO statement for different database drivers. -pub fn get_insert_into_statement( - db_driver: &DatabaseEngine, - final_table_name: &str, - columns: &str, -) -> Result { - let quote: char = match db_driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => '\"', - DatabaseEngine::MariaDB | DatabaseEngine::MySQL => '`', - }; - - Ok(format!( - "INSERT INTO {quote}{final_table_name}{quote} ({columns}) VALUES " - )) -} - -/// This function is used to generate the COPY statement for different database drivers. -pub fn get_copy_temp_to_final_table( - db_driver: &DatabaseEngine, - temporary_table_name: &str, - final_table_name: &str, -) -> Result { - let quote: char = match db_driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => '\"', - DatabaseEngine::MySQL | DatabaseEngine::MariaDB => '`', - }; - Ok(format!( - "INSERT INTO {quote}{final_table_name}{quote} SELECT * FROM {quote}{temporary_table_name}{quote}" - )) -} - -/// This function is used to generate the CREATE TABLE statement with fixed size for each column for different database drivers. -pub fn get_create_statement_with_fixed_size( - driver: &DatabaseEngine, - final_table_name: &str, - map_column_max_length: &HashMap<&str, usize>, - snake_case_headers: &[String], -) -> Result { - // Constants - const MAX_VARCHAR_LENGTH: usize = 255; - const TEXT_TYPE: &str = "TEXT"; - const VARCHAR_TYPE: &str = "VARCHAR"; - - // Determine database-specific formatting - let quote = match driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => '"', - DatabaseEngine::MySQL | DatabaseEngine::MariaDB => '`', - }; - - let mut columns: Vec = Vec::with_capacity(snake_case_headers.len()); - let mut total_length: usize = 0; - - // Build column definitions - for header in snake_case_headers { - let max_length = map_column_max_length - .get(header.as_str()) - .copied() - .unwrap_or(MAX_VARCHAR_LENGTH); - - // Determine column type - let type_str = if max_length <= MAX_VARCHAR_LENGTH { - format!("{}({})", VARCHAR_TYPE, max_length) - } else { - TEXT_TYPE.to_string() - }; - - // Format column definition - let column = format!("{0}{1}{0} {2}", quote, header, type_str); - total_length += column.len(); - columns.push(column); - } - - // Pre-calculate final string capacity - let table_quoted: String = format!("{0}{1}{0}", quote, final_table_name); - let mut result: String = String::with_capacity( - 15 + // "CREATE TABLE ();" - table_quoted.len() + - total_length + - columns.len() * 2, // ", " separators - ); - - // Build the final SQL statement - result.push_str("CREATE TABLE "); - result.push_str(&table_quoted); - result.push_str(" ("); - result.push_str(&columns.join(", ")); - result.push_str(");"); - - Ok(result) -} - -/// This function is used to generate the CREATE TABLE statement for different database drivers. -pub fn get_create_statement( - driver: &DatabaseEngine, - final_table_name: &str, - snake_case_headers: &[String], -) -> Result { - match driver { - DatabaseEngine::SQLite | DatabaseEngine::Postgres => Ok(format!( - "CREATE TABLE \"{final_table_name}\" ({})", - snake_case_headers - .iter() - .map(|h| format!("{h} TEXT")) - .collect::>() - .join(", ") - )), - DatabaseEngine::MariaDB | DatabaseEngine::MySQL => Ok(format!( - "CREATE TABLE `{final_table_name}` ({})", - snake_case_headers - .iter() - .map(|h| format!("{h} TEXT")) - .collect::>() - .join(", ") - )), - } -} - -/// Helper function to drop a table if it exists -pub async fn drop_table_if_exists( - connection: &Connection, - db_driver: &DatabaseEngine, - table_name: &str, -) -> Result<(), String> { - let drop_query: &str = &get_drop_statement(db_driver, table_name)?; - execute_query( - connection, - drop_query, - &format!("Failed to drop table '{table_name}'"), - ) - .await -} - -/// Helper function to execute a query and handle errors -pub async fn execute_query( - connection: &Connection, - query: &str, - context: &str, -) -> Result<(), String> { - connection - .query(query) - .await - .map_err(|err| format!("{context}: {err}")) -} - -/// Helper function to batch-insert records into a table -pub async fn batch_insert( - connection: &Connection, - insert_query_base: &str, - batch: &[String], - context: &str, -) -> Result<(), String> { - if batch.is_empty() { - return Ok(()); - } - let insert_query: String = format!("{insert_query_base}{}", batch.join(", ")); - execute_query(connection, &insert_query, context).await -} - -/// Create the final table and copy data from the temporary table -pub async fn create_and_copy_final_table( - connection: &Connection, - db_driver: &DatabaseEngine, - final_table_name: &str, - temporary_table_name: &str, - columns_size_map: &HashMap<&str, usize>, - final_columns_name: &[String], -) -> Result<(), String> { - let create_final_table_query: String = get_create_statement_with_fixed_size( - db_driver, - final_table_name, - columns_size_map, - final_columns_name, - )?; - execute_query( - connection, - &create_final_table_query, - "Failed to create final table", - ) - .await?; - - let copy_data_query: String = - get_copy_temp_to_final_table(db_driver, temporary_table_name, final_table_name)?; - - execute_query( - connection, - ©_data_query, - "Failed to copy data to final table", - ) - .await?; - - Ok(()) -} - -/// Get the query to fetch all tables from the database for different drivers -pub fn build_query_all_tables(driver: &DatabaseEngine, schema: &str) -> String { - let query: String = match driver { - &DatabaseEngine::MySQL | &DatabaseEngine::MariaDB => format!( - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{schema}';" - ), - &DatabaseEngine::Postgres => { - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'public';".into() - } - &DatabaseEngine::SQLite => "SELECT name FROM sqlite_master WHERE type='table';".into(), - }; - query -} - /// Exports a table’s data into a CSV file. It uses offset/LIMIT pagination to retrieve data in batches pub async fn export_table( connection: &Connection, - download_config: DownloadConfig, + download_config: &DownloadConfig, + table_name: &str, ) -> Result<(), Box> { const LIMIT: i32 = 5000; let mut offset: i32 = 0; let mut header_written: bool = false; - let file_path: String = format!( - "{}/{}_export.csv", - download_config.location, download_config.table_name - ); + let file_path: String = format!("{}/{table_name}_export.csv", download_config.location); let separator: u8 = match download_config.separator { SeparatorType::Comma => b',', @@ -254,10 +33,10 @@ pub async fn export_table( .from_path(&file_path) .expect("Failed to create CSV writer"); - let base_sql: String = format!("SELECT * FROM {}", download_config.table_name); + let base_sql: String = format!("SELECT * FROM {table_name}"); loop { - let sql_query: String = format!("{} LIMIT {} OFFSET {}", base_sql, LIMIT, offset); + let sql_query: String = format!("{base_sql} LIMIT {LIMIT} OFFSET {offset}"); let query_result: QueryResult = connection.query_many_with_result(&sql_query).await?; let (columns, rows): (Vec, Vec>) = match query_result { @@ -351,3 +130,93 @@ pub async fn export_table( wtr.flush().expect("Failed to flush CSV writer"); Ok(()) } + +/// Helper function to drop a table if it exists +pub async fn drop_table_if_exists( + connection: &Connection, + db_driver: &DatabaseEngine, + table_name: &str, +) -> Result<(), String> { + let drop_query: &str = &build_drop_statement_sql(db_driver, table_name)?; + execute_query( + connection, + drop_query, + &format!("Failed to drop table '{table_name}'"), + ) + .await +} + +/// Helper function to execute a query and handle errors +pub async fn execute_query( + connection: &Connection, + query: &str, + context: &str, +) -> Result<(), String> { + connection + .query(query) + .await + .map_err(|err| format!("{context}: {err}")) +} + +/// Helper function to batch-insert records into a table +pub async fn batch_insert( + connection: &Connection, + insert_query_base: &str, + batch: &[String], + context: &str, +) -> Result<(), String> { + if batch.is_empty() { + return Ok(()); + } + let insert_query: String = format!("{insert_query_base}{}", batch.join(", ")); + execute_query(connection, &insert_query, context).await +} + +/// Create the final table and copy data from the temporary table +pub async fn create_and_copy_final_table( + connection: &Connection, + db_driver: &DatabaseEngine, + final_table_name: &str, + temporary_table_name: &str, + columns_size_map: &HashMap<&str, usize>, + final_columns_name: &[String], +) -> Result<(), String> { + let create_final_table_query: String = build_create_with_fixed_size_sql( + db_driver, + final_table_name, + columns_size_map, + final_columns_name, + ); + execute_query( + connection, + &create_final_table_query, + "Failed to create final table", + ) + .await?; + + let copy_data_query: String = + build_copy_table_sql(db_driver, temporary_table_name, final_table_name); + + execute_query( + connection, + ©_data_query, + "Failed to copy data to final table", + ) + .await?; + + Ok(()) +} + +/// Drop a list of tables if they exist +pub async fn drop_existing_tables( + connection: &Connection, + table_names: &[&str], + db_driver: &DatabaseEngine, +) -> Result<(), String> { + for table_name in table_names.iter() { + if let Err(e) = drop_table_if_exists(connection, db_driver, table_name).await { + eprintln!("Error: {e}"); + } + } + Ok(()) +} diff --git a/src-tauri/src/fileflow/database/mod.rs b/src-tauri/src/fileflow/database/mod.rs index b3b606b..bc2f05a 100644 --- a/src-tauri/src/fileflow/database/mod.rs +++ b/src-tauri/src/fileflow/database/mod.rs @@ -1 +1,3 @@ pub mod connection; +pub mod database_actions; +pub mod sql_builder; diff --git a/src-tauri/src/fileflow/database/sql_builder.rs b/src-tauri/src/fileflow/database/sql_builder.rs new file mode 100644 index 0000000..ea10047 --- /dev/null +++ b/src-tauri/src/fileflow/database/sql_builder.rs @@ -0,0 +1,392 @@ +use crate::fileflow::enumeration::database_engine::DatabaseEngine; +use std::collections::HashMap; + +/// This function is used to generate the DROP TABLE statement for different database drivers. +pub fn build_drop_statement_sql( + db_driver: &DatabaseEngine, + final_table_name: &str, +) -> Result { + match db_driver { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => { + Ok(format!("DROP TABLE IF EXISTS \"{final_table_name}\"")) + } + DatabaseEngine::MariaDB | DatabaseEngine::MySQL => { + Ok(format!("DROP TABLE IF EXISTS `{final_table_name}`")) + } + } +} + +/// This function is used to generate the INSERT INTO statement for different database drivers. +pub fn build_prepared_statement_sql( + db_driver: &DatabaseEngine, + table_name: &str, + columns: &[String], +) -> String { + let quote: char = match db_driver { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => '\"', + DatabaseEngine::MariaDB | DatabaseEngine::MySQL => '`', + }; + let mut query = format!("INSERT INTO {quote}{table_name}{quote} ("); + for column in columns.iter() { + if column.is_empty() { + continue; + } + query.push_str(&format!("{quote}{column}{quote}")); + if column != columns.last().unwrap() { + query.push_str(", "); + } + } + query.push_str(") VALUES "); + query +} + +/// This function is used to generate the COPY statement for different database drivers. +pub fn build_copy_table_sql( + db_driver: &DatabaseEngine, + temporary_table_name: &str, + final_table_name: &str, +) -> String { + let quote: char = match db_driver { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => '\"', + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => '`', + }; + format!( + "INSERT INTO {quote}{final_table_name}{quote} SELECT * FROM {quote}{temporary_table_name}{quote}" + ) +} + +/// This function is used to generate the CREATE TABLE statement with fixed size for each column for different database drivers. +pub fn build_create_with_fixed_size_sql( + driver: &DatabaseEngine, + final_table_name: &str, + map_column_max_length: &HashMap<&str, usize>, + snake_case_headers: &[String], +) -> String { + // Constants + const MAX_VARCHAR_LENGTH: usize = 255; + const TEXT_TYPE: &str = "TEXT"; + const VARCHAR_TYPE: &str = "VARCHAR"; + + // Determine database-specific formatting + let quote: char = match driver { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => '"', + DatabaseEngine::MySQL | DatabaseEngine::MariaDB => '`', + }; + + let mut columns: Vec = Vec::with_capacity(snake_case_headers.len()); + let mut total_length: usize = 0; + + // Build column definitions + for header in snake_case_headers { + let max_length = map_column_max_length + .get(header.as_str()) + .copied() + .unwrap_or(MAX_VARCHAR_LENGTH); + + // Determine column type + let type_str: String = if max_length <= MAX_VARCHAR_LENGTH { + format!("{VARCHAR_TYPE}({max_length})") // VARCHAR(n) + } else { + TEXT_TYPE.into() + }; + + // Format column definition + let column: String = format!("{0}{1}{0} {2}", quote, header, type_str); + total_length += column.len(); + columns.push(column); + } + + // Pre-calculate final string capacity + let table_quoted: String = format!("{0}{1}{0}", quote, final_table_name); + let mut result: String = String::with_capacity( + 15 + // "CREATE TABLE ();" + table_quoted.len() + + total_length + + columns.len() * 2, // ", " separators + ); + + // Build the final SQL statement + result.push_str("CREATE TABLE "); + result.push_str(&table_quoted); + result.push_str(" ("); + result.push_str(&columns.join(", ")); + result.push_str(");"); + + result +} + +/// This function is used to generate the CREATE TABLE statement for different database drivers. +pub fn build_create_table_sql( + driver: &DatabaseEngine, + final_table_name: &str, + snake_case_headers: &[String], +) -> String { + match driver { + DatabaseEngine::SQLite | DatabaseEngine::Postgres => format!( + "CREATE TABLE \"{final_table_name}\" ({})", + snake_case_headers + .iter() + .map(|h| format!("{h} TEXT")) + .collect::>() + .join(", ") + ), + DatabaseEngine::MariaDB | DatabaseEngine::MySQL => format!( + "CREATE TABLE `{final_table_name}` ({})", + snake_case_headers + .iter() + .map(|h| format!("`{h}` TEXT")) + .collect::>() + .join(", ") + ), + } +} + +/// Get the query to fetch all tables from the database for different drivers +pub fn build_query_all_tables(driver: &DatabaseEngine, schema: &str) -> String { + let query: String = match driver { + &DatabaseEngine::MySQL | &DatabaseEngine::MariaDB => format!( + "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{schema}';" + ), + &DatabaseEngine::Postgres => { + "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'public';".into() + } + &DatabaseEngine::SQLite => "SELECT name FROM sqlite_master WHERE type='table';".into(), + }; + query +} + +#[cfg(test)] +mod test { + use crate::fileflow::database::sql_builder::{ + build_create_table_sql, build_create_with_fixed_size_sql, build_drop_statement_sql, + build_prepared_statement_sql, build_query_all_tables, + }; + use crate::fileflow::enumeration::database_engine::DatabaseEngine; + use std::collections::HashMap; + + #[tokio::test] + async fn test_get_drop_statement() { + assert_eq!( + build_drop_statement_sql(&DatabaseEngine::SQLite, "table_name").unwrap(), + "DROP TABLE IF EXISTS \"table_name\"" + ); + assert_eq!( + build_drop_statement_sql(&DatabaseEngine::MySQL, "table_name").unwrap(), + "DROP TABLE IF EXISTS `table_name`" + ); + assert_eq!( + build_drop_statement_sql(&DatabaseEngine::Postgres, "table_name").unwrap(), + "DROP TABLE IF EXISTS \"table_name\"" + ); + assert_eq!( + build_drop_statement_sql(&DatabaseEngine::SQLite, "").unwrap(), + "DROP TABLE IF EXISTS \"\"" + ); + assert_eq!( + build_drop_statement_sql(&DatabaseEngine::MySQL, "").unwrap(), + "DROP TABLE IF EXISTS ``" + ); + assert_eq!( + build_drop_statement_sql(&DatabaseEngine::Postgres, "").unwrap(), + "DROP TABLE IF EXISTS \"\"" + ); + } + + #[tokio::test] + async fn test_get_insert_into_statement() { + assert_eq!( + build_prepared_statement_sql( + &DatabaseEngine::SQLite, + "table_name", + &["columns".into()] + ), + "INSERT INTO \"table_name\" (\"columns\") VALUES " + ); + assert_eq!( + build_prepared_statement_sql(&DatabaseEngine::MySQL, "table_name", &["columns".into()]), + "INSERT INTO `table_name` (`columns`) VALUES " + ); + assert_eq!( + build_prepared_statement_sql( + &DatabaseEngine::Postgres, + "table_name", + &["columns".into()] + ), + "INSERT INTO \"table_name\" (\"columns\") VALUES " + ); + + assert_eq!( + build_prepared_statement_sql(&DatabaseEngine::SQLite, "table_name", &["".into()]), + "INSERT INTO \"table_name\" () VALUES " + ); + assert_eq!( + build_prepared_statement_sql(&DatabaseEngine::MySQL, "table_name", &["".into()]), + "INSERT INTO `table_name` () VALUES " + ); + assert_eq!( + build_prepared_statement_sql(&DatabaseEngine::Postgres, "table_name", &["".into()]), + "INSERT INTO \"table_name\" () VALUES " + ); + + assert_eq!( + build_prepared_statement_sql( + &DatabaseEngine::SQLite, + "table_name", + &["header1".into(), "header2".into()] + ), + "INSERT INTO \"table_name\" (\"header1\", \"header2\") VALUES " + ); + assert_eq!( + build_prepared_statement_sql( + &DatabaseEngine::MySQL, + "table_name", + &["header1".into(), "header2".into()] + ), + "INSERT INTO `table_name` (`header1`, `header2`) VALUES " + ); + assert_eq!( + build_prepared_statement_sql( + &DatabaseEngine::Postgres, + "table_name", + &["header1".into(), "header2".into()] + ), + "INSERT INTO \"table_name\" (\"header1\", \"header2\") VALUES " + ); + } + + #[tokio::test] + async fn test_get_create_statement() { + let snake_case_headers: Vec = vec!["header1".into(), "header2".into()]; + assert_eq!( + build_create_table_sql(&DatabaseEngine::SQLite, "table_name", &snake_case_headers), + "CREATE TABLE \"table_name\" (header1 TEXT, header2 TEXT)" + ); + assert_eq!( + build_create_table_sql(&DatabaseEngine::MySQL, "table_name", &snake_case_headers), + "CREATE TABLE `table_name` (`header1` TEXT, `header2` TEXT)" + ); + assert_eq!( + build_create_table_sql(&DatabaseEngine::Postgres, "table_name", &snake_case_headers), + "CREATE TABLE \"table_name\" (header1 TEXT, header2 TEXT)" + ); + + let snake_case_headers: Vec = vec!["header1".into()]; + assert_eq!( + build_create_table_sql(&DatabaseEngine::SQLite, "table_name", &snake_case_headers), + "CREATE TABLE \"table_name\" (header1 TEXT)" + ); + assert_eq!( + build_create_table_sql(&DatabaseEngine::MySQL, "table_name", &snake_case_headers), + "CREATE TABLE `table_name` (`header1` TEXT)" + ); + assert_eq!( + build_create_table_sql(&DatabaseEngine::Postgres, "table_name", &snake_case_headers), + "CREATE TABLE \"table_name\" (header1 TEXT)" + ); + } + + #[tokio::test] + async fn test_get_create_statement_with_fixed_size() { + const FINAL_TABLE_NAME: &str = "test_table"; + + let snake_case_headers: Vec = vec!["header1".into(), "header2".into()]; + let map_max_length: HashMap<&str, usize> = + snake_case_headers.iter().map(|h| (h.as_str(), 0)).collect(); + + let mut db_driver: HashMap<&DatabaseEngine, &str> = HashMap::new(); + db_driver.insert( + &DatabaseEngine::Postgres, + "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(0), \"header2\" VARCHAR(0));", + ); + db_driver.insert( + &DatabaseEngine::MySQL, + "CREATE TABLE `test_table` (`header1` VARCHAR(0), `header2` VARCHAR(0));", + ); + db_driver.insert( + &DatabaseEngine::MariaDB, + "CREATE TABLE `test_table` (`header1` VARCHAR(0), `header2` VARCHAR(0));", + ); + db_driver.insert( + &DatabaseEngine::SQLite, + "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(0), \"header2\" VARCHAR(0));", + ); + + let final_columns: Vec = vec!["header1".into(), "header2".into()]; + + for (driver, expected) in db_driver { + let result: String = build_create_with_fixed_size_sql( + driver, + FINAL_TABLE_NAME, + &map_max_length, + &final_columns, + ); + assert_eq!(result, expected); + } + + let map_max_length: HashMap<&str, usize> = snake_case_headers + .iter() + .map(|h| (h.as_str(), 10)) + .collect(); + let mut db_driver: HashMap<&DatabaseEngine, &str> = HashMap::new(); + db_driver.insert( + &DatabaseEngine::Postgres, + "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(10), \"header2\" VARCHAR(10));", + ); + db_driver.insert( + &DatabaseEngine::MySQL, + "CREATE TABLE `test_table` (`header1` VARCHAR(10), `header2` VARCHAR(10));", + ); + db_driver.insert( + &DatabaseEngine::MariaDB, + "CREATE TABLE `test_table` (`header1` VARCHAR(10), `header2` VARCHAR(10));", + ); + db_driver.insert( + &DatabaseEngine::SQLite, + "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(10), \"header2\" VARCHAR(10));", + ); + + for (driver, expected) in db_driver { + let result: String = build_create_with_fixed_size_sql( + driver, + FINAL_TABLE_NAME, + &map_max_length, + &final_columns, + ); + assert_eq!(result, expected); + } + } + + #[tokio::test] + async fn test_build_query_all_tables() { + let test_cases: Vec<(&DatabaseEngine, String)> = vec![ + ( + &DatabaseEngine::MySQL, + "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'test';" + .into(), + ), + ( + &DatabaseEngine::MariaDB, + "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'test';" + .into(), + ), + ( + &DatabaseEngine::Postgres, + "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'public';" + .into(), + ), + ( + &DatabaseEngine::SQLite, + "SELECT name FROM sqlite_master WHERE type='table';".into(), + ), + ]; + + for (driver, expected) in test_cases { + assert_eq!( + build_query_all_tables(driver, "test"), + expected, + "Failed for driver: {:?}", + driver + ); + } + } +} diff --git a/src-tauri/src/fileflow/enumeration/mod.rs b/src-tauri/src/fileflow/enumeration/mod.rs index cd0b48a..11cfdf4 100644 --- a/src-tauri/src/fileflow/enumeration/mod.rs +++ b/src-tauri/src/fileflow/enumeration/mod.rs @@ -1,3 +1,3 @@ pub mod database_engine; pub mod insertion_type; -pub mod separator; \ No newline at end of file +pub mod separator; diff --git a/src-tauri/src/fileflow/mod.rs b/src-tauri/src/fileflow/mod.rs index 1a626aa..b879a2f 100644 --- a/src-tauri/src/fileflow/mod.rs +++ b/src-tauri/src/fileflow/mod.rs @@ -1,5 +1,5 @@ pub mod action; pub mod database; +pub mod enumeration; pub mod stuct; pub mod utils; -pub mod enumeration; diff --git a/src-tauri/src/fileflow/stuct/db_config.rs b/src-tauri/src/fileflow/stuct/db_config.rs index 8fb44bf..d81fe75 100644 --- a/src-tauri/src/fileflow/stuct/db_config.rs +++ b/src-tauri/src/fileflow/stuct/db_config.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::fileflow::enumeration::database_engine::DatabaseEngine; +use serde::{Deserialize, Serialize}; #[derive(Deserialize, Clone, Serialize)] pub struct DbConfig { @@ -10,4 +10,4 @@ pub struct DbConfig { pub password: String, pub db_name: String, pub sqlite_file_path: String, -} \ No newline at end of file +} diff --git a/src-tauri/src/fileflow/stuct/download_config.rs b/src-tauri/src/fileflow/stuct/download_config.rs index 27742a7..8fa219d 100644 --- a/src-tauri/src/fileflow/stuct/download_config.rs +++ b/src-tauri/src/fileflow/stuct/download_config.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] pub struct DownloadConfig { - pub table_name: String, + pub table_name_list: Vec, pub location: String, pub separator: SeparatorType, } diff --git a/src-tauri/src/fileflow/stuct/mod.rs b/src-tauri/src/fileflow/stuct/mod.rs index 5012f4a..a4a4752 100644 --- a/src-tauri/src/fileflow/stuct/mod.rs +++ b/src-tauri/src/fileflow/stuct/mod.rs @@ -1,5 +1,5 @@ +pub mod combo_item; pub mod db_config; +pub mod download_config; pub mod insert_config; pub mod save_config; -pub mod combo_item; -pub mod download_config; diff --git a/src-tauri/src/fileflow/stuct/save_config.rs b/src-tauri/src/fileflow/stuct/save_config.rs index 1f7a942..194ab03 100644 --- a/src-tauri/src/fileflow/stuct/save_config.rs +++ b/src-tauri/src/fileflow/stuct/save_config.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::fileflow::enumeration::database_engine::DatabaseEngine; +use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Clone)] pub struct SaveConfig { diff --git a/src-tauri/src/fileflow/utils/csv_utils.rs b/src-tauri/src/fileflow/utils/csv_utils.rs new file mode 100644 index 0000000..94b8246 --- /dev/null +++ b/src-tauri/src/fileflow/utils/csv_utils.rs @@ -0,0 +1,24 @@ +use std::fs::File; +use std::io; +use std::io::{BufRead, BufReader}; + +/// This function is used to detect the separator from a string. +pub fn find_separator(line: &str) -> Result { + const POSSIBLE_SEPARATORS: [char; 6] = [',', ';', '\t', '|', ' ', '\0']; + for sep in POSSIBLE_SEPARATORS.iter() { + if line.contains(*sep) { + return Ok(*sep); + } + } + Err("Could not detect a valid separator".into()) +} + +/// Read the first line of a file +pub fn read_first_line(file_path: &str) -> io::Result { + let file: File = File::open(file_path).expect("Could not open file"); + let reader: BufReader = BufReader::new(file); + if let Some(line) = reader.lines().next() { + return line; + } + Err(io::Error::new(io::ErrorKind::NotFound, "File is empty")) +} diff --git a/src-tauri/src/fileflow/utils/fileflowlib.rs b/src-tauri/src/fileflow/utils/fileflowlib.rs index 51fee09..65325b0 100644 --- a/src-tauri/src/fileflow/utils/fileflowlib.rs +++ b/src-tauri/src/fileflow/utils/fileflowlib.rs @@ -1,61 +1,8 @@ use crate::fileflow::stuct::save_config::SaveConfig; -use csv::StringRecord; use std::fs::{File, OpenOptions}; use std::io; -use std::io::{BufRead, BufReader}; use std::path::PathBuf; -/// This function is used to generate the column names for a CSV file. -pub fn get_formated_column_names(headers: Vec) -> Vec { - const COLUMN_PREFIX: &str = "column_"; - - let mut headers: Vec = headers; - for (i, item) in headers.iter_mut().enumerate() { - if item.trim().is_empty() { - *item = format!("{COLUMN_PREFIX}{}", i + 1); - } - } - headers - .iter() - .map(|h| h.to_lowercase().replace(' ', "_")) - .collect() -} - -/// This function is used to detect the separator from a string. -pub fn find_separator(line: &str) -> Result { - const POSSIBLE_SEPARATORS: [char; 6] = [',', ';', '\t', '|', ' ', '\0']; - for sep in POSSIBLE_SEPARATORS.iter() { - if line.contains(*sep) { - return Ok(*sep); - } - } - Err("Could not detect a valid separator".into()) -} - -/// Sanitize a value for safe insertion into the database -pub fn sanitize_value(value: &str) -> String { - value.trim().replace("'", "''").replace("\\", "\\\\") -} - -/// Read the first line of a file -pub fn read_first_line(file_path: &str) -> io::Result { - let file: File = File::open(file_path).expect("Could not open file"); - let reader: BufReader = BufReader::new(file); - if let Some(line) = reader.lines().next() { - return line; - } - Err(io::Error::new(io::ErrorKind::NotFound, "File is empty")) -} - -/// Escape values for SQL insert statement to avoid SQL injection attacks and other issues with special characters in values. -pub fn escaped_values(values: StringRecord) -> String { - let vec: Vec = values - .iter() - .map(|v| format!("'{}'", sanitize_value(v))) - .collect(); - vec.join(", ") -} - /// This function is used to get the size of a file. pub fn get_all_saved_configs(config_file: &str) -> Vec { let default_configs: Vec = Vec::new(); diff --git a/src-tauri/src/fileflow/utils/mod.rs b/src-tauri/src/fileflow/utils/mod.rs index 036348e..2934cbd 100644 --- a/src-tauri/src/fileflow/utils/mod.rs +++ b/src-tauri/src/fileflow/utils/mod.rs @@ -1,3 +1,4 @@ pub mod constants; +pub mod csv_utils; pub mod fileflowlib; -pub mod sql; +pub mod string_formater; diff --git a/src-tauri/src/fileflow/utils/string_formater.rs b/src-tauri/src/fileflow/utils/string_formater.rs new file mode 100644 index 0000000..1ec4562 --- /dev/null +++ b/src-tauri/src/fileflow/utils/string_formater.rs @@ -0,0 +1,56 @@ +use csv::StringRecord; + +/// This function is used to generate the column names for a CSV file. +pub fn get_formated_column_names(headers: &[String]) -> Vec { + const COLUMN_PREFIX: &str = "column_"; + let mut safe_headers: Vec = Vec::with_capacity(headers.len()); + + for (index, item) in headers.iter().enumerate() { + let trimmed_column_name: &str = item.trim(); + if trimmed_column_name.is_empty() { + safe_headers.push(format!("{COLUMN_PREFIX}{}", index + 1)); + } else { + safe_headers.push(sanitize_column(trimmed_column_name)); + } + } + safe_headers +} + +/// Sanitize a value for safe insertion into the database +pub fn sanitize_value(value: &str) -> String { + let mut sanitized: String = String::with_capacity(value.len()); + + for c in value.trim().chars() { + match c { + '\'' => sanitized.push_str("''"), // Escape single quotes + '\\' => sanitized.push_str("\\\\"), // Escape backslashes + '\"' | '\0' => {} // Remove double quotes and null characters + '\r' | '\n' => sanitized.push(' '), // Normalize newlines + _ => sanitized.push(c), + } + } + + sanitized +} + +/// Sanitize a column name for safe insertion into the database +pub fn sanitize_column(value: &str) -> String { + value + .trim() + .chars() + .filter_map(|c| match c { + '\'' | '\\' | '\"' => None, // Remove these characters + ' ' => Some('_'), // Replace spaces with underscores + _ => Some(c.to_ascii_lowercase()), // to lowercase + }) + .collect() +} + +/// Escape values for SQL insert statement to avoid SQL injection attacks and other issues with special characters in values. +pub fn escaped_record(values: StringRecord) -> String { + let vec: Vec = values + .iter() + .map(|v| format!("'{}'", sanitize_value(v))) + .collect(); + vec.join(", ") +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index d172cd9..7b344e6 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -6,9 +6,9 @@ mod fileflow; #[cfg(test)] mod tests; -use crate::fileflow::action::database_actions::get_table_list; +use crate::fileflow::action::database_command::get_table_list; use fileflow::action::actions::*; -use fileflow::action::database_actions::*; +use fileflow::action::database_command::*; use std::sync::Arc; use tokio::sync::Mutex; diff --git a/src-tauri/src/tests/action_test.rs b/src-tauri/src/tests/action_test.rs index fd0fe09..8c5c1b2 100644 --- a/src-tauri/src/tests/action_test.rs +++ b/src-tauri/src/tests/action_test.rs @@ -1,10 +1,9 @@ -use crate::fileflow::action::insertion_mode::fast_insert; use crate::fileflow::database::connection::Connection; use crate::fileflow::enumeration::database_engine::DatabaseEngine; use crate::fileflow::stuct::db_config::DbConfig; use crate::fileflow::stuct::save_config::SaveConfig; use crate::fileflow::utils::fileflowlib::{get_all_saved_configs, save_config}; -use crate::tests::utils::{ +use crate::tests::utils_tests::{ create_test_db, delete_config_file, generate_csv_file, get_test_save_config, get_test_sqlite_config, remove_csv_file, remove_test_db, }; @@ -12,6 +11,7 @@ use csv::{Reader, ReaderBuilder}; use sqlx::sqlite::SqliteRow; use sqlx::{Error, Pool, Row, Sqlite}; use std::fs::File; +use crate::fileflow::action::database_command::fast_insert; #[tokio::test] async fn test_fast_insert() { @@ -77,6 +77,8 @@ async fn test_fast_insert() { assert_ne!(value1, "value3"); assert_ne!(value2, "value4"); + drop(conn); + remove_test_db("fast_insert").expect("Failed to remove test table"); remove_csv_file("test_fast_insert").expect("Failed to remove CSV file"); } diff --git a/src-tauri/src/tests/standard_test.rs b/src-tauri/src/tests/csv_utils_test.rs similarity index 53% rename from src-tauri/src/tests/standard_test.rs rename to src-tauri/src/tests/csv_utils_test.rs index 23d1ab7..6cc0894 100644 --- a/src-tauri/src/tests/standard_test.rs +++ b/src-tauri/src/tests/csv_utils_test.rs @@ -1,6 +1,5 @@ -use crate::fileflow::utils::fileflowlib::{escaped_values, find_separator, read_first_line}; -use crate::tests::utils::{generate_csv_file, remove_csv_file}; -use csv::StringRecord; +use crate::fileflow::utils::csv_utils::{find_separator, read_first_line}; +use crate::tests::utils_tests::{generate_csv_file, remove_csv_file}; #[tokio::test] async fn test_detect_separator() { @@ -22,22 +21,6 @@ async fn test_detect_separator() { } } -#[tokio::test] -async fn test_escape_values() { - let record: StringRecord = StringRecord::from(vec!["value1", "value2"]); - let values: String = escaped_values(record); - assert_eq!(values, "'value1', 'value2'"); - - let record: StringRecord = StringRecord::from(vec![ - "INSERT INTO test_table VALUES (1,2);", - "UPDATE test_table SET column1 = 1;", - "DELETE FROM test_table WHERE column1 = 1;", - "SELECT * FROM test_table;", - ]); - let values: String = escaped_values(record); - assert_eq!(values, "'INSERT INTO test_table VALUES (1,2);', 'UPDATE test_table SET column1 = 1;', 'DELETE FROM test_table WHERE column1 = 1;', 'SELECT * FROM test_table;'"); -} - #[tokio::test] async fn test_read_first_line() { let csv_file_path: String = diff --git a/src-tauri/src/tests/database_test.rs b/src-tauri/src/tests/database_test.rs index 749d1c6..fe0ea02 100644 --- a/src-tauri/src/tests/database_test.rs +++ b/src-tauri/src/tests/database_test.rs @@ -1,43 +1,32 @@ use crate::fileflow::database::connection::{Connection, QueryResult}; -use crate::fileflow::enumeration::database_engine::DatabaseEngine; +use crate::fileflow::database::database_actions::export_table; use crate::fileflow::enumeration::separator::SeparatorType; use crate::fileflow::stuct::db_config::DbConfig; use crate::fileflow::stuct::download_config::DownloadConfig; -use crate::fileflow::utils::fileflowlib::get_formated_column_names; -use crate::fileflow::utils::sql::{ - export_table, build_query_all_tables, get_create_statement, get_create_statement_with_fixed_size, - get_drop_statement, get_insert_into_statement, -}; -use crate::tests::utils::{ +use crate::tests::utils_tests::{ create_test_db, get_test_maridb_config, get_test_mysql_config, get_test_pg_config, get_test_sqlite_config, remove_test_db, }; use sqlx::testing::TestTermination; use sqlx::{Error, Row}; -use std::collections::HashMap; use std::path::PathBuf; #[tokio::test] async fn test_get_connection_url() { - let pg_config: DbConfig = get_test_pg_config(); - let mariadb_config: DbConfig = get_test_maridb_config(); - let mysql_config: DbConfig = get_test_mysql_config(); - let sqlite_config: DbConfig = get_test_sqlite_config(String::new()); - assert_eq!( - Connection::get_connection_url(&pg_config).unwrap(), + Connection::get_connection_url(&get_test_pg_config()), "postgres://postgres:password@localhost:5432/test_db" ); assert_eq!( - Connection::get_connection_url(&mariadb_config).unwrap(), + Connection::get_connection_url(&get_test_maridb_config()), "mysql://root:password@localhost:3306/test_db" ); assert_eq!( - Connection::get_connection_url(&mysql_config).unwrap(), + Connection::get_connection_url(&get_test_mysql_config()), "mysql://root:password@localhost:3306/test_db" ); assert_eq!( - Connection::get_connection_url(&sqlite_config).unwrap(), + Connection::get_connection_url(&get_test_sqlite_config(String::new())), "test_db.db" ); } @@ -47,198 +36,14 @@ async fn test_sqlite_connection() { let file_path: String = create_test_db("sqlite_connection"); let config: DbConfig = get_test_sqlite_config(file_path); let conn = Connection::connect(&config).await; + assert!(conn.is_success(), "Failed to connect to the database"); assert!(conn.is_ok()); - remove_test_db("sqlite_connection").expect("Failed to remove test table"); -} - -#[tokio::test] -async fn test_get_drop_statement() { - assert_eq!( - get_drop_statement(&DatabaseEngine::SQLite, "table_name").unwrap(), - "DROP TABLE IF EXISTS \"table_name\"" - ); - assert_eq!( - get_drop_statement(&DatabaseEngine::MySQL, "table_name").unwrap(), - "DROP TABLE IF EXISTS `table_name`" - ); - assert_eq!( - get_drop_statement(&DatabaseEngine::Postgres, "table_name").unwrap(), - "DROP TABLE IF EXISTS \"table_name\"" - ); - assert_eq!( - get_drop_statement(&DatabaseEngine::SQLite, "").unwrap(), - "DROP TABLE IF EXISTS \"\"" - ); - assert_eq!( - get_drop_statement(&DatabaseEngine::MySQL, "").unwrap(), - "DROP TABLE IF EXISTS ``" - ); - assert_eq!( - get_drop_statement(&DatabaseEngine::Postgres, "").unwrap(), - "DROP TABLE IF EXISTS \"\"" - ); -} - -#[tokio::test] -async fn test_get_insert_into_statement() { - assert_eq!( - get_insert_into_statement(&DatabaseEngine::SQLite, "table_name", "columns").unwrap(), - "INSERT INTO \"table_name\" (columns) VALUES " - ); - assert_eq!( - get_insert_into_statement(&DatabaseEngine::MySQL, "table_name", "columns").unwrap(), - "INSERT INTO `table_name` (columns) VALUES " - ); - assert_eq!( - get_insert_into_statement(&DatabaseEngine::Postgres, "table_name", "columns").unwrap(), - "INSERT INTO \"table_name\" (columns) VALUES " - ); - - assert_eq!( - get_insert_into_statement(&DatabaseEngine::SQLite, "table_name", "").unwrap(), - "INSERT INTO \"table_name\" () VALUES " - ); - assert_eq!( - get_insert_into_statement(&DatabaseEngine::MySQL, "table_name", "").unwrap(), - "INSERT INTO `table_name` () VALUES " - ); - assert_eq!( - get_insert_into_statement(&DatabaseEngine::Postgres, "table_name", "").unwrap(), - "INSERT INTO \"table_name\" () VALUES " - ); - - assert_eq!( - get_insert_into_statement(&DatabaseEngine::SQLite, "table_name", "header1, header2") - .unwrap(), - "INSERT INTO \"table_name\" (header1, header2) VALUES " - ); - assert_eq!( - get_insert_into_statement(&DatabaseEngine::MySQL, "table_name", "header1, header2") - .unwrap(), - "INSERT INTO `table_name` (header1, header2) VALUES " - ); - assert_eq!( - get_insert_into_statement(&DatabaseEngine::Postgres, "table_name", "header1, header2") - .unwrap(), - "INSERT INTO \"table_name\" (header1, header2) VALUES " - ); -} -#[tokio::test] -async fn test_get_create_statement() { - let snake_case_headers: Vec = vec!["header1".into(), "header2".into()]; - assert_eq!( - get_create_statement(&DatabaseEngine::SQLite, "table_name", &snake_case_headers).unwrap(), - "CREATE TABLE \"table_name\" (header1 TEXT, header2 TEXT)" - ); - assert_eq!( - get_create_statement(&DatabaseEngine::MySQL, "table_name", &snake_case_headers).unwrap(), - "CREATE TABLE `table_name` (header1 TEXT, header2 TEXT)" - ); - assert_eq!( - get_create_statement(&DatabaseEngine::Postgres, "table_name", &snake_case_headers).unwrap(), - "CREATE TABLE \"table_name\" (header1 TEXT, header2 TEXT)" - ); - - let snake_case_headers: Vec = vec!["header1".into()]; - assert_eq!( - get_create_statement(&DatabaseEngine::SQLite, "table_name", &snake_case_headers).unwrap(), - "CREATE TABLE \"table_name\" (header1 TEXT)" - ); - assert_eq!( - get_create_statement(&DatabaseEngine::MySQL, "table_name", &snake_case_headers).unwrap(), - "CREATE TABLE `table_name` (header1 TEXT)" - ); - assert_eq!( - get_create_statement(&DatabaseEngine::Postgres, "table_name", &snake_case_headers).unwrap(), - "CREATE TABLE \"table_name\" (header1 TEXT)" - ); -} - -#[tokio::test] -async fn test_get_create_statement_with_fixed_size() { - const FINAL_TABLE_NAME: &str = "test_table"; - - let snake_case_headers: Vec = vec!["header1".into(), "header2".into()]; - let map_max_length: HashMap<&str, usize> = - snake_case_headers.iter().map(|h| (h.as_str(), 0)).collect(); - - let mut db_driver: HashMap<&DatabaseEngine, &str> = HashMap::new(); - db_driver.insert( - &DatabaseEngine::Postgres, - "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(0), \"header2\" VARCHAR(0));", - ); - db_driver.insert( - &DatabaseEngine::MySQL, - "CREATE TABLE `test_table` (`header1` VARCHAR(0), `header2` VARCHAR(0));", - ); - db_driver.insert( - &DatabaseEngine::MariaDB, - "CREATE TABLE `test_table` (`header1` VARCHAR(0), `header2` VARCHAR(0));", - ); - db_driver.insert( - &DatabaseEngine::SQLite, - "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(0), \"header2\" VARCHAR(0));", - ); - - let final_columns: Vec = vec!["header1".into(), "header2".into()]; - - for (driver, expected) in db_driver { - let result: Result = get_create_statement_with_fixed_size( - driver, - FINAL_TABLE_NAME, - &map_max_length, - &final_columns, - ); - assert_eq!(result.unwrap(), expected); - } - - let map_max_length: HashMap<&str, usize> = snake_case_headers - .iter() - .map(|h| (h.as_str(), 10)) - .collect(); - let mut db_driver: HashMap<&DatabaseEngine, &str> = HashMap::new(); - db_driver.insert( - &DatabaseEngine::Postgres, - "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(10), \"header2\" VARCHAR(10));", - ); - db_driver.insert( - &DatabaseEngine::MySQL, - "CREATE TABLE `test_table` (`header1` VARCHAR(10), `header2` VARCHAR(10));", - ); - db_driver.insert( - &DatabaseEngine::MariaDB, - "CREATE TABLE `test_table` (`header1` VARCHAR(10), `header2` VARCHAR(10));", - ); - db_driver.insert( - &DatabaseEngine::SQLite, - "CREATE TABLE \"test_table\" (\"header1\" VARCHAR(10), \"header2\" VARCHAR(10));", - ); - - for (driver, expected) in db_driver { - let result: Result = get_create_statement_with_fixed_size( - driver, - FINAL_TABLE_NAME, - &map_max_length, - &final_columns, - ); - assert_eq!(result.unwrap(), expected); - } -} - -#[tokio::test] -async fn test_get_formated_column_names() { - let headers: Vec = vec!["header 1".into(), " header2".into()]; - let formatted_headers: Vec = get_formated_column_names(headers); - assert_eq!(formatted_headers, vec!["header_1", "_header2"]); - - let headers: Vec = vec!["header 1".into(), String::new(), "header2".into()]; - let formatted_headers: Vec = get_formated_column_names(headers); - assert_eq!( - formatted_headers, - vec!["header____1", "column_2", "header2"] - ); + let conn: Connection = conn.unwrap(); + conn.disconnect(); + drop(conn); + remove_test_db("sqlite_connection").expect("Failed to remove test table"); } #[tokio::test] @@ -259,11 +64,8 @@ async fn test_query_many_with_result() { ]; for sql in SQL_ARRAY.iter() { - match conn.query(sql).await { - Ok(_) => {} - Err(e) => { - println!("Error: {:?} for query: {sql}", e); - } + if let Err(e) = conn.query(sql).await { + println!("Error: {:?} for query: {sql}", e); } } @@ -279,41 +81,11 @@ async fn test_query_many_with_result() { assert_eq!(rows[0].len(), 2); } + conn.disconnect(); + drop(conn); remove_test_db("test_query_many_with_result").expect("Failed to remove test query"); } -#[tokio::test] -async fn test_build_query_all_tables() { - let test_cases: Vec<(&DatabaseEngine, String)> = vec![ - ( - &DatabaseEngine::MySQL, - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'test';".into(), - ), - ( - &DatabaseEngine::MariaDB, - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'test';".into(), - ), - ( - &DatabaseEngine::Postgres, - "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'public';" - .into(), - ), - ( - &DatabaseEngine::SQLite, - "SELECT name FROM sqlite_master WHERE type='table';".into(), - ), - ]; - - for (driver, expected) in test_cases { - assert_eq!( - build_query_all_tables(driver, "test"), - expected, - "Failed for driver: {:?}", - driver - ); - } -} - #[tokio::test] async fn test_download_table() { let file_path: String = create_test_db("test_download_table"); @@ -351,21 +123,20 @@ async fn test_download_table() { let download_config: DownloadConfig = DownloadConfig { separator: SeparatorType::Semicolon, - table_name: "test_table".into(), + table_name_list: vec!["test_table".into()], location: "./".into(), }; let file_path: PathBuf = PathBuf::from(format!( "{}/{}_export.csv", - download_config.location, download_config.table_name + download_config.location, download_config.table_name_list[0] )); - export_table(&conn, download_config) + export_table(&conn, &download_config, &download_config.table_name_list[0]) .await .expect("Failed to export table"); - // check if file exists - assert!(std::path::Path::new(&file_path).exists()); + assert!(std::path::Path::new(&file_path).exists()); // check if file exists // get the content of the file let content: String = std::fs::read_to_string(&file_path).expect("Failed to read file"); @@ -376,11 +147,11 @@ async fn test_download_table() { let download_config: DownloadConfig = DownloadConfig { separator: SeparatorType::Comma, - table_name: "test_table".into(), + table_name_list: vec!["test_table".into()], location: "./".into(), }; - export_table(&conn, download_config) + export_table(&conn, &download_config, &download_config.table_name_list[0]) .await .expect("Failed to export table"); @@ -394,6 +165,7 @@ async fn test_download_table() { "Failed to export table" ); + // Clean up std::fs::remove_file(&file_path).expect("Failed to remove file"); remove_test_db("test_download_table").expect("Failed to remove test table"); diff --git a/src-tauri/src/tests/mod.rs b/src-tauri/src/tests/mod.rs index b2de678..3824798 100644 --- a/src-tauri/src/tests/mod.rs +++ b/src-tauri/src/tests/mod.rs @@ -1,10 +1,12 @@ #[cfg(test)] mod action_test; #[cfg(test)] +mod csv_utils_test; +#[cfg(test)] mod database_test; #[cfg(test)] -mod standard_test; +mod string_formater_test; #[cfg(test)] mod struct_test; #[cfg(test)] -mod utils; +mod utils_tests; diff --git a/src-tauri/src/tests/string_formater_test.rs b/src-tauri/src/tests/string_formater_test.rs new file mode 100644 index 0000000..5973aa3 --- /dev/null +++ b/src-tauri/src/tests/string_formater_test.rs @@ -0,0 +1,57 @@ +use crate::fileflow::utils::string_formater::{ + escaped_record, get_formated_column_names, sanitize_column, sanitize_value, +}; +use csv::StringRecord; + +#[tokio::test] +async fn test_escape_values() { + let record: StringRecord = StringRecord::from(vec!["value1", "value2"]); + let values: String = escaped_record(record); + assert_eq!(values, "'value1', 'value2'"); + + let record: StringRecord = StringRecord::from(vec![ + "\"INSERT INTO test_table VALUES (1,2);\"", + "UPDATE test_table SET column1 = 1;", + "DELETE FROM test_table WHERE column1 = 1;", + "SELECT * FROM test_table;", + ]); + let values: String = escaped_record(record); + assert_eq!(values, "'INSERT INTO test_table VALUES (1,2);', 'UPDATE test_table SET column1 = 1;', 'DELETE FROM test_table WHERE column1 = 1;', 'SELECT * FROM test_table;'"); +} + +#[tokio::test] +async fn test_sanitize_value() { + assert_eq!(sanitize_value("value1"), "value1"); + assert_eq!(sanitize_value("'value1'"), "''value1''"); + assert_eq!(sanitize_value("value1'"), "value1''"); + assert_eq!(sanitize_value("value1\\value2"), "value1\\\\value2"); + assert_eq!(sanitize_value("value1\"value2"), "value1value2"); +} + +#[tokio::test] +async fn test_sanitize_column() { + assert_eq!(sanitize_column("column1"), "column1"); + assert_eq!(sanitize_column("'column1'"), "column1"); + assert_eq!(sanitize_column("column1'"), "column1"); + assert_eq!(sanitize_column("column1\\column2"), "column1column2"); + assert_eq!(sanitize_column("column1\"column2"), "column1column2"); + assert_eq!(sanitize_column("column 1"), "column_1"); + assert_eq!(sanitize_column("column 1'"), "column_1"); + assert_eq!(sanitize_column("COlUMn 1'"), "column_1"); +} + +#[tokio::test] +async fn test_get_formated_column_names() { + assert_eq!( + get_formated_column_names(&vec!["header 1".into(), " header2".into()]), + vec!["header_1", "header2"] + ); + assert_eq!( + get_formated_column_names(&vec!["header 1".into(), String::new(), "header2".into(), "".into()]), + vec!["header____1", "column_2", "header2", "column_4"] + ); + assert_eq!( + get_formated_column_names(&vec!["header'\" 1".into(), "header 2''\\".into()]), + vec!["header_1", "header_2"] + ); +} diff --git a/src-tauri/src/tests/struct_test.rs b/src-tauri/src/tests/struct_test.rs index 304a86a..fbbb6bd 100644 --- a/src-tauri/src/tests/struct_test.rs +++ b/src-tauri/src/tests/struct_test.rs @@ -67,12 +67,14 @@ async fn test_save_config() { #[tokio::test] async fn test_downlopad_config() { let config = DownloadConfig { - table_name: "table_name".into(), + table_name_list: vec!["table1".into(), "table2".into()], location: "location".into(), separator: SeparatorType::Comma, }; - assert_eq!(config.table_name, "table_name"); + assert_eq!(config.table_name_list.len(), 2); + assert_eq!(config.table_name_list[0], "table1"); + assert_eq!(config.table_name_list[1], "table2"); assert_eq!(config.location, "location"); assert_eq!(config.separator, SeparatorType::Comma); } diff --git a/src-tauri/src/tests/utils.rs b/src-tauri/src/tests/utils_tests.rs similarity index 97% rename from src-tauri/src/tests/utils.rs rename to src-tauri/src/tests/utils_tests.rs index 74a215b..613b899 100644 --- a/src-tauri/src/tests/utils.rs +++ b/src-tauri/src/tests/utils_tests.rs @@ -116,9 +116,10 @@ pub fn remove_test_db(db_name: &str) -> Result<(), Box> { if !std::path::Path::new(&file_path).exists() { println!("File does not exist"); - Err("Failed to create SQLite file")?; + return Ok(()); } - std::fs::remove_file(&file_path).expect("Failed to remove SQLite file"); + + std::fs::remove_file(file_path).expect("Failed to remove SQLite file"); Ok(()) } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 7f558f1..12e04e2 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -18,6 +18,7 @@ }, "productName": "FileFlow", "mainBinaryName": "FileFlow", + "version" : "1.0.2", "identifier": "com.fileflow.app", "plugins": {}, "app": { diff --git a/src/components/fileflowui/extract/download/Download.tsx b/src/components/fileflowui/extract/download/Download.tsx index 52ef223..ddb49b1 100644 --- a/src/components/fileflowui/extract/download/Download.tsx +++ b/src/components/fileflowui/extract/download/Download.tsx @@ -1,8 +1,7 @@ import React, {useEffect, useState} from 'react'; import {invoke} from "@tauri-apps/api/core"; import {Label} from "@/components/ui/label.tsx"; -import {ComboboxComponent} from "@/components/hooks/component/ComboboxComponent.tsx"; -import {log_error} from "@/components/hooks/utils.tsx"; +import {log_error, requestAllTablesFromConnection} from "@/components/hooks/utils.tsx"; import {Select, SelectContent, SelectItem, SelectTrigger} from "@/components/ui/select.tsx"; import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card.tsx"; import {toast} from "sonner"; @@ -11,6 +10,7 @@ import Loader from "@/components/hooks/Loader.tsx"; import ConnectionForm from "@/components/hooks/database/ConnectionForm.tsx"; import {AnimatePresence, motion} from 'framer-motion'; import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; +import {CheckBoxCombo} from "@/components/hooks/component/CheckBoxCombo.tsx"; const Download: React.FC = () => { @@ -27,7 +27,7 @@ const Download: React.FC = () => { }); const [tables, setTables] = useState>([]); - const [selectedTable, setSelectedTable] = useState(null); + const [selectedTables, setSelectedTables] = useState([]); const [separator, setSeparator] = useState<',' | ';' | ' ' | '|'>(','); const [absolutePath, setAbsolutePath] = useState(''); const [showLoader, setShowLoader] = useState(false); @@ -38,23 +38,13 @@ const Download: React.FC = () => { const retrieveTables = async (): Promise => { try { - console.log('Retrieving tables...'); - const get_table_list_response: boolean | ComboItem[] = await invoke | boolean>('get_table_list'); - - if (typeof get_table_list_response === "boolean") { - throw new Error('Failed to get table list'); - } + const parsedData: ComboItem[] | boolean = await requestAllTablesFromConnection(); - if (get_table_list_response.length === 0) { - throw new Error('No tables found'); + if (typeof parsedData === "boolean") { + throw new Error("Failed to retrieve tables"); } - const parsedData: ComboItem[] = get_table_list_response.map(item => ({ - value: item.value, - label: item.label - })); - setTables(parsedData); } catch (error) { log_error(error) @@ -64,15 +54,20 @@ const Download: React.FC = () => { const handleDownload = async () => { try { - if (!selectedTable && absolutePath === "") { + if (!selectedTables && absolutePath === "") { throw new Error('Please fill in all required fields'); } + selectedTables.forEach((table) => { + console.log(`Selected table: ${table}`); + } + ); + setShowLoader(true); const download_table_response: string = await invoke('download_table', { config: { - table_name: selectedTable, + table_name_list: selectedTables, location: absolutePath, separator: getSeparatorName(separator).toLocaleLowerCase() } @@ -127,10 +122,12 @@ const Download: React.FC = () => {
- - Export Configuration - +
+ + Export Configuration + +
@@ -147,7 +144,7 @@ const Download: React.FC = () => { Tables available ({tables.length})
- +
)} diff --git a/src/components/fileflowui/load/insert/ButtonGroupAction.tsx b/src/components/fileflowui/load/insert/ButtonGroupAction.tsx index 730ae7e..a31fbb7 100644 --- a/src/components/fileflowui/load/insert/ButtonGroupAction.tsx +++ b/src/components/fileflowui/load/insert/ButtonGroupAction.tsx @@ -5,16 +5,17 @@ import {invoke} from "@tauri-apps/api/core"; import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip.tsx"; import {log_error} from "@/components/hooks/utils.tsx"; import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; +import {InsertionModeEnum} from "@/components/fileflowui/load/insert/Insert.tsx"; interface ButtonGroupProps { dbConfig: DatabaseConfig; updateDbConfigField: (field: keyof DatabaseConfig, value: DatabaseConfig[keyof DatabaseConfig]) => void; - filePath: string; - setFilePath: (path: string) => void; + filesPath: string[]; + setFilesPath: (path: string[]) => void; tableName: string; setTableName: (name: string) => void mode: string; - setMode: (mode: "fast" | "optimized") => void; + setMode: (mode: InsertionModeEnum) => void; showLoader: boolean; setShowLoader: (showLoader: boolean) => void; } @@ -24,7 +25,7 @@ const ButtonGroupAction: React.FC = (props: ButtonGroupProps) const handleInsert = async (e: React.FormEvent) => { e.preventDefault(); try { - if (!props.filePath) { + if (!props.filesPath) { toast.warning('Please select a file'); return; } @@ -34,22 +35,27 @@ const ButtonGroupAction: React.FC = (props: ButtonGroupProps) return; } + props.setShowLoader(true); - const insert_csv_data_response: string | boolean = await invoke('insert_csv_data', { - csv: { - table_name: props.tableName, - file_path: props.filePath, - db_driver: props.dbConfig.db_driver.toLowerCase(), - mode: props.mode, - }, - }); - - if (typeof insert_csv_data_response !== "string") { - throw new Error('Error inserting data'); + const tableName: string[] = props.tableName.split(',').map((name) => name.trim()); + + for (const [index, file] of props.filesPath.entries()) { + const insert_csv_data_response: string = await invoke('insert_csv_data', { + csv: { + table_name: tableName[index], + file_path: file, + db_driver: props.dbConfig.db_driver.toLowerCase(), + mode: props.mode, + }, + }); + + if (insert_csv_data_response.startsWith("Error:")) { + log_error(insert_csv_data_response); + } + toast.success(insert_csv_data_response); } - toast.success(insert_csv_data_response); } catch (error) { log_error(error); } @@ -65,13 +71,13 @@ const ButtonGroupAction: React.FC = (props: ButtonGroupProps) props.updateDbConfigField('db_name', ''); props.updateDbConfigField('sqlite_file_path', ''); - props.setMode('fast'); - props.setFilePath(''); + props.setMode(InsertionModeEnum.Fast); + props.setFilesPath([]); props.setTableName(''); props.setShowLoader(false); }; - const insertOk: boolean = props.filePath !== "" && props.dbConfig.is_connected; + const insertOk: boolean = props.filesPath.length > 0 && props.dbConfig.is_connected; return (
diff --git a/src/components/fileflowui/load/insert/Insert.tsx b/src/components/fileflowui/load/insert/Insert.tsx index 6e211d2..483d8f2 100644 --- a/src/components/fileflowui/load/insert/Insert.tsx +++ b/src/components/fileflowui/load/insert/Insert.tsx @@ -10,6 +10,12 @@ import ConnectionForm from "@/components/hooks/database/ConnectionForm.tsx"; import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card.tsx"; import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; + +export enum InsertionModeEnum { + Fast = "fast", + Optimized = "optimized", +} + const Insert: React.FC = () => { const [dbConfig, setDbConfig] = useState({ @@ -24,8 +30,8 @@ const Insert: React.FC = () => { is_connected: false }); - const [filePath, setFilePath] = useState(''); - const [mode, setMode] = useState<"fast" | "optimized">("fast"); + const [filesPath, setFilesPath] = useState([]); + const [mode, setMode] = useState(InsertionModeEnum.Fast); const [showLoader, setShowLoader] = useState(false); const [tableName, setTableName] = useState(''); @@ -37,11 +43,22 @@ const Insert: React.FC = () => { ); useEffect(() => { - if (filePath && filePath !== "") { - setTableName(getNormalizedTableName(filePath)); + updateTablesName(); + }, [filesPath]); + + + const updateTablesName = (): void => { + let tableMessage: string = ''; + for (const file of filesPath) { + const tableName: string = getNormalizedTableName(file); + if (file !== filesPath[filesPath.length - 1]) { + tableMessage += `${tableName}, `; + } else { + tableMessage += `${tableName}`; + } } - - }, [filePath]); + setTableName(tableMessage); + } return (
@@ -72,8 +89,9 @@ const Insert: React.FC = () => { + {/* File Upload */} - + {/* Table Name Input */} @@ -96,8 +114,8 @@ const Insert: React.FC = () => { void; + setMode: (mode: InsertionModeEnum) => void; } const ModeSelection: React.FC = (props: ModeSelectionProps) => { @@ -12,7 +13,7 @@ const ModeSelection: React.FC = (props: ModeSelectionProps)
{ - if (e === "fast" || e === "optimized") { + if (e === InsertionModeEnum.Fast || e === InsertionModeEnum.Optimized) { props.setMode(e); } } @@ -21,7 +22,7 @@ const ModeSelection: React.FC = (props: ModeSelectionProps)
- +
@@ -36,7 +37,7 @@ const ModeSelection: React.FC = (props: ModeSelectionProps)
- +
diff --git a/src/components/hooks/component/CheckBoxCombo.tsx b/src/components/hooks/component/CheckBoxCombo.tsx new file mode 100644 index 0000000..0336fd7 --- /dev/null +++ b/src/components/hooks/component/CheckBoxCombo.tsx @@ -0,0 +1,108 @@ +import * as React from "react" +import {ChevronsUpDown} from "lucide-react" + +import {Button} from "@/components/ui/button.tsx" +import {Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList,} from "@/components/ui/command.tsx" +import {Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover.tsx" +import {Checkbox} from "@/components/ui/checkbox" + +interface ComboItem { + value: string + label: string +} + +interface CheckBoxComboProps { + lists: Array + setSelectedValue: (values: string[]) => void +} + +export function CheckBoxCombo(props: CheckBoxComboProps) { + const [open, setOpen] = React.useState(false) + const [selectedValues, setSelectedValues] = React.useState([]) + + const handleSelect = (currentValue: string) => { + let updatedValues: string[] + if (selectedValues.includes(currentValue)) { + updatedValues = selectedValues.filter((val) => val !== currentValue) + } else { + updatedValues = [...selectedValues, currentValue] + } + setSelectedValues(updatedValues) + props.setSelectedValue(updatedValues) + } + + const updateText = (): string => { + if (selectedValues.length <= 0) { + return "Select items" + } + if (selectedValues.length === 1) { + const selectedItem: ComboItem | undefined = props.lists.find( + (item): boolean => item.value === selectedValues[0] + ) + return selectedItem ? selectedItem.label : "Select items" + } + if (selectedValues.length > 1) { + return `${selectedValues.length} items selected` + } + return "" + } + + const deselectAll = () => { + setSelectedValues([]) + props.setSelectedValue([]) + } + + const selectAll = () => { + const allValues: string[] = props.lists.map((item) => item.value) + setSelectedValues(allValues) + props.setSelectedValue(allValues) + } + + return ( + + + + + + + + + No item found. + + {props.lists.map((item) => ( + handleSelect(currentValue)} + > + handleSelect(item.value)} + className="mr-2" + /> + {item.label} + + ))} + + +
+ + +
+
+
+
+ ) +} diff --git a/src/components/hooks/database/DatabaseDialog.tsx b/src/components/hooks/database/DatabaseDialog.tsx index b5469f6..d1e66c7 100644 --- a/src/components/hooks/database/DatabaseDialog.tsx +++ b/src/components/hooks/database/DatabaseDialog.tsx @@ -1,150 +1,150 @@ -import React from "react"; -import { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger -} from "@/components/ui/dialog"; -import {Button} from "@/components/ui/button"; -import {Play} from "lucide-react"; -import {Label} from "@/components/ui/label"; -import {Input} from "@/components/ui/input"; -import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; - -interface DataBaseDialogProps { - dbConfig: DatabaseConfig; - sql: string; - updateDbConfigField: (field: keyof DatabaseConfig, value: DatabaseConfig[keyof DatabaseConfig]) => void; - executeSQL: () => void; -} - -const DataBaseDialog: React.FC = (props: DataBaseDialogProps) => { - - return ( -
- - {/* Trigger Button */} - - {props.sql !== "" && ( - - )} - - - {/* Dialog Content */} - - - Database Configuration - - Configure the database connection and execute your SQL query. - - - - {/* Form Layout */} -
- - - {/* First Row: Username and Password */} -
-
- - props.updateDbConfigField("username", e.target.value)} - className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" - /> -
-
- - props.updateDbConfigField("password", e.target.value)} - className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" - /> -
-
- - {/* Second Row: URL and Port */} -
-
- - props.updateDbConfigField("db_host", e.target.value)} - className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" - /> -
-
- - props.updateDbConfigField("port", e.target.value)} - className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" - /> -
-
- - {/* Third Row: Database Name */} -
- - props.updateDbConfigField("db_name", e.target.value)} - className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" - /> -
-
- - {/* Dialog Footer */} - -
- - - -
- -
-
-
-
- ); -}; - -export default DataBaseDialog; +// import React from "react"; +// import { +// Dialog, +// DialogClose, +// DialogContent, +// DialogDescription, +// DialogFooter, +// DialogHeader, +// DialogTitle, +// DialogTrigger +// } from "@/components/ui/dialog"; +// import {Button} from "@/components/ui/button"; +// import {Play} from "lucide-react"; +// import {Label} from "@/components/ui/label"; +// import {Input} from "@/components/ui/input"; +// import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; +// +// interface DataBaseDialogProps { +// dbConfig: DatabaseConfig; +// sql: string; +// updateDbConfigField: (field: keyof DatabaseConfig, value: DatabaseConfig[keyof DatabaseConfig]) => void; +// executeSQL: () => void; +// } +// +// const DataBaseDialog: React.FC = (props: DataBaseDialogProps) => { +// +// return ( +//
+// +// {/* Trigger Button */} +// +// {props.sql !== "" && ( +// +// )} +// +// +// {/* Dialog Content */} +// +// +// Database Configuration +// +// Configure the database connection and execute your SQL query. +// +// +// +// {/* Form Layout */} +//
+// +// +// {/* First Row: Username and Password */} +//
+//
+// +// props.updateDbConfigField("username", e.target.value)} +// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" +// /> +//
+//
+// +// props.updateDbConfigField("password", e.target.value)} +// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" +// /> +//
+//
+// +// {/* Second Row: URL and Port */} +//
+//
+// +// props.updateDbConfigField("db_host", e.target.value)} +// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" +// /> +//
+//
+// +// props.updateDbConfigField("port", e.target.value)} +// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" +// /> +//
+//
+// +// {/* Third Row: Database Name */} +//
+// +// props.updateDbConfigField("db_name", e.target.value)} +// className="w-full border rounded-md p-2 shadow-sm focus:ring-purple-300 focus:border-purple-500" +// /> +//
+//
+// +// {/* Dialog Footer */} +// +//
+// +// +// +//
+// +//
+//
+//
+//
+// ); +// }; +// +// export default DataBaseDialog; diff --git a/src/components/hooks/database/DatabaseForm.tsx b/src/components/hooks/database/DatabaseForm.tsx index ebdb16e..723a481 100644 --- a/src/components/hooks/database/DatabaseForm.tsx +++ b/src/components/hooks/database/DatabaseForm.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import SelectDBMS from "@/components/hooks/database/SelectDatabase.tsx"; +import SelectDBMS from "@/components/hooks/database/SelectDatabaseEngine.tsx"; import {Input} from "@/components/ui/input.tsx"; import SqliteForm from "@/components/hooks/database/SqliteForm.tsx"; import {DatabaseFormProps} from "@/interfaces/DatabaseFormProps.tsx"; diff --git a/src/components/hooks/database/SelectDatabase.tsx b/src/components/hooks/database/SelectDatabaseEngine.tsx similarity index 91% rename from src/components/hooks/database/SelectDatabase.tsx rename to src/components/hooks/database/SelectDatabaseEngine.tsx index ed11955..0466e1a 100644 --- a/src/components/hooks/database/SelectDatabase.tsx +++ b/src/components/hooks/database/SelectDatabaseEngine.tsx @@ -7,7 +7,7 @@ interface SelectDatabaseProps { updateDbConfigField: (field: keyof DatabaseConfig, value: DatabaseConfig[keyof DatabaseConfig]) => void; } -const SelectDatabase: React.FC = (props: SelectDatabaseProps) => { +const SelectDatabaseEngine: React.FC = (props: SelectDatabaseProps) => { const databaseOptions = { mysql: "MySQL", @@ -40,4 +40,4 @@ const SelectDatabase: React.FC = (props: SelectDatabaseProp ); }; -export default SelectDatabase; +export default SelectDatabaseEngine; diff --git a/src/components/hooks/database/SqliteForm.tsx b/src/components/hooks/database/SqliteForm.tsx index 2ddf366..b2004aa 100644 --- a/src/components/hooks/database/SqliteForm.tsx +++ b/src/components/hooks/database/SqliteForm.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {Input} from "@/components/ui/input.tsx"; import {Button} from "@/components/ui/button.tsx"; import {HardDrive} from "lucide-react"; -import SelectDBMS from "@/components/hooks/database/SelectDatabase.tsx"; +import SelectDBMS from "@/components/hooks/database/SelectDatabaseEngine.tsx"; import * as dialog from "@tauri-apps/plugin-dialog"; import {toast} from "sonner"; import {DatabaseConfig} from "@/interfaces/DatabaseConfig.tsx"; diff --git a/src/components/hooks/file/FileUpload.tsx b/src/components/hooks/file/FileUpload.tsx index a4a2122..3b58ac9 100644 --- a/src/components/hooks/file/FileUpload.tsx +++ b/src/components/hooks/file/FileUpload.tsx @@ -4,45 +4,52 @@ import {Button} from "@/components/ui/button.tsx"; import {FileArchive} from "lucide-react"; import {invoke} from "@tauri-apps/api/core"; import * as dialog from "@tauri-apps/plugin-dialog" -import {toast} from "sonner"; -import {getFileNameFromPath} from "@/components/hooks/utils.tsx"; +import {getFileNameFromPath, log_error} from "@/components/hooks/utils.tsx"; interface FileUploadProps { - filePath: string; - setFilePath: (path: string) => void; + filesPath: string[]; + setFilePath: (path: string[]) => void + multiple?: boolean; } const FileUpload: React.FC = (props: FileUploadProps) => { - const [fileSize, setFileSize] = React.useState(''); + const [message, setMessage] = React.useState(''); - const [fileName, setFileName] = React.useState(''); - - const openFileDialog = async () => { + const openFileDialog = async (): Promise => { try { - const selectedFilePath = await dialog.open({ + const selectedFilePath: string | null = await dialog.open({ filters: [{name: 'CSV Files', extensions: ['csv']}], - multiple: false, + multiple: props.multiple || false, directory: false, }); - setFileName(getFileNameFromPath(selectedFilePath?.toString() || '')); - - if (selectedFilePath && selectedFilePath !== fileName) { - const path: string = selectedFilePath?.toString(); + if (!selectedFilePath) { + return; + } - props.setFilePath(path) + if (Array.isArray(selectedFilePath)) { + let message: string = ''; + for (const filePath of selectedFilePath) { - const response = await invoke('get_size_of_file', {filePath: path}); + const fileName: string = getFileNameFromPath(filePath?.toString() || ''); + const fileSize: string = await invoke('get_size_of_file', {filePath: filePath?.toString() || ''}); + message += `${fileName} (${fileSize})`; - if (typeof response !== 'string') { - throw new Error('Error getting file size'); + if (filePath !== selectedFilePath[selectedFilePath.length - 1]) { + message += ', '; + } } - - setFileSize(response); + setMessage(message); + props.setFilePath(selectedFilePath); + } else { + const fileName: string = getFileNameFromPath(selectedFilePath); + const fileSize: string = await invoke('get_size_of_file', {filePath: selectedFilePath}); + setMessage(`${fileName} (${fileSize})`); + props.setFilePath([selectedFilePath]); } } catch (error) { - toast.error(`Error opening file`); + log_error(error); } }; @@ -62,7 +69,7 @@ const FileUpload: React.FC = (props: FileUploadProps) => {
= (props: SaveConfigDialo Cancel - +