From 686490ded1e94030efbe9b895f64926b0e4395d7 Mon Sep 17 00:00:00 2001 From: coolcoala Date: Fri, 4 Jul 2025 02:28:27 +0300 Subject: [PATCH] New Interface (initial commit) --- components.json | 21 + lib/utils.ts | 6 + package.json | 108 +- pnpm-lock.yaml | 2120 +++++++++++++++-- src/App.tsx | 2 - src/components/base/NoticeManager.tsx | 76 +- src/components/base/base-dialog.tsx | 78 +- src/components/base/base-empty.tsx | 31 +- src/components/base/base-error-boundary.tsx | 26 +- src/components/base/base-fieldset.tsx | 46 +- src/components/base/base-loading-overlay.tsx | 29 +- src/components/base/base-loading.tsx | 52 +- src/components/base/base-page.tsx | 58 +- src/components/base/base-search-box.tsx | 194 +- src/components/base/base-styled-select.tsx | 50 +- .../base/base-styled-text-field.tsx | 40 +- src/components/base/base-switch.tsx | 79 +- src/components/base/base-tooltip-icon.tsx | 60 +- .../connection/connection-detail.tsx | 80 +- src/components/connection/connection-item.tsx | 104 +- .../connection/connection-table.tsx | 287 +-- src/components/home/proxy-selectors.tsx | 265 +++ src/components/layout/layout-item.tsx | 80 +- src/components/layout/layout-traffic.tsx | 108 +- src/components/layout/scroll-top-button.tsx | 45 +- src/components/layout/traffic-graph.tsx | 18 +- src/components/layout/update-button.tsx | 7 +- src/components/layout/use-custom-theme.ts | 158 +- src/components/log/log-item.tsx | 73 +- src/components/profile/confirm-viewer.tsx | 64 +- src/components/profile/editor-viewer.tsx | 198 +- src/components/profile/file-input.tsx | 74 +- src/components/profile/group-item.tsx | 208 +- .../profile/groups-editor-viewer.tsx | 1301 ++++------ src/components/profile/log-viewer.tsx | 88 +- src/components/profile/profile-box.tsx | 87 +- src/components/profile/profile-item.tsx | 909 +++---- src/components/profile/profile-more.tsx | 206 +- src/components/profile/profile-viewer.tsx | 595 ++--- .../profile/proxies-editor-viewer.tsx | 618 ++--- src/components/profile/proxy-item.tsx | 160 +- src/components/profile/rule-item.tsx | 194 +- .../profile/rules-editor-viewer.tsx | 1026 ++++---- src/components/proxy/provider-button.tsx | 290 +-- src/components/proxy/proxy-groups.tsx | 813 ++----- src/components/proxy/proxy-head.tsx | 308 ++- src/components/proxy/proxy-item-mini.tsx | 278 +-- src/components/proxy/proxy-item.tsx | 255 +- src/components/proxy/proxy-render.tsx | 280 +-- src/components/rule/provider-button.tsx | 369 ++- src/components/rule/rule-item.tsx | 96 +- .../setting/mods/backup-config-viewer.tsx | 325 +-- .../setting/mods/backup-table-viewer.tsx | 298 ++- src/components/setting/mods/backup-viewer.tsx | 149 +- .../setting/mods/clash-core-viewer.tsx | 171 +- .../setting/mods/clash-port-viewer.tsx | 417 ++-- src/components/setting/mods/config-viewer.tsx | 19 +- .../setting/mods/controller-viewer.tsx | 247 +- src/components/setting/mods/dns-viewer.tsx | 1190 ++------- src/components/setting/mods/hotkey-input.tsx | 152 +- src/components/setting/mods/hotkey-viewer.tsx | 114 +- src/components/setting/mods/layout-viewer.tsx | 491 ++-- .../setting/mods/lite-mode-viewer.tsx | 188 +- src/components/setting/mods/misc-viewer.tsx | 307 +-- .../setting/mods/network-interface-viewer.tsx | 221 +- .../setting/mods/password-input.tsx | 93 +- src/components/setting/mods/setting-comp.tsx | 108 +- .../setting/mods/stack-mode-switch.tsx | 51 +- .../setting/mods/sysproxy-viewer.tsx | 707 ++---- .../setting/mods/theme-mode-switch.tsx | 20 +- src/components/setting/mods/theme-viewer.tsx | 219 +- src/components/setting/mods/tun-viewer.tsx | 275 +-- src/components/setting/mods/update-viewer.tsx | 174 +- src/components/setting/mods/web-ui-item.tsx | 195 +- src/components/setting/mods/web-ui-viewer.tsx | 148 +- src/components/setting/setting-clash.tsx | 289 +-- src/components/setting/setting-system.tsx | 349 +-- .../setting/setting-verge-advanced.tsx | 181 +- .../setting/setting-verge-basic.tsx | 318 +-- .../shared/ProxyControlSwitches.tsx | 294 +-- src/components/test/test-box.tsx | 75 +- src/components/test/test-item.tsx | 261 +- src/components/test/test-viewer.tsx | 207 +- src/components/ui/alert-dialog.tsx | 155 ++ src/components/ui/alert.tsx | 66 + src/components/ui/badge.tsx | 46 + src/components/ui/button.tsx | 59 + src/components/ui/card.tsx | 92 + src/components/ui/command.tsx | 182 ++ src/components/ui/context-menu.tsx | 250 ++ src/components/ui/dialog.tsx | 141 ++ src/components/ui/dropdown-menu.tsx | 255 ++ src/components/ui/form.tsx | 166 ++ src/components/ui/input.tsx | 21 + src/components/ui/label.tsx | 22 + src/components/ui/popover.tsx | 46 + src/components/ui/progress.tsx | 29 + src/components/ui/select.tsx | 183 ++ src/components/ui/separator.tsx | 26 + src/components/ui/sheet.tsx | 137 ++ src/components/ui/sonner.tsx | 23 + src/components/ui/switch.tsx | 29 + src/components/ui/table.tsx | 114 + src/components/ui/textarea.tsx | 18 + src/components/ui/tooltip.tsx | 59 + src/index.css | 127 + src/locales/ru.json | 8 +- src/main.tsx | 1 + src/pages/_layout.tsx | 87 +- src/pages/_routers.tsx | 14 +- src/pages/connections.tsx | 290 +-- src/pages/home.tsx | 542 ++--- src/pages/logs.tsx | 243 +- src/pages/profiles.tsx | 952 +++----- src/pages/proxies.tsx | 89 +- src/pages/rules.tsx | 176 +- src/pages/settings.tsx | 230 +- src/pages/test.tsx | 269 +-- src/services/i18n.ts | 4 +- tsconfig.json | 2 +- vite.config.mts | 2 + 121 files changed, 12852 insertions(+), 13274 deletions(-) create mode 100644 components.json create mode 100644 lib/utils.ts create mode 100644 src/components/home/proxy-selectors.tsx create mode 100644 src/components/ui/alert-dialog.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/context-menu.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/sheet.tsx create mode 100644 src/components/ui/sonner.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/table.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/components/ui/tooltip.tsx create mode 100644 src/index.css diff --git a/components.json b/components.json new file mode 100644 index 00000000..df0f847d --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@root/src/components", + "utils": "@root/lib/utils", + "ui": "@root/src/components/ui", + "lib": "@root/lib", + "hooks": "@root/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 00000000..a5ef1935 --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/package.json b/package.json index edd7e232..0bd19749 100644 --- a/package.json +++ b/package.json @@ -29,77 +29,117 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.1", + "@emotion/styled": "^11.14.0", + "@hookform/resolvers": "^5.1.1", "@juggle/resize-observer": "^3.4.0", - "@mui/icons-material": "^7.1.2", - "@mui/lab": "7.0.0-beta.14", - "@mui/material": "^7.1.2", - "@mui/x-data-grid": "^8.6.0", - "@tauri-apps/api": "2.6.0", - "@tauri-apps/plugin-clipboard-manager": "^2.3.0", - "@tauri-apps/plugin-dialog": "^2.3.0", - "@tauri-apps/plugin-fs": "^2.4.0", - "@tauri-apps/plugin-global-shortcut": "^2.3.0", - "@tauri-apps/plugin-notification": "^2.3.0", - "@tauri-apps/plugin-process": "^2.3.0", - "@tauri-apps/plugin-shell": "2.3.0", - "@tauri-apps/plugin-updater": "2.9.0", - "@tauri-apps/plugin-window-state": "^2.3.0", + "@mui/icons-material": "^7.1.1", + "@mui/lab": "7.0.0-beta.13", + "@mui/material": "^7.1.1", + "@mui/x-data-grid": "^8.5.1", + "@radix-ui/react-alert-dialog": "^1.1.14", + "@radix-ui/react-context-menu": "^2.2.15", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-icons": "^1.3.2", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-progress": "^1.1.7", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.5", + "@radix-ui/react-tooltip": "^1.2.7", + "@tailwindcss/vite": "^4.1.11", + "@tanstack/react-table": "^8.21.3", + "@tauri-apps/api": "2.5.0", + "@tauri-apps/plugin-clipboard-manager": "^2.2.2", + "@tauri-apps/plugin-dialog": "^2.2.2", + "@tauri-apps/plugin-fs": "^2.3.0", + "@tauri-apps/plugin-global-shortcut": "^2.2.1", + "@tauri-apps/plugin-notification": "^2.2.2", + "@tauri-apps/plugin-process": "^2.2.1", + "@tauri-apps/plugin-shell": "2.2.1", + "@tauri-apps/plugin-updater": "2.7.1", + "@tauri-apps/plugin-window-state": "^2.2.2", + "@types/d3-shape": "^3.1.7", "@types/json-schema": "^7.0.15", "ahooks": "^3.8.5", - "axios": "^1.10.0", - "chart.js": "^4.5.0", + "axios": "^1.9.0", + "chart.js": "^4.4.9", + "class-variance-authority": "^0.7.1", "cli-color": "^2.0.4", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "d3-shape": "^3.2.0", "dayjs": "1.11.13", - "foxact": "^0.2.49", - "glob": "^11.0.3", + "foxact": "^0.2.45", + "glob": "^11.0.2", "i18next": "^25.2.1", + "js-base64": "^3.7.7", "js-yaml": "^4.1.0", - "json-schema": "^0.4.0", "lodash-es": "^4.17.21", + "lucide-react": "^0.514.0", "monaco-editor": "^0.52.2", "monaco-yaml": "^5.4.0", "nanoid": "^5.1.5", + "next-themes": "^0.4.6", + "peggy": "^5.0.3", "react": "19.1.0", "react-chartjs-2": "^5.3.0", "react-dom": "19.1.0", "react-error-boundary": "6.0.0", - "react-hook-form": "^7.58.1", - "react-i18next": "15.5.3", + "react-hook-form": "^7.57.0", + "react-i18next": "15.5.2", "react-markdown": "10.1.0", "react-monaco-editor": "0.58.0", "react-router-dom": "7.6.2", - "react-virtuoso": "^4.13.0", + "react-virtuoso": "^4.12.8", "sockette": "^2.0.6", + "sonner": "^2.0.5", "swr": "^2.3.3", + "tailwind-merge": "^3.3.1", "tar": "^7.4.3", "types-pac": "^1.0.3", - "zustand": "^5.0.6" + "zod": "^3.25.67", + "zustand": "^5.0.5" }, "devDependencies": { "@actions/github": "^6.0.1", - "@tauri-apps/cli": "2.6.1", + "@tauri-apps/cli": "2.5.0", + "@types/js-cookie": "^3.0.6", "@types/js-yaml": "^4.0.9", "@types/lodash-es": "^4.17.12", - "@types/react": "19.1.8", + "@types/node": "^24.0.0", + "@types/react": "19.1.6", "@types/react-dom": "19.1.6", - "@vitejs/plugin-legacy": "^7.0.0", - "@vitejs/plugin-react": "4.6.0", + "@vitejs/plugin-legacy": "^6.1.1", + "@vitejs/plugin-react": "4.5.1", "adm-zip": "^0.5.16", + "autoprefixer": "^10.4.21", "commander": "^14.0.0", "cross-env": "^7.0.3", "https-proxy-agent": "^7.0.6", - "meta-json-schema": "^1.19.11", + "husky": "^9.1.7", + "meta-json-schema": "^1.19.10", "node-fetch": "^3.3.2", - "prettier": "^3.6.2", - "prettier-plugin-organize-imports": "^4.1.0", - "sass": "^1.89.2", - "terser": "^5.43.1", + "postcss": "^8.5.4", + "prettier": "^3.5.3", + "pretty-quick": "^4.2.2", + "sass": "^1.89.1", + "tailwindcss": "^4.1.11", + "terser": "^5.41.0", + "tw-animate-css": "^1.3.4", "typescript": "^5.8.3", - "vite": "^7.0.0", + "vite": "^6.3.5", "vite-plugin-monaco-editor": "^1.1.0", "vite-plugin-svgr": "^4.3.0" }, + "prettier": { + "tabWidth": 2, + "semi": true, + "singleQuote": false, + "endOfLine": "lf" + }, "type": "module", "packageManager": "pnpm@9.13.2" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f287a62..3f2ae253 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,55 +19,106 @@ importers: version: 3.2.2(react@19.1.0) '@emotion/react': specifier: ^11.14.0 - version: 11.14.0(@types/react@19.1.8)(react@19.1.0) + version: 11.14.0(@types/react@19.1.6)(react@19.1.0) '@emotion/styled': - specifier: ^11.14.1 - version: 11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) + specifier: ^11.14.0 + version: 11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) + '@hookform/resolvers': + specifier: ^5.1.1 + version: 5.1.1(react-hook-form@7.58.1(react@19.1.0)) '@juggle/resize-observer': specifier: ^3.4.0 version: 3.4.0 '@mui/icons-material': - specifier: ^7.1.2 - version: 7.1.2(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) + specifier: ^7.1.1 + version: 7.1.2(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) '@mui/lab': - specifier: 7.0.0-beta.14 - version: 7.0.0-beta.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: 7.0.0-beta.13 + version: 7.0.0-beta.13(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@mui/material': - specifier: ^7.1.2 - version: 7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^7.1.1 + version: 7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@mui/x-data-grid': - specifier: ^8.6.0 - version: 8.6.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^8.5.1 + version: 8.6.0(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-alert-dialog': + specifier: ^1.1.14 + version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-context-menu': + specifier: ^2.2.15 + version: 2.2.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dialog': + specifier: ^1.1.14 + version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.15 + version: 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-icons': + specifier: ^1.3.2 + version: 1.3.2(react@19.1.0) + '@radix-ui/react-label': + specifier: ^2.1.7 + version: 2.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popover': + specifier: ^1.1.14 + version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-progress': + specifier: ^1.1.7 + version: 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-select': + specifier: ^2.2.5 + version: 2.2.5(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-separator': + specifier: ^1.1.7 + version: 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.3(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-switch': + specifier: ^1.2.5 + version: 1.2.5(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-tooltip': + specifier: ^1.2.7 + version: 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tailwindcss/vite': + specifier: ^4.1.11 + version: 4.1.11(vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)) + '@tanstack/react-table': + specifier: ^8.21.3 + version: 8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tauri-apps/api': - specifier: 2.6.0 - version: 2.6.0 + specifier: 2.5.0 + version: 2.5.0 '@tauri-apps/plugin-clipboard-manager': - specifier: ^2.3.0 + specifier: ^2.2.2 version: 2.3.0 '@tauri-apps/plugin-dialog': - specifier: ^2.3.0 + specifier: ^2.2.2 version: 2.3.0 '@tauri-apps/plugin-fs': - specifier: ^2.4.0 + specifier: ^2.3.0 version: 2.4.0 '@tauri-apps/plugin-global-shortcut': - specifier: ^2.3.0 + specifier: ^2.2.1 version: 2.3.0 '@tauri-apps/plugin-notification': - specifier: ^2.3.0 + specifier: ^2.2.2 version: 2.3.0 '@tauri-apps/plugin-process': - specifier: ^2.3.0 + specifier: ^2.2.1 version: 2.3.0 '@tauri-apps/plugin-shell': - specifier: 2.3.0 - version: 2.3.0 + specifier: 2.2.1 + version: 2.2.1 '@tauri-apps/plugin-updater': - specifier: 2.9.0 - version: 2.9.0 + specifier: 2.7.1 + version: 2.7.1 '@tauri-apps/plugin-window-state': - specifier: ^2.3.0 + specifier: ^2.2.2 version: 2.3.0 + '@types/d3-shape': + specifier: ^3.1.7 + version: 3.1.7 '@types/json-schema': specifier: ^7.0.15 version: 7.0.15 @@ -75,35 +126,50 @@ importers: specifier: ^3.8.5 version: 3.8.5(react@19.1.0) axios: - specifier: ^1.10.0 + specifier: ^1.9.0 version: 1.10.0 chart.js: - specifier: ^4.5.0 + specifier: ^4.4.9 version: 4.5.0 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 cli-color: specifier: ^2.0.4 version: 2.0.4 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + cmdk: + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + d3-shape: + specifier: ^3.2.0 + version: 3.2.0 dayjs: specifier: 1.11.13 version: 1.11.13 foxact: - specifier: ^0.2.49 + specifier: ^0.2.45 version: 0.2.49(react@19.1.0) glob: - specifier: ^11.0.3 + specifier: ^11.0.2 version: 11.0.3 i18next: specifier: ^25.2.1 version: 25.2.1(typescript@5.8.3) + js-base64: + specifier: ^3.7.7 + version: 3.7.7 js-yaml: specifier: ^4.1.0 version: 4.1.0 - json-schema: - specifier: ^0.4.0 - version: 0.4.0 lodash-es: specifier: ^4.17.21 version: 4.17.21 + lucide-react: + specifier: ^0.514.0 + version: 0.514.0(react@19.1.0) monaco-editor: specifier: ^0.52.2 version: 0.52.2 @@ -113,6 +179,12 @@ importers: nanoid: specifier: ^5.1.5 version: 5.1.5 + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + peggy: + specifier: ^5.0.3 + version: 5.0.4 react: specifier: 19.1.0 version: 19.1.0 @@ -126,14 +198,14 @@ importers: specifier: 6.0.0 version: 6.0.0(react@19.1.0) react-hook-form: - specifier: ^7.58.1 + specifier: ^7.57.0 version: 7.58.1(react@19.1.0) react-i18next: - specifier: 15.5.3 - version: 15.5.3(i18next@25.2.1(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) + specifier: 15.5.2 + version: 15.5.2(i18next@25.2.1(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) react-markdown: specifier: 10.1.0 - version: 10.1.0(@types/react@19.1.8)(react@19.1.0) + version: 10.1.0(@types/react@19.1.6)(react@19.1.0) react-monaco-editor: specifier: 0.58.0 version: 0.58.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -141,51 +213,69 @@ importers: specifier: 7.6.2 version: 7.6.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-virtuoso: - specifier: ^4.13.0 + specifier: ^4.12.8 version: 4.13.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) sockette: specifier: ^2.0.6 version: 2.0.6 + sonner: + specifier: ^2.0.5 + version: 2.0.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) swr: specifier: ^2.3.3 version: 2.3.3(react@19.1.0) + tailwind-merge: + specifier: ^3.3.1 + version: 3.3.1 tar: specifier: ^7.4.3 version: 7.4.3 types-pac: specifier: ^1.0.3 version: 1.0.3 + zod: + specifier: ^3.25.67 + version: 3.25.71 zustand: - specifier: ^5.0.6 - version: 5.0.6(@types/react@19.1.8)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)) + specifier: ^5.0.5 + version: 5.0.6(@types/react@19.1.6)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)) devDependencies: '@actions/github': specifier: ^6.0.1 version: 6.0.1 '@tauri-apps/cli': - specifier: 2.6.1 - version: 2.6.1 + specifier: 2.5.0 + version: 2.5.0 + '@types/js-cookie': + specifier: ^3.0.6 + version: 3.0.6 '@types/js-yaml': specifier: ^4.0.9 version: 4.0.9 '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 + '@types/node': + specifier: ^24.0.0 + version: 24.0.10 '@types/react': - specifier: 19.1.8 - version: 19.1.8 + specifier: 19.1.6 + version: 19.1.6 '@types/react-dom': specifier: 19.1.6 - version: 19.1.6(@types/react@19.1.8) + version: 19.1.6(@types/react@19.1.6) '@vitejs/plugin-legacy': - specifier: ^7.0.0 - version: 7.0.0(terser@5.43.1)(vite@7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)) + specifier: ^6.1.1 + version: 6.1.1(terser@5.43.1)(vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)) '@vitejs/plugin-react': - specifier: 4.6.0 - version: 4.6.0(vite@7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)) + specifier: 4.5.1 + version: 4.5.1(vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)) adm-zip: specifier: ^0.5.16 version: 0.5.16 + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.6) commander: specifier: ^14.0.0 version: 14.0.0 @@ -195,36 +285,48 @@ importers: https-proxy-agent: specifier: ^7.0.6 version: 7.0.6 + husky: + specifier: ^9.1.7 + version: 9.1.7 meta-json-schema: - specifier: ^1.19.11 + specifier: ^1.19.10 version: 1.19.11 node-fetch: specifier: ^3.3.2 version: 3.3.2 + postcss: + specifier: ^8.5.4 + version: 8.5.6 prettier: - specifier: ^3.6.2 + specifier: ^3.5.3 version: 3.6.2 - prettier-plugin-organize-imports: - specifier: ^4.1.0 - version: 4.1.0(prettier@3.6.2)(typescript@5.8.3) + pretty-quick: + specifier: ^4.2.2 + version: 4.2.2(prettier@3.6.2) sass: - specifier: ^1.89.2 + specifier: ^1.89.1 version: 1.89.2 + tailwindcss: + specifier: ^4.1.11 + version: 4.1.11 terser: - specifier: ^5.43.1 + specifier: ^5.41.0 version: 5.43.1 + tw-animate-css: + specifier: ^1.3.4 + version: 1.3.5 typescript: specifier: ^5.8.3 version: 5.8.3 vite: - specifier: ^7.0.0 - version: 7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) + specifier: ^6.3.5 + version: 6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) vite-plugin-monaco-editor: specifier: ^1.1.0 version: 1.1.0(monaco-editor@0.52.2) vite-plugin-svgr: specifier: ^4.3.0 - version: 4.3.0(rollup@4.40.2)(typescript@5.8.3)(vite@7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)) + version: 4.3.0(rollup@4.40.2)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)) packages: @@ -965,6 +1067,26 @@ packages: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} + '@floating-ui/core@1.7.2': + resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} + + '@floating-ui/dom@1.7.2': + resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==} + + '@floating-ui/react-dom@2.1.4': + resolution: {integrity: sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@hookform/resolvers@5.1.1': + resolution: {integrity: sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==} + peerDependencies: + react-hook-form: ^7.55.0 + '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -1022,13 +1144,13 @@ packages: '@types/react': optional: true - '@mui/lab@7.0.0-beta.14': - resolution: {integrity: sha512-pn+ZvylDcBKQOo17oa/PhtIA/UFQFq8RvpN+r/jHrztz/CjMDju2CWBne0txvQ5JIS8uTIGp2/IsTa7II1g5wg==} + '@mui/lab@7.0.0-beta.13': + resolution: {integrity: sha512-wLSeePenug3+/kek4cFMIF3QZVC2fHt2Z3O3HwOFvakgErmT39WltYsNpWNojCnXUqcIExUp9xNW0Wk+tJShgA==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.5.0 '@emotion/styled': ^11.3.0 - '@mui/material': ^7.1.2 + '@mui/material': ^7.1.1 '@mui/material-pigment-css': ^7.1.1 '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -1273,11 +1395,445 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} + '@peggyjs/from-mem@2.0.1': + resolution: {integrity: sha512-5dAPJsLrb3KQahPb8kUqg9nGS2dKlMC4vCB3dMWoZIRqmPrNbBt6P6jidczFBoz+2EbFXBxXi0o9BUpEPHoD+g==} + engines: {node: '>=20'} + + '@pkgr/core@0.2.7': + resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@rolldown/pluginutils@1.0.0-beta.19': - resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==} + '@radix-ui/number@1.1.1': + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + + '@radix-ui/primitive@1.1.2': + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + + '@radix-ui/react-alert-dialog@1.1.14': + resolution: {integrity: sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ==} + 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-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + 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.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + 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-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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context-menu@2.2.15': + resolution: {integrity: sha512-UsQUMjcYTsBjTSXw0P3GO0werEQvUY2plgRQuKoCTtkNr45q1DiL51j4m7gxhABzZ0BadoXNsIbg7F3KwiUBbw==} + 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-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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.14': + resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} + 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-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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.10': + resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} + 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-dropdown-menu@2.1.15': + resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==} + 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-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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + 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-icons@1.3.2': + resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc + + '@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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-label@2.1.7': + resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} + 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-menu@2.1.15': + resolution: {integrity: sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==} + 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-popover@1.1.14': + resolution: {integrity: sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==} + 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-popper@1.2.7': + resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} + 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-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + 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-presence@1.1.4': + resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} + 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-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + 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-progress@1.1.7': + resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} + 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-roving-focus@1.1.10': + resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==} + 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-select@2.2.5': + resolution: {integrity: sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==} + 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-separator@1.1.7': + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} + 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-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.2.5': + resolution: {integrity: sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==} + 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-tooltip@1.2.7': + resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} + 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-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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@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 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + 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/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@rolldown/pluginutils@1.0.0-beta.9': + resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==} '@rollup/pluginutils@5.1.4': resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} @@ -1388,6 +1944,9 @@ packages: cpu: [x64] os: [win32] + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} @@ -1456,77 +2015,181 @@ packages: peerDependencies: '@svgr/core': '*' - '@tauri-apps/api@2.6.0': - resolution: {integrity: sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==} + '@tailwindcss/node@4.1.11': + resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==} - '@tauri-apps/cli-darwin-arm64@2.6.1': - resolution: {integrity: sha512-67aVLeXcJrl9D+4xQLR8NCQYKXQyZB96Tai+uy53jOY0u+uKvFngjbSS9CmntILFhLWoueDxQj3Ws5OSvjekiA==} + '@tailwindcss/oxide-android-arm64@4.1.11': + resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.11': + resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tauri-apps/cli-darwin-x64@2.6.1': - resolution: {integrity: sha512-6mQp/VikM0pKVKau1p/NMVyGq1JakOFbk6YlCzwdd04OwmiDuVe9PTxzqcA/JnqWXEhSGlRKeyMXUxqifPBPYw==} + '@tailwindcss/oxide-darwin-x64@4.1.11': + resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tauri-apps/cli-linux-arm-gnueabihf@2.6.1': - resolution: {integrity: sha512-iY9cr2k3h2nh6I0bExEWCOgWloN4q0p/evhfNBIAIly1kycy+xWHEsFj2WfzU31Ce8RkqxilPHNuCp36gDM+Yw==} + '@tailwindcss/oxide-freebsd-x64@4.1.11': + resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': + resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tauri-apps/cli-linux-arm64-gnu@2.6.1': - resolution: {integrity: sha512-AT+SpHShi/uJipzj+A9kHPDUgZfzHYyl0B3/4UXYtHB9hQfQUmjh9wLadmx0ai/ESyJFxkJJK6FFRvxotQ3gIQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': + resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-arm64-musl@2.6.1': - resolution: {integrity: sha512-CCgE0uqcWTtU48TmK/J03h5x/hxi3PEc11ci3LQ9WqIE15btyhynNzDKbxKwOyDCMcfbDT+MweoiCPnely2ZRA==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.11': + resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-riscv64-gnu@2.6.1': - resolution: {integrity: sha512-2i3MYr2oKaJk7Tz2FZ+R5x8cV827CudvEXW6FzCER26pfx/PofH/55N7aRQf4+qPXi5O3T+3myq88nRerzHr6g==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.11': + resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.11': + resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.11': + resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': + resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.11': + resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.11': + resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.11': + resolution: {integrity: sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@tanstack/react-table@8.21.3': + resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/table-core@8.21.3': + resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} + engines: {node: '>=12'} + + '@tauri-apps/api@2.5.0': + resolution: {integrity: sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA==} + + '@tauri-apps/api@2.6.0': + resolution: {integrity: sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==} + + '@tauri-apps/cli-darwin-arm64@2.5.0': + resolution: {integrity: sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tauri-apps/cli-darwin-x64@2.5.0': + resolution: {integrity: sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tauri-apps/cli-linux-arm-gnueabihf@2.5.0': + resolution: {integrity: sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tauri-apps/cli-linux-arm64-gnu@2.5.0': + resolution: {integrity: sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-arm64-musl@2.5.0': + resolution: {integrity: sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-riscv64-gnu@2.5.0': + resolution: {integrity: sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - '@tauri-apps/cli-linux-x64-gnu@2.6.1': - resolution: {integrity: sha512-of5i3FW1tjTXECtMdirQsQnVIzcKq1s5Cad9YLVa6agEnFnZbFU3rKQ6/Wfu5SZbjSiI7SBO1hksjIAq1vkVcg==} + '@tauri-apps/cli-linux-x64-gnu@2.5.0': + resolution: {integrity: sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-linux-x64-musl@2.6.1': - resolution: {integrity: sha512-QfAgg7VIlOrTa2X3fBLRp6ugbC5ZonSWgVhNCLDgHLp5Cga41XOi5qU7ZtZhjjCdcWNiE0DtBQmSb1YVhCp+SA==} + '@tauri-apps/cli-linux-x64-musl@2.5.0': + resolution: {integrity: sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-win32-arm64-msvc@2.6.1': - resolution: {integrity: sha512-ee1h3jwamumGjLEXZA4VsSUcVWTtGpuvxy+nqFfu7wb2k6IcBrFEJGa6yXa7sQjuCAAorLfSIXGuDEJARqnpFw==} + '@tauri-apps/cli-win32-arm64-msvc@2.5.0': + resolution: {integrity: sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tauri-apps/cli-win32-ia32-msvc@2.6.1': - resolution: {integrity: sha512-WEZVYekxuG9X9mFJEeJPHsSAl8sHOEEbJQriim+OziFbLA9pv/gfcRRqsbmEJL8uBC8GLTEOdUVolohwXk6S9g==} + '@tauri-apps/cli-win32-ia32-msvc@2.5.0': + resolution: {integrity: sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@tauri-apps/cli-win32-x64-msvc@2.6.1': - resolution: {integrity: sha512-fBsjPqIIHaaQt7tnjIGmPHu5p/BNBVD4JfOhO3QqIVBzAb+W2bDyIQPdoDMI943soLr/+N10xeTiPu+3L74+dQ==} + '@tauri-apps/cli-win32-x64-msvc@2.5.0': + resolution: {integrity: sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tauri-apps/cli@2.6.1': - resolution: {integrity: sha512-8NrwfZjeyKH1zwg+Xu4epx8WLjffoiW1Zs9CCFYCJns7uUghzudDm92o+8ROosg5Njlvp1GXBuIRsdrEwBsDhg==} + '@tauri-apps/cli@2.5.0': + resolution: {integrity: sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg==} engines: {node: '>= 10'} hasBin: true @@ -1548,11 +2211,11 @@ packages: '@tauri-apps/plugin-process@2.3.0': resolution: {integrity: sha512-0DNj6u+9csODiV4seSxxRbnLpeGYdojlcctCuLOCgpH9X3+ckVZIEj6H7tRQ7zqWr7kSTEWnrxtAdBb0FbtrmQ==} - '@tauri-apps/plugin-shell@2.3.0': - resolution: {integrity: sha512-6GIRxO2z64uxPX4CCTuhQzefvCC0ew7HjdBhMALiGw74vFBDY95VWueAHOHgNOMV4UOUAFupyidN9YulTe5xlA==} + '@tauri-apps/plugin-shell@2.2.1': + resolution: {integrity: sha512-G1GFYyWe/KlCsymuLiNImUgC8zGY0tI0Y3p8JgBCWduR5IEXlIJS+JuG1qtveitwYXlfJrsExt3enhv5l2/yhA==} - '@tauri-apps/plugin-updater@2.9.0': - resolution: {integrity: sha512-j++sgY8XpeDvzImTrzWA08OqqGqgkNyxczLD7FjNJJx/uXxMZFz5nDcfkyoI/rCjYuj2101Tci/r/HFmOmoxCg==} + '@tauri-apps/plugin-updater@2.7.1': + resolution: {integrity: sha512-1OPqEY/z7NDVSeTEMIhD2ss/vXWdpfZ5Th2Mk0KtPR/RA6FKuOTDGZQhxoyYBk0pcZJ+nNZUbl/IujDCLBApjA==} '@tauri-apps/plugin-window-state@2.3.0': resolution: {integrity: sha512-BfhM0gm6jsF+VyYeOZ3eNqDfRJ2YPMh0a5qesD8AizVGv7wVB3cGLqh6EaeMpGl7y6kP9lvEMYkDRHuqKbcsgw==} @@ -1569,6 +2232,12 @@ packages: '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1581,6 +2250,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/js-cookie@3.0.6': + resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} @@ -1599,6 +2271,9 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@24.0.10': + resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} + '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1615,8 +2290,8 @@ packages: peerDependencies: '@types/react': '*' - '@types/react@19.1.8': - resolution: {integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==} + '@types/react@19.1.6': + resolution: {integrity: sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1627,18 +2302,18 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@vitejs/plugin-legacy@7.0.0': - resolution: {integrity: sha512-qevhyYFUeZXBd/bAZGwpBgyn4GGAYije9YPV8Jg07newPCZtFEIlFlzsQowPbm87iKekOIL/90wKn+hvGkjzkg==} - engines: {node: ^20.19.0 || >=22.12.0} + '@vitejs/plugin-legacy@6.1.1': + resolution: {integrity: sha512-BvusL+mYZ0q5qS5Rq3D70QxZBmhyiHRaXLtYJHH5AEsAmdSqJR4xe5KwMi1H3w8/9lVJwhkLYqFQ9vmWYWy6kA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} peerDependencies: terser: ^5.16.0 - vite: ^7.0.0 + vite: ^6.0.0 - '@vitejs/plugin-react@4.6.0': - resolution: {integrity: sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==} + '@vitejs/plugin-react@4.5.1': + resolution: {integrity: sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 acorn@8.14.1: resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} @@ -1678,9 +2353,20 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + axios@1.10.0: resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} @@ -1770,6 +2456,9 @@ packages: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-color@2.0.4: resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==} engines: {node: '>=0.10'} @@ -1781,6 +2470,12 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cmdk@1.1.1: + resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1843,6 +2538,14 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + d@1.0.2: resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} engines: {node: '>=0.12'} @@ -1882,6 +2585,13 @@ packages: engines: {node: '>=0.10'} hasBin: true + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -1907,6 +2617,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enhanced-resolve@5.18.2: + resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2028,6 +2742,9 @@ packages: react: optional: true + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2044,6 +2761,10 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -2061,6 +2782,9 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -2092,6 +2816,11 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + i18next@25.2.1: resolution: {integrity: sha512-+UoXK5wh+VlE1Zy5p6MjcvctHXAhRwQKCxiJD8noKZzIXmnAX8gdHX5fLPA3MEVxEN4vbZkQFy8N0LyD9tUqPw==} peerDependencies: @@ -2100,6 +2829,10 @@ packages: typescript: optional: true + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + immutable@5.1.2: resolution: {integrity: sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==} @@ -2162,6 +2895,13 @@ packages: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + js-base64@3.7.7: + resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} + js-cookie@3.0.5: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} @@ -2186,9 +2926,6 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -2197,6 +2934,70 @@ packages: jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -2229,6 +3030,11 @@ packages: lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} + lucide-react@0.514.0: + resolution: {integrity: sha512-HXD0OAMd+JM2xCjlwG1EGW9Nuab64dhjO3+MvdyD+pSUeOTBaVAPhQblKIYmmX4RyBYbdzW0VWnJpjJmxWGr6w==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -2386,6 +3192,10 @@ packages: peerDependencies: monaco-editor: '>=0.36' + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2399,6 +3209,12 @@ packages: engines: {node: ^18 || >=20} hasBin: true + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} @@ -2420,6 +3236,10 @@ packages: node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -2459,6 +3279,11 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + peggy@5.0.4: + resolution: {integrity: sha512-NMRm2w2irCFbiOaejvcDEyn+DMUaGd8s4RT1ztj9Kr/kR367pziIvmjqJ0OFqcAg+LqT5tPGsW96MNT5gUNdUw==} + engines: {node: '>=20'} + hasBin: true + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2470,25 +3295,25 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - prettier-plugin-organize-imports@4.1.0: - resolution: {integrity: sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==} - peerDependencies: - prettier: '>=2.0' - typescript: '>=2.9' - vue-tsc: ^2.1.0 - peerDependenciesMeta: - vue-tsc: - optional: true - prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true + pretty-quick@4.2.2: + resolution: {integrity: sha512-uAh96tBW1SsD34VhhDmWuEmqbpfYc/B3j++5MC/6b3Cb8Ow7NJsvKFhg0eoGu2xXX+o9RkahkTK6sUdd8E7g5w==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + prettier: ^3.0.0 + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -2523,8 +3348,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - react-i18next@15.5.3: - resolution: {integrity: sha512-ypYmOKOnjqPEJZO4m1BI0kS8kWqkBNsKYyhVUfij0gvjy9xJNoG/VcGkxq5dRlVwzmrmY1BQMAmpbbUBLwC4Kw==} + react-i18next@15.5.2: + resolution: {integrity: sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A==} peerDependencies: i18next: '>= 23.2.3' react: '>= 16.8.0' @@ -2562,6 +3387,26 @@ packages: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.1: + resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react-router-dom@7.6.2: resolution: {integrity: sha512-Q8zb6VlTbdYKK5JJBLQEN06oTUa/RAbG/oQS1auK1I0TbJOXktqm+QENEVJU6QvWynlXPRBXI3fiOQcSEA78rA==} engines: {node: '>=20.0.0'} @@ -2579,6 +3424,16 @@ packages: react-dom: optional: true + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -2662,6 +3517,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} @@ -2686,6 +3546,16 @@ packages: sockette@2.0.6: resolution: {integrity: sha512-W6iG8RGV6Zife3Cj+FhuyHV447E6fqFM2hKmnaQrTvg3OydINV3Msj3WPFbX76blUlUxvQSMMMdrJxce8NqI5Q==} + sonner@2.0.6: + resolution: {integrity: sha512-yHFhk8T/DK3YxjFQXIrcHT1rGEeTLliVzWbO0xN8GberVun2RiBnxAjXAYpZrqwEVHBG9asI/Li8TAAhN9m59Q==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + source-map-generator@2.0.1: + resolution: {integrity: sha512-AtEu86XavXC2HD/bQVQoDbovnTRZE/0QMAAHn6RxALAoLOM3a47IG06TpsJK23BnCmjbTYNLwx2vUhgzRYgGMA==} + engines: {node: '>=20'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2747,6 +3617,16 @@ packages: systemjs@6.15.1: resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==} + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwindcss@4.1.11: + resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==} + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + tar@7.4.3: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} @@ -2760,6 +3640,9 @@ packages: resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} engines: {node: '>=0.12'} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -2781,6 +3664,9 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + tw-animate-css@1.3.5: + resolution: {integrity: sha512-t3u+0YNoloIhj1mMXs779P6MO9q3p3mvGn4k1n3nJPqJw/glZcuijG2qTSN4z4mgNRfW5ZC3aXJFLwDtiipZXA==} + type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} @@ -2792,6 +3678,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + undici@5.29.0: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} @@ -2839,6 +3728,26 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + use-sync-external-store@1.5.0: resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} peerDependencies: @@ -2860,19 +3769,19 @@ packages: peerDependencies: vite: '>=2.6.0' - vite@7.0.0: - resolution: {integrity: sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g==} - engines: {node: ^20.19.0 || >=22.12.0} + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 jiti: '>=1.21.0' - less: ^4.0.0 + less: '*' lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -2956,6 +3865,9 @@ packages: engines: {node: '>= 14'} hasBin: true + zod@3.25.71: + resolution: {integrity: sha512-BsBc/NPk7h8WsUWYWYL+BajcJPY8YhjelaWu2NMLuzgraKAz4Lb4/6K11g9jpuDetjMiqhZ6YaexFLOC0Ogi3Q==} + zustand@5.0.6: resolution: {integrity: sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==} engines: {node: '>=12.20.0'} @@ -3703,7 +4615,7 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0)': + '@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 '@emotion/babel-plugin': 11.13.5 @@ -3715,7 +4627,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 transitivePeerDependencies: - supports-color @@ -3729,18 +4641,18 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)': + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.3.1 - '@emotion/react': 11.14.0(@types/react@19.1.8)(react@19.1.0) + '@emotion/react': 11.14.0(@types/react@19.1.6)(react@19.1.0) '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.0) '@emotion/utils': 1.4.2 react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 transitivePeerDependencies: - supports-color @@ -3831,6 +4743,28 @@ snapshots: '@fastify/busboy@2.1.1': {} + '@floating-ui/core@1.7.2': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.2': + dependencies: + '@floating-ui/core': 1.7.2 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/dom': 1.7.2 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@floating-ui/utils@0.2.10': {} + + '@hookform/resolvers@5.1.1(react-hook-form@7.58.1(react@19.1.0))': + dependencies: + '@standard-schema/utils': 0.3.0 + react-hook-form: 7.58.1(react@19.1.0) + '@isaacs/balanced-match@4.0.1': {} '@isaacs/brace-expansion@5.0.0': @@ -3878,39 +4812,39 @@ snapshots: '@mui/core-downloads-tracker@7.1.2': {} - '@mui/icons-material@7.1.2(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)': + '@mui/icons-material@7.1.2(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.6)(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 - '@mui/material': 7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mui/material': 7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 - '@mui/lab@7.0.0-beta.14(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@mui/lab@7.0.0-beta.13(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 - '@mui/material': 7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) - '@mui/types': 7.4.3(@types/react@19.1.8) - '@mui/utils': 7.1.1(@types/react@19.1.8)(react@19.1.0) + '@mui/material': 7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) + '@mui/types': 7.4.3(@types/react@19.1.6) + '@mui/utils': 7.1.1(@types/react@19.1.6)(react@19.1.0) clsx: 2.1.1 prop-types: 15.8.1 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.8)(react@19.1.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) - '@types/react': 19.1.8 + '@emotion/react': 11.14.0(@types/react@19.1.6)(react@19.1.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) + '@types/react': 19.1.6 - '@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 '@mui/core-downloads-tracker': 7.1.2 - '@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) - '@mui/types': 7.4.3(@types/react@19.1.8) - '@mui/utils': 7.1.1(@types/react@19.1.8)(react@19.1.0) + '@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) + '@mui/types': 7.4.3(@types/react@19.1.6) + '@mui/utils': 7.1.1(@types/react@19.1.6)(react@19.1.0) '@popperjs/core': 2.11.8 - '@types/react-transition-group': 4.4.12(@types/react@19.1.8) + '@types/react-transition-group': 4.4.12(@types/react@19.1.6) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 @@ -3919,20 +4853,20 @@ snapshots: react-is: 19.1.0 react-transition-group: 4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.8)(react@19.1.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) - '@types/react': 19.1.8 + '@emotion/react': 11.14.0(@types/react@19.1.6)(react@19.1.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) + '@types/react': 19.1.6 - '@mui/private-theming@7.1.1(@types/react@19.1.8)(react@19.1.0)': + '@mui/private-theming@7.1.1(@types/react@19.1.6)(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 - '@mui/utils': 7.1.1(@types/react@19.1.8)(react@19.1.0) + '@mui/utils': 7.1.1(@types/react@19.1.6)(react@19.1.0) prop-types: 15.8.1 react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 - '@mui/styled-engine@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)': + '@mui/styled-engine@7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 '@emotion/cache': 11.14.0 @@ -3942,66 +4876,66 @@ snapshots: prop-types: 15.8.1 react: 19.1.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.8)(react@19.1.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) + '@emotion/react': 11.14.0(@types/react@19.1.6)(react@19.1.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) - '@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)': + '@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 - '@mui/private-theming': 7.1.1(@types/react@19.1.8)(react@19.1.0) - '@mui/styled-engine': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(react@19.1.0) - '@mui/types': 7.4.3(@types/react@19.1.8) - '@mui/utils': 7.1.1(@types/react@19.1.8)(react@19.1.0) + '@mui/private-theming': 7.1.1(@types/react@19.1.6)(react@19.1.0) + '@mui/styled-engine': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(react@19.1.0) + '@mui/types': 7.4.3(@types/react@19.1.6) + '@mui/utils': 7.1.1(@types/react@19.1.6)(react@19.1.0) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 19.1.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.8)(react@19.1.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) - '@types/react': 19.1.8 + '@emotion/react': 11.14.0(@types/react@19.1.6)(react@19.1.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) + '@types/react': 19.1.6 - '@mui/types@7.4.3(@types/react@19.1.8)': + '@mui/types@7.4.3(@types/react@19.1.6)': dependencies: '@babel/runtime': 7.27.6 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 - '@mui/utils@7.1.1(@types/react@19.1.8)(react@19.1.0)': + '@mui/utils@7.1.1(@types/react@19.1.6)(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 - '@mui/types': 7.4.3(@types/react@19.1.8) + '@mui/types': 7.4.3(@types/react@19.1.6) '@types/prop-types': 15.7.14 clsx: 2.1.1 prop-types: 15.8.1 react: 19.1.0 react-is: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 - '@mui/x-data-grid@8.6.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@mui/x-data-grid@8.6.0(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 - '@mui/material': 7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) - '@mui/utils': 7.1.1(@types/react@19.1.8)(react@19.1.0) - '@mui/x-internals': 8.6.0(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) + '@mui/material': 7.1.2(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) + '@mui/utils': 7.1.1(@types/react@19.1.6)(react@19.1.0) + '@mui/x-internals': 8.6.0(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) clsx: 2.1.1 prop-types: 15.8.1 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) use-sync-external-store: 1.5.0(react@19.1.0) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.8)(react@19.1.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) + '@emotion/react': 11.14.0(@types/react@19.1.6)(react@19.1.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) transitivePeerDependencies: - '@types/react' - '@mui/x-internals@8.6.0(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)': + '@mui/x-internals@8.6.0(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 - '@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) - '@mui/utils': 7.1.1(@types/react@19.1.8)(react@19.1.0) + '@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0))(@types/react@19.1.6)(react@19.1.0) + '@mui/utils': 7.1.1(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 reselect: 5.1.1 transitivePeerDependencies: @@ -4126,9 +5060,441 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.1 optional: true + '@peggyjs/from-mem@2.0.1': + dependencies: + semver: 7.7.2 + + '@pkgr/core@0.2.7': {} + '@popperjs/core@2.11.8': {} - '@rolldown/pluginutils@1.0.0-beta.19': {} + '@radix-ui/number@1.1.1': {} + + '@radix-ui/primitive@1.1.2': {} + + '@radix-ui/react-alert-dialog@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.6)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-context-menu@2.2.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-context@1.1.2(@types/react@19.1.6)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.6)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-direction@1.1.1(@types/react@19.1.6)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.6)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-icons@1.3.2(react@19.1.0)': + dependencies: + react: 19.1.0 + + '@radix-ui/react-id@1.1.1(@types/react@19.1.6)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-menu@2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.6)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-popover@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.6)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-popper@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react-dom': 2.1.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/rect': 1.1.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-select@2.2.5(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.6)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-slot@1.2.3(@types/react@19.1.6)(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-switch@1.2.5(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.6)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.6)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.6)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.6)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.6)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.6)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.6)(react@19.1.0)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.6)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.6 + + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) + + '@radix-ui/rect@1.1.1': {} + + '@rolldown/pluginutils@1.0.0-beta.9': {} '@rollup/pluginutils@5.1.4(rollup@4.40.2)': dependencies: @@ -4198,6 +5564,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true + '@standard-schema/utils@0.3.0': {} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.27.4)': dependencies: '@babel/core': 7.27.4 @@ -4268,54 +5636,135 @@ snapshots: transitivePeerDependencies: - supports-color + '@tailwindcss/node@4.1.11': + dependencies: + '@ampproject/remapping': 2.3.0 + enhanced-resolve: 5.18.2 + jiti: 2.4.2 + lightningcss: 1.30.1 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.11 + + '@tailwindcss/oxide-android-arm64@4.1.11': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.11': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.11': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.11': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.11': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.11': + optional: true + + '@tailwindcss/oxide@4.1.11': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.11 + '@tailwindcss/oxide-darwin-arm64': 4.1.11 + '@tailwindcss/oxide-darwin-x64': 4.1.11 + '@tailwindcss/oxide-freebsd-x64': 4.1.11 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.11 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.11 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.11 + '@tailwindcss/oxide-linux-x64-musl': 4.1.11 + '@tailwindcss/oxide-wasm32-wasi': 4.1.11 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.11 + + '@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1))': + dependencies: + '@tailwindcss/node': 4.1.11 + '@tailwindcss/oxide': 4.1.11 + tailwindcss: 4.1.11 + vite: 6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) + + '@tanstack/react-table@8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@tanstack/table-core': 8.21.3 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@tanstack/table-core@8.21.3': {} + + '@tauri-apps/api@2.5.0': {} + '@tauri-apps/api@2.6.0': {} - '@tauri-apps/cli-darwin-arm64@2.6.1': + '@tauri-apps/cli-darwin-arm64@2.5.0': optional: true - '@tauri-apps/cli-darwin-x64@2.6.1': + '@tauri-apps/cli-darwin-x64@2.5.0': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@2.6.1': + '@tauri-apps/cli-linux-arm-gnueabihf@2.5.0': optional: true - '@tauri-apps/cli-linux-arm64-gnu@2.6.1': + '@tauri-apps/cli-linux-arm64-gnu@2.5.0': optional: true - '@tauri-apps/cli-linux-arm64-musl@2.6.1': + '@tauri-apps/cli-linux-arm64-musl@2.5.0': optional: true - '@tauri-apps/cli-linux-riscv64-gnu@2.6.1': + '@tauri-apps/cli-linux-riscv64-gnu@2.5.0': optional: true - '@tauri-apps/cli-linux-x64-gnu@2.6.1': + '@tauri-apps/cli-linux-x64-gnu@2.5.0': optional: true - '@tauri-apps/cli-linux-x64-musl@2.6.1': + '@tauri-apps/cli-linux-x64-musl@2.5.0': optional: true - '@tauri-apps/cli-win32-arm64-msvc@2.6.1': + '@tauri-apps/cli-win32-arm64-msvc@2.5.0': optional: true - '@tauri-apps/cli-win32-ia32-msvc@2.6.1': + '@tauri-apps/cli-win32-ia32-msvc@2.5.0': optional: true - '@tauri-apps/cli-win32-x64-msvc@2.6.1': + '@tauri-apps/cli-win32-x64-msvc@2.5.0': optional: true - '@tauri-apps/cli@2.6.1': + '@tauri-apps/cli@2.5.0': optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.6.1 - '@tauri-apps/cli-darwin-x64': 2.6.1 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.6.1 - '@tauri-apps/cli-linux-arm64-gnu': 2.6.1 - '@tauri-apps/cli-linux-arm64-musl': 2.6.1 - '@tauri-apps/cli-linux-riscv64-gnu': 2.6.1 - '@tauri-apps/cli-linux-x64-gnu': 2.6.1 - '@tauri-apps/cli-linux-x64-musl': 2.6.1 - '@tauri-apps/cli-win32-arm64-msvc': 2.6.1 - '@tauri-apps/cli-win32-ia32-msvc': 2.6.1 - '@tauri-apps/cli-win32-x64-msvc': 2.6.1 + '@tauri-apps/cli-darwin-arm64': 2.5.0 + '@tauri-apps/cli-darwin-x64': 2.5.0 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.5.0 + '@tauri-apps/cli-linux-arm64-gnu': 2.5.0 + '@tauri-apps/cli-linux-arm64-musl': 2.5.0 + '@tauri-apps/cli-linux-riscv64-gnu': 2.5.0 + '@tauri-apps/cli-linux-x64-gnu': 2.5.0 + '@tauri-apps/cli-linux-x64-musl': 2.5.0 + '@tauri-apps/cli-win32-arm64-msvc': 2.5.0 + '@tauri-apps/cli-win32-ia32-msvc': 2.5.0 + '@tauri-apps/cli-win32-x64-msvc': 2.5.0 '@tauri-apps/plugin-clipboard-manager@2.3.0': dependencies: @@ -4341,13 +5790,13 @@ snapshots: dependencies: '@tauri-apps/api': 2.6.0 - '@tauri-apps/plugin-shell@2.3.0': + '@tauri-apps/plugin-shell@2.2.1': dependencies: - '@tauri-apps/api': 2.6.0 + '@tauri-apps/api': 2.5.0 - '@tauri-apps/plugin-updater@2.9.0': + '@tauri-apps/plugin-updater@2.7.1': dependencies: - '@tauri-apps/api': 2.6.0 + '@tauri-apps/api': 2.5.0 '@tauri-apps/plugin-window-state@2.3.0': dependencies: @@ -4374,6 +5823,12 @@ snapshots: dependencies: '@babel/types': 7.27.6 + '@types/d3-path@3.1.1': {} + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -4388,6 +5843,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/js-cookie@3.0.6': {} + '@types/js-yaml@4.0.9': {} '@types/json-schema@7.0.15': {} @@ -4404,19 +5861,23 @@ snapshots: '@types/ms@2.1.0': {} + '@types/node@24.0.10': + dependencies: + undici-types: 7.8.0 + '@types/parse-json@4.0.2': {} '@types/prop-types@15.7.14': {} - '@types/react-dom@19.1.6(@types/react@19.1.8)': + '@types/react-dom@19.1.6(@types/react@19.1.6)': dependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 - '@types/react-transition-group@4.4.12(@types/react@19.1.8)': + '@types/react-transition-group@4.4.12(@types/react@19.1.6)': dependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 - '@types/react@19.1.8': + '@types/react@19.1.6': dependencies: csstype: 3.1.3 @@ -4426,7 +5887,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-legacy@7.0.0(terser@5.43.1)(vite@7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1))': + '@vitejs/plugin-legacy@6.1.1(terser@5.43.1)(vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1))': dependencies: '@babel/core': 7.27.4 '@babel/preset-env': 7.27.2(@babel/core@7.27.4) @@ -4437,19 +5898,19 @@ snapshots: regenerator-runtime: 0.14.1 systemjs: 6.15.1 terser: 5.43.1 - vite: 7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) + vite: 6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@4.6.0(vite@7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1))': + '@vitejs/plugin-react@4.5.1(vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1))': dependencies: '@babel/core': 7.27.4 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.4) '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.4) - '@rolldown/pluginutils': 1.0.0-beta.19 + '@rolldown/pluginutils': 1.0.0-beta.9 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) + vite: 6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) transitivePeerDependencies: - supports-color @@ -4484,8 +5945,22 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + asynckit@0.4.0: {} + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.25.0 + caniuse-lite: 1.0.30001718 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + axios@1.10.0: dependencies: follow-redirects: 1.15.9 @@ -4578,6 +6053,10 @@ snapshots: chownr@3.0.0: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + cli-color@2.0.4: dependencies: d: 1.0.2 @@ -4590,6 +6069,18 @@ snapshots: clsx@2.1.1: {} + cmdk@1.1.1(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -4647,6 +6138,12 @@ snapshots: csstype@3.1.3: {} + d3-path@3.1.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + d@1.0.2: dependencies: es5-ext: 0.10.64 @@ -4673,6 +6170,10 @@ snapshots: detect-libc@1.0.3: optional: true + detect-libc@2.0.4: {} + + detect-node-es@1.1.0: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -4701,6 +6202,11 @@ snapshots: emoji-regex@9.2.2: {} + enhanced-resolve@5.18.2: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + entities@4.5.0: {} error-ex@1.3.2: @@ -4844,6 +6350,8 @@ snapshots: optionalDependencies: react: 19.1.0 + fraction.js@4.3.7: {} + fsevents@2.3.3: optional: true @@ -4864,6 +6372,8 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -4882,6 +6392,8 @@ snapshots: gopd@1.2.0: {} + graceful-fs@4.2.11: {} + has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -4933,12 +6445,16 @@ snapshots: transitivePeerDependencies: - supports-color + husky@9.1.7: {} + i18next@25.2.1(typescript@5.8.3): dependencies: '@babel/runtime': 7.27.6 optionalDependencies: typescript: 5.8.3 + ignore@7.0.5: {} + immutable@5.1.2: {} import-fresh@3.3.1: @@ -4990,6 +6506,10 @@ snapshots: dependencies: '@isaacs/cliui': 8.0.2 + jiti@2.4.2: {} + + js-base64@3.7.7: {} + js-cookie@3.0.5: {} js-tokens@4.0.0: {} @@ -5004,12 +6524,55 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-schema@0.4.0: {} - json5@2.2.3: {} jsonc-parser@3.3.1: {} + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + lines-and-columns@1.2.4: {} lodash-es@4.17.21: {} @@ -5038,6 +6601,10 @@ snapshots: dependencies: es5-ext: 0.10.64 + lucide-react@0.514.0(react@19.1.0): + dependencies: + react: 19.1.0 + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -5338,12 +6905,19 @@ snapshots: vscode-uri: 3.1.0 yaml: 2.7.1 + mri@1.2.0: {} + ms@2.1.3: {} nanoid@3.3.11: {} nanoid@5.1.5: {} + next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + next-tick@1.1.0: {} no-case@3.0.4: @@ -5364,6 +6938,8 @@ snapshots: node-releases@2.0.19: {} + normalize-range@0.1.2: {} + object-assign@4.1.1: {} once@1.4.0: @@ -5406,6 +6982,12 @@ snapshots: path-type@4.0.0: {} + peggy@5.0.4: + dependencies: + '@peggyjs/from-mem': 2.0.1 + commander: 14.0.0 + source-map-generator: 2.0.1 + picocolors@1.1.1: {} picomatch@2.3.1: @@ -5413,19 +6995,27 @@ snapshots: picomatch@4.0.2: {} + postcss-value-parser@4.2.0: {} + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 - prettier-plugin-organize-imports@4.1.0(prettier@3.6.2)(typescript@5.8.3): - dependencies: - prettier: 3.6.2 - typescript: 5.8.3 - prettier@3.6.2: {} + pretty-quick@4.2.2(prettier@3.6.2): + dependencies: + '@pkgr/core': 0.2.7 + ignore: 7.0.5 + mri: 1.2.0 + picocolors: 1.1.1 + picomatch: 4.0.2 + prettier: 3.6.2 + tinyexec: 0.3.2 + tslib: 2.8.1 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -5457,7 +7047,7 @@ snapshots: dependencies: react: 19.1.0 - react-i18next@15.5.3(i18next@25.2.1(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3): + react-i18next@15.5.2(i18next@25.2.1(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3): dependencies: '@babel/runtime': 7.27.6 html-parse-stringify: 3.0.1 @@ -5471,11 +7061,11 @@ snapshots: react-is@19.1.0: {} - react-markdown@10.1.0(@types/react@19.1.8)(react@19.1.0): + react-markdown@10.1.0(@types/react@19.1.6)(react@19.1.0): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 19.1.8 + '@types/react': 19.1.6 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 @@ -5497,6 +7087,25 @@ snapshots: react-refresh@0.17.0: {} + react-remove-scroll-bar@2.3.8(@types/react@19.1.6)(react@19.1.0): + dependencies: + react: 19.1.0 + react-style-singleton: 2.2.3(@types/react@19.1.6)(react@19.1.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.6 + + react-remove-scroll@2.7.1(@types/react@19.1.6)(react@19.1.0): + dependencies: + react: 19.1.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.1.6)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.1.6)(react@19.1.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.1.6)(react@19.1.0) + use-sidecar: 1.1.3(@types/react@19.1.6)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.6 + react-router-dom@7.6.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 @@ -5511,6 +7120,14 @@ snapshots: optionalDependencies: react-dom: 19.1.0(react@19.1.0) + react-style-singleton@2.2.3(@types/react@19.1.6)(react@19.1.0): + dependencies: + get-nonce: 1.0.1 + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.6 + react-transition-group@4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.27.6 @@ -5621,6 +7238,8 @@ snapshots: semver@6.3.1: {} + semver@7.7.2: {} + server-only@0.0.1: {} set-cookie-parser@2.7.1: {} @@ -5640,6 +7259,13 @@ snapshots: sockette@2.0.6: {} + sonner@2.0.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + source-map-generator@2.0.1: {} + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -5700,6 +7326,12 @@ snapshots: systemjs@6.15.1: {} + tailwind-merge@3.3.1: {} + + tailwindcss@4.1.11: {} + + tapable@2.2.2: {} + tar@7.4.3: dependencies: '@isaacs/fs-minipass': 4.0.1 @@ -5721,6 +7353,8 @@ snapshots: es5-ext: 0.10.64 next-tick: 1.1.0 + tinyexec@0.3.2: {} + tinyglobby@0.2.14: dependencies: fdir: 6.4.6(picomatch@4.0.2) @@ -5739,12 +7373,16 @@ snapshots: tunnel@0.0.6: {} + tw-animate-css@1.3.5: {} + type@2.7.3: {} types-pac@1.0.3: {} typescript@5.8.3: {} + undici-types@7.8.0: {} + undici@5.29.0: dependencies: '@fastify/busboy': 2.1.1 @@ -5801,6 +7439,21 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + use-callback-ref@1.3.3(@types/react@19.1.6)(react@19.1.0): + dependencies: + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.6 + + use-sidecar@1.1.3(@types/react@19.1.6)(react@19.1.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.6 + use-sync-external-store@1.5.0(react@19.1.0): dependencies: react: 19.1.0 @@ -5819,18 +7472,18 @@ snapshots: dependencies: monaco-editor: 0.52.2 - vite-plugin-svgr@4.3.0(rollup@4.40.2)(typescript@5.8.3)(vite@7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)): + vite-plugin-svgr@4.3.0(rollup@4.40.2)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1)): dependencies: '@rollup/pluginutils': 5.1.4(rollup@4.40.2) '@svgr/core': 8.1.0(typescript@5.8.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.8.3)) - vite: 7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) + vite: 6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1) transitivePeerDependencies: - rollup - supports-color - typescript - vite@7.0.0(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1): + vite@6.3.5(@types/node@24.0.10)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)(yaml@2.7.1): dependencies: esbuild: 0.25.4 fdir: 6.4.6(picomatch@4.0.2) @@ -5839,7 +7492,10 @@ snapshots: rollup: 4.40.2 tinyglobby: 0.2.14 optionalDependencies: + '@types/node': 24.0.10 fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.30.1 sass: 1.89.2 terser: 5.43.1 yaml: 2.7.1 @@ -5887,9 +7543,11 @@ snapshots: yaml@2.7.1: {} - zustand@5.0.6(@types/react@19.1.8)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)): + zod@3.25.71: {} + + zustand@5.0.6(@types/react@19.1.6)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)): optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.6 react: 19.1.0 use-sync-external-store: 1.5.0(react@19.1.0) diff --git a/src/App.tsx b/src/App.tsx index 3ca17dc0..2ef77bb4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,7 @@ import { AppDataProvider } from "./providers/app-data-provider"; import Layout from "./pages/_layout"; -import { useNotificationPermission } from "./hooks/useNotificationPermission"; function App() { - useNotificationPermission(); return ( diff --git a/src/components/base/NoticeManager.tsx b/src/components/base/NoticeManager.tsx index 223447a5..88ec7281 100644 --- a/src/components/base/NoticeManager.tsx +++ b/src/components/base/NoticeManager.tsx @@ -1,68 +1,30 @@ -import React, { useSyncExternalStore } from "react"; -import { Snackbar, Alert, IconButton, Box } from "@mui/material"; -import { CloseRounded } from "@mui/icons-material"; +"use client"; + +import { Toaster, toast } from "sonner"; +import { useEffect, useSyncExternalStore } from "react"; import { - subscribeNotices, - hideNotice, getSnapshotNotices, + hideNotice, + subscribeNotices, } from "@/services/noticeService"; -export const NoticeManager: React.FC = () => { +export const NoticeManager = () => { const currentNotices = useSyncExternalStore( subscribeNotices, getSnapshotNotices, ); - const handleClose = (id: number) => { - hideNotice(id); - }; + useEffect(() => { + for (const notice of currentNotices) { + const toastId = toast(notice.message, { + id: notice.id, + duration: notice.duration, + onDismiss: (t) => { + hideNotice(t.id as number); + }, + }); + } + }, [currentNotices]); - return ( - - {currentNotices.map((notice) => ( - - handleClose(notice.id)} - > - - - } - > - {notice.message} - - - ))} - - ); + return ; }; diff --git a/src/components/base/base-dialog.tsx b/src/components/base/base-dialog.tsx index 56d90eaa..382287a4 100644 --- a/src/components/base/base-dialog.tsx +++ b/src/components/base/base-dialog.tsx @@ -1,15 +1,18 @@ import { ReactNode } from "react"; -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - type SxProps, - type Theme, -} from "@mui/material"; -import { LoadingButton } from "@mui/lab"; +import { useTranslation } from "react-i18next"; +// --- Новые импорты --- +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Loader2 } from "lucide-react"; // Иконка для спиннера + +// --- Интерфейсы --- interface Props { title: ReactNode; open: boolean; @@ -18,12 +21,12 @@ interface Props { disableOk?: boolean; disableCancel?: boolean; disableFooter?: boolean; - contentSx?: SxProps; + className?: string; // Замена для contentSx, чтобы передавать классы Tailwind children?: ReactNode; loading?: boolean; onOk?: () => void; onCancel?: () => void; - onClose?: () => void; + onClose?: () => void; // onOpenChange в shadcn/ui делает то же самое } export interface DialogRef { @@ -38,37 +41,44 @@ export const BaseDialog: React.FC = (props) => { children, okBtn, cancelBtn, - contentSx, + className, disableCancel, disableOk, disableFooter, loading, + onClose, + onCancel, + onOk, } = props; + const { t } = useTranslation(); + return ( - - {title} + // Управляем состоянием через onOpenChange, которое вызывает onClose + !isOpen && onClose?.()}> + + + {title} + - {children} + {children} - {!disableFooter && ( - - {!disableCancel && ( - - )} - {!disableOk && ( - - {okBtn} - - )} - - )} + {!disableFooter && ( + + {!disableCancel && ( + + )} + {!disableOk && ( + + )} + + )} + ); }; diff --git a/src/components/base/base-empty.tsx b/src/components/base/base-empty.tsx index 665c12c5..77742f13 100644 --- a/src/components/base/base-empty.tsx +++ b/src/components/base/base-empty.tsx @@ -1,10 +1,10 @@ -import { alpha, Box, Typography } from "@mui/material"; -import { InboxRounded } from "@mui/icons-material"; +import { ReactNode } from "react"; import { useTranslation } from "react-i18next"; +import { Inbox } from "lucide-react"; // 1. Импортируем иконку из lucide-react interface Props { - text?: React.ReactNode; - extra?: React.ReactNode; + text?: ReactNode; + extra?: ReactNode; } export const BaseEmpty = (props: Props) => { @@ -12,20 +12,15 @@ export const BaseEmpty = (props: Props) => { const { t } = useTranslation(); return ( - ({ - width: "100%", - height: "100%", - display: "flex", - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - color: alpha(palette.text.secondary, 0.75), - })} - > - - {t(`${text}`)} + // 2. Заменяем Box на div и переводим sx в классы Tailwind +
+ {/* 3. Заменяем иконку MUI на lucide-react и задаем размер классами */} + + + {/* 4. Заменяем Typography на p */} +

{t(`${text}`)}

+ {extra} - +
); }; diff --git a/src/components/base/base-error-boundary.tsx b/src/components/base/base-error-boundary.tsx index 2475a2ff..9a4e2ef3 100644 --- a/src/components/base/base-error-boundary.tsx +++ b/src/components/base/base-error-boundary.tsx @@ -1,16 +1,30 @@ import { ReactNode } from "react"; import { ErrorBoundary, FallbackProps } from "react-error-boundary"; +import { useTranslation } from "react-i18next"; +import { AlertTriangle } from "lucide-react"; // Импортируем иконку +// Новый, стилизованный компонент для отображения ошибки function ErrorFallback({ error }: FallbackProps) { + const { t } = useTranslation(); + return ( -
-

Something went wrong:(

+
+
+ +

{t("Something went wrong")}

+
-
{error.message}
+
+        {error.message}
+      
-
- Error Stack -
{error.stack}
+
+ + {t("Error Stack")} + +
+          {error.stack}
+        
); diff --git a/src/components/base/base-fieldset.tsx b/src/components/base/base-fieldset.tsx index 131f6758..9e88b810 100644 --- a/src/components/base/base-fieldset.tsx +++ b/src/components/base/base-fieldset.tsx @@ -1,38 +1,30 @@ import React from "react"; -import { Box, styled } from "@mui/material"; +import { cn } from "@root/lib/utils"; // Импортируем утилиту для объединения классов type Props = { label: string; - fontSize?: string; - width?: string; - padding?: string; children?: React.ReactNode; + className?: string; // Пропс для дополнительной стилизации }; -export const BaseFieldset: React.FC = (props: Props) => { - const Fieldset = styled(Box)<{ component?: string }>(() => ({ - position: "relative", - border: "1px solid #bbb", - borderRadius: "5px", - width: props.width ?? "auto", - padding: props.padding ?? "15px", - })); - - const Label = styled("legend")(({ theme }) => ({ - position: "absolute", - top: "-10px", - left: props.padding ?? "15px", - backgroundColor: theme.palette.background.paper, - backgroundImage: - "linear-gradient(rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.16))", - color: theme.palette.text.primary, - fontSize: props.fontSize ?? "1em", - })); +export const BaseFieldset: React.FC = (props) => { + const { label, children, className } = props; return ( -
- - {props.children} -
+ // 1. Используем тег fieldset для семантики. Он позиционирован как relative. +
+ {/* 2. Используем legend. Он абсолютно спозиционирован относительно fieldset. */} + + {label} + + + {/* 3. Здесь будет содержимое филдсета */} + {children} +
); }; diff --git a/src/components/base/base-loading-overlay.tsx b/src/components/base/base-loading-overlay.tsx index 036250b9..05f82ca2 100644 --- a/src/components/base/base-loading-overlay.tsx +++ b/src/components/base/base-loading-overlay.tsx @@ -1,32 +1,29 @@ import React from "react"; -import { Box, CircularProgress } from "@mui/material"; +import { BaseLoading } from "./base-loading"; // 1. Импортируем наш собственный компонент загрузки +import { cn } from "@root/lib/utils"; export interface BaseLoadingOverlayProps { isLoading: boolean; + className?: string; } export const BaseLoadingOverlay: React.FC = ({ isLoading, + className, }) => { if (!isLoading) return null; return ( - - - + {/* 3. Используем наш BaseLoading и делаем его немного больше */} + +
); }; diff --git a/src/components/base/base-loading.tsx b/src/components/base/base-loading.tsx index 0fdbebf1..b5ae78e3 100644 --- a/src/components/base/base-loading.tsx +++ b/src/components/base/base-loading.tsx @@ -1,48 +1,14 @@ -import { styled } from "@mui/material"; +import { Loader2 } from "lucide-react"; // 1. Импортируем стандартную иконку загрузки +import { cn } from "@root/lib/utils"; // Утилита для объединения классов -const Loading = styled("div")` - position: relative; - display: flex; - height: 100%; - min-height: 18px; - box-sizing: border-box; - align-items: center; +interface Props { + className?: string; +} - & > div { - box-sizing: border-box; - width: 6px; - height: 6px; - margin: 2px; - border-radius: 100%; - animation: loading 0.7s -0.15s infinite linear; - } - - & > div:nth-child(2n-1) { - animation-delay: -0.5s; - } - - @keyframes loading { - 50% { - opacity: 0.2; - transform: scale(0.75); - } - 100% { - opacity: 1; - transform: scale(1); - } - } -`; - -const LoadingItem = styled("div")(({ theme }) => ({ - background: theme.palette.text.secondary, -})); - -export const BaseLoading = () => { +export const BaseLoading: React.FC = ({ className }) => { return ( - - - - - + // 2. Используем иконку с анимацией вращения от Tailwind + // Мы можем легко менять ее размер и цвет через className + ); }; diff --git a/src/components/base/base-page.tsx b/src/components/base/base-page.tsx index b1f92e2c..b24f2703 100644 --- a/src/components/base/base-page.tsx +++ b/src/components/base/base-page.tsx @@ -1,50 +1,40 @@ import React, { ReactNode } from "react"; -import { Typography } from "@mui/material"; import { BaseErrorBoundary } from "./base-error-boundary"; -import { useTheme } from "@mui/material/styles"; +import { cn } from "@root/lib/utils"; interface Props { - title?: React.ReactNode; // the page title - header?: React.ReactNode; // something behind title - contentStyle?: React.CSSProperties; - children?: ReactNode; - full?: boolean; + title?: ReactNode; // Заголовок страницы + header?: ReactNode; // Элементы в правой части шапки (кнопки и т.д.) + children?: ReactNode; // Основное содержимое страницы + className?: string; // Дополнительные классы для основной области контента } export const BasePage: React.FC = (props) => { - const { title, header, contentStyle, full, children } = props; - const theme = useTheme(); - - const isDark = theme.palette.mode === "dark"; + const { title, header, children, className } = props; return ( -
-
- - {title} - + {/* 1. Корневой контейнер: flex-колонка на всю высоту */} +
- {header} + {/* 2. Шапка: не растягивается, имеет фиксированную высоту и нижнюю границу */} +
+

+ {title} +

+
+ {header} +
-
-
-
- {children} -
-
-
+ {/* 3. Основная область: занимает все оставшееся место и прокручивается */} +
+ {children} +
+
); diff --git a/src/components/base/base-search-box.tsx b/src/components/base/base-search-box.tsx index a01789fd..2987eec7 100644 --- a/src/components/base/base-search-box.tsx +++ b/src/components/base/base-search-box.tsx @@ -1,11 +1,12 @@ -import { Box, SvgIcon, TextField, styled } from "@mui/material"; -import Tooltip from "@mui/material/Tooltip"; -import { ChangeEvent, useEffect, useRef, useState, useMemo } from "react"; - +import { ChangeEvent, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import matchCaseIcon from "@/assets/image/component/match_case.svg?react"; -import matchWholeWordIcon from "@/assets/image/component/match_whole_word.svg?react"; -import useRegularExpressionIcon from "@/assets/image/component/use_regular_expression.svg?react"; +import { cn } from "@root/lib/utils"; + +// Новые импорты +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; +import { CaseSensitive, WholeWord, Regex } from "lucide-react"; // Иконки из lucide-react export type SearchState = { text: string; @@ -16,150 +17,97 @@ export type SearchState = { type SearchProps = { placeholder?: string; - matchCase?: boolean; - matchWholeWord?: boolean; - useRegularExpression?: boolean; onSearch: (match: (content: string) => boolean, state: SearchState) => void; }; -const StyledTextField = styled(TextField)(({ theme }) => ({ - "& .MuiInputBase-root": { - background: theme.palette.mode === "light" ? "#fff" : undefined, - paddingRight: "4px", - }, - "& .MuiInputBase-root svg[aria-label='active'] path": { - fill: theme.palette.primary.light, - }, - "& .MuiInputBase-root svg[aria-label='inactive'] path": { - fill: "#A7A7A7", - }, -})); - export const BaseSearchBox = (props: SearchProps) => { const { t } = useTranslation(); - const inputRef = useRef(null); - const [matchCase, setMatchCase] = useState(props.matchCase ?? false); - const [matchWholeWord, setMatchWholeWord] = useState( - props.matchWholeWord ?? false, - ); - const [useRegularExpression, setUseRegularExpression] = useState( - props.useRegularExpression ?? false, - ); + const [text, setText] = useState(""); + const [matchCase, setMatchCase] = useState(false); + const [matchWholeWord, setMatchWholeWord] = useState(false); + const [useRegularExpression, setUseRegularExpression] = useState(false); const [errorMessage, setErrorMessage] = useState(""); - const iconStyle = { - style: { - height: "24px", - width: "24px", - cursor: "pointer", - } as React.CSSProperties, - inheritViewBox: true, - }; - const createMatcher = useMemo(() => { return (searchText: string) => { try { + setErrorMessage(""); // Сбрасываем ошибку при новой попытке return (content: string) => { if (!searchText) return true; - - let item = !matchCase ? content.toLowerCase() : content; - let searchItem = !matchCase ? searchText.toLowerCase() : searchText; + const flags = matchCase ? "" : "i"; if (useRegularExpression) { - return new RegExp(searchItem).test(item); + return new RegExp(searchText, flags).test(content); } + let pattern = searchText.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); // Экранируем спецсимволы if (matchWholeWord) { - return new RegExp(`\\b${searchItem}\\b`).test(item); + pattern = `\\b${pattern}\\b`; } - return item.includes(searchItem); + return new RegExp(pattern, flags).test(content); }; - } catch (err) { - setErrorMessage(`${err}`); - return () => true; + } catch (err: any) { + setErrorMessage(err.message); + return () => true; // Возвращаем "безопасный" матчер в случае ошибки } }; }, [matchCase, matchWholeWord, useRegularExpression]); useEffect(() => { - if (!inputRef.current) return; - const value = inputRef.current.value; - setErrorMessage(""); - props.onSearch(createMatcher(value), { - text: value, - matchCase, - matchWholeWord, - useRegularExpression, - }); - }, [matchCase, matchWholeWord, useRegularExpression, createMatcher]); + props.onSearch(createMatcher(text), { text, matchCase, matchWholeWord, useRegularExpression }); + }, [matchCase, matchWholeWord, useRegularExpression, createMatcher]); // Убрали text из зависимостей - const onChange = (e: ChangeEvent) => { - const value = e.target?.value ?? ""; - setErrorMessage(""); - props.onSearch(createMatcher(value), { - text: value, - matchCase, - matchWholeWord, - useRegularExpression, - }); + const handleChange = (e: ChangeEvent) => { + const value = e.target.value; + setText(value); + props.onSearch(createMatcher(value), { text: value, matchCase, matchWholeWord, useRegularExpression }); }; + const getToggleVariant = (isActive: boolean) => (isActive ? "secondary" : "ghost"); + return ( - - - -
- setMatchCase(!matchCase)} - /> -
-
- -
- setMatchWholeWord(!matchWholeWord)} - /> -
-
- -
- - setUseRegularExpression(!useRegularExpression) - } - />{" "} -
-
- - ), - }, - }} - /> -
+
+
+ {/* Добавляем правый отступ, чтобы текст не заезжал под иконки */} + + {/* Контейнер для иконок, абсолютно спозиционированный справа */} +
+ + + + + +

{t("Match Case")}

+
+ + + + +

{t("Match Whole Word")}

+
+ + + + +

{t("Use Regular Expression")}

+
+
+
+
+ {/* Отображение ошибки под полем ввода */} + {errorMessage &&

{errorMessage}

} +
); }; diff --git a/src/components/base/base-styled-select.tsx b/src/components/base/base-styled-select.tsx index d34ff1a4..0b186b9a 100644 --- a/src/components/base/base-styled-select.tsx +++ b/src/components/base/base-styled-select.tsx @@ -1,19 +1,37 @@ -import { Select, SelectProps, styled } from "@mui/material"; +import * as React from "react"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { cn } from "@root/lib/utils"; + +// Определяем новые пропсы для нашего компонента +export interface BaseStyledSelectProps { + children: React.ReactNode; // Сюда будут передаваться + value?: string; + onValueChange?: (value: string) => void; + placeholder?: string; + className?: string; // для дополнительной стилизации +} + +export const BaseStyledSelect: React.FC = (props) => { + const { value, onValueChange, placeholder, children, className } = props; -export const BaseStyledSelect = styled((props: SelectProps) => { return ( - + + + + {children} + ); -})(({ theme }) => ({ - background: theme.palette.mode === "light" ? "#fff" : undefined, -})); +}; diff --git a/src/components/base/base-styled-text-field.tsx b/src/components/base/base-styled-text-field.tsx index 0899da8e..3af4538e 100644 --- a/src/components/base/base-styled-text-field.tsx +++ b/src/components/base/base-styled-text-field.tsx @@ -1,24 +1,32 @@ -import { TextField, type TextFieldProps, styled } from "@mui/material"; +import * as React from "react"; // 1. Убедимся, что React импортирован import { useTranslation } from "react-i18next"; +import { cn } from "@root/lib/utils"; +import { Input } from "@/components/ui/input"; // 2. Убираем импорт несуществующего типа InputProps -export const BaseStyledTextField = styled((props: TextFieldProps) => { +// 3. Определяем наши пропсы, расширяя стандартный тип для input-элементов из React +export interface BaseStyledTextFieldProps + extends React.InputHTMLAttributes {} + +export const BaseStyledTextField = React.forwardRef< + HTMLInputElement, + BaseStyledTextFieldProps // Используем наш правильный тип +>((props, ref) => { const { t } = useTranslation(); + const { className, ...restProps } = props; return ( - ); -})(({ theme }) => ({ - "& .MuiInputBase-root": { - background: theme.palette.mode === "light" ? "#fff" : undefined, - }, -})); +}); + +BaseStyledTextField.displayName = "BaseStyledTextField"; diff --git a/src/components/base/base-switch.tsx b/src/components/base/base-switch.tsx index 302c06bd..58146d40 100644 --- a/src/components/base/base-switch.tsx +++ b/src/components/base/base-switch.tsx @@ -1,58 +1,23 @@ -import { styled } from "@mui/material/styles"; -import { default as MuiSwitch, SwitchProps } from "@mui/material/Switch"; +import * as React from "react"; +import { Switch as ShadcnSwitch } from "@/components/ui/switch"; +import { cn } from "@root/lib/utils"; -export const Switch = styled((props: SwitchProps) => ( - -))(({ theme }) => ({ - width: 42, - height: 26, - padding: 0, - marginRight: 1, - "& .MuiSwitch-switchBase": { - padding: 0, - margin: 2, - transitionDuration: "300ms", - "&.Mui-checked": { - transform: "translateX(16px)", - color: "#fff", - "& + .MuiSwitch-track": { - backgroundColor: theme.palette.primary.main, - opacity: 1, - border: 0, - }, - "&.Mui-disabled + .MuiSwitch-track": { - opacity: 0.5, - }, - }, - "&.Mui-focusVisible .MuiSwitch-thumb": { - color: "#33cf4d", - border: "6px solid #fff", - }, - "&.Mui-disabled .MuiSwitch-thumb": { - color: - theme.palette.mode === "light" - ? theme.palette.grey[100] - : theme.palette.grey[600], - }, - "&.Mui-disabled + .MuiSwitch-track": { - opacity: theme.palette.mode === "light" ? 0.7 : 0.3, - }, - }, - "& .MuiSwitch-thumb": { - boxSizing: "border-box", - width: 22, - height: 22, - }, - "& .MuiSwitch-track": { - borderRadius: 26 / 2, - backgroundColor: theme.palette.mode === "light" ? "#BBBBBB" : "#39393D", - opacity: 1, - transition: theme.transitions.create(["background-color"], { - duration: 500, - }), - }, -})); +// Тип пропсов остается без изменений +export type SwitchProps = React.ComponentPropsWithoutRef; + +const Switch = React.forwardRef< + HTMLButtonElement, + SwitchProps +>(({ className, ...props }, ref) => { + return ( + + ); +}); + +Switch.displayName = "Switch"; + +export { Switch }; diff --git a/src/components/base/base-tooltip-icon.tsx b/src/components/base/base-tooltip-icon.tsx index e7028955..9cfa3ab0 100644 --- a/src/components/base/base-tooltip-icon.tsx +++ b/src/components/base/base-tooltip-icon.tsx @@ -1,24 +1,52 @@ +import * as React from "react"; +import { cn } from "@root/lib/utils"; + +// 1. Убираем импорт несуществующего типа ButtonProps +import { Button } from "@/components/ui/button"; import { Tooltip, - IconButton, - IconButtonProps, - SvgIconProps, -} from "@mui/material"; -import { InfoRounded } from "@mui/icons-material"; + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Info } from "lucide-react"; -interface Props extends IconButtonProps { - title?: string; - icon?: React.ElementType; +// 2. Определяем наши пропсы, расширяя стандартный тип для кнопок из React +export interface TooltipIconProps + extends React.ButtonHTMLAttributes { + tooltip: React.ReactNode; + icon?: React.ReactNode; } -export const TooltipIcon: React.FC = (props: Props) => { - const { title = "", icon: Icon = InfoRounded, ...restProps } = props; +export const TooltipIcon = React.forwardRef< + HTMLButtonElement, + TooltipIconProps +>(({ tooltip, icon, className, ...props }, ref) => { + const displayIcon = icon || ; return ( - - - - - + + + + + + + {typeof tooltip === "string" ?

{tooltip}

: tooltip} +
+
+
); -}; +}); + +TooltipIcon.displayName = "TooltipIcon"; diff --git a/src/components/connection/connection-detail.tsx b/src/components/connection/connection-detail.tsx index 1d16924c..a0d5f8ed 100644 --- a/src/components/connection/connection-detail.tsx +++ b/src/components/connection/connection-detail.tsx @@ -1,10 +1,16 @@ import dayjs from "dayjs"; import { forwardRef, useImperativeHandle, useState } from "react"; import { useLockFn } from "ahooks"; -import { Box, Button, Snackbar, useTheme } from "@mui/material"; +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet"; import { deleteConnection } from "@/services/api"; import parseTraffic from "@/utils/parse-traffic"; import { t } from "i18next"; +import { Button } from "@/components/ui/button"; export interface ConnectionDetailRef { open: (detail: IConnectionsItem) => void; @@ -14,38 +20,37 @@ export const ConnectionDetail = forwardRef( (props, ref) => { const [open, setOpen] = useState(false); const [detail, setDetail] = useState(null!); - const theme = useTheme(); useImperativeHandle(ref, () => ({ open: (detail: IConnectionsItem) => { - if (open) return; - setOpen(true); setDetail(detail); + setOpen(true); }, })); - const onClose = () => setOpen(false); + const handleOpenChange = (isOpen: boolean) => { + setOpen(isOpen); + }; + + if (!detail) return null; return ( - - ) : null - } - /> + + + + {t("Connection Details")} + +
+ setOpen(false)} + /> +
+
+
); }, ); @@ -57,7 +62,6 @@ interface InnerProps { const InnerConnectionDetail = ({ data, onClose }: InnerProps) => { const { metadata, rulePayload } = data; - const theme = useTheme(); const chains = [...data.chains].reverse().join(" / "); const rule = rulePayload ? `${data.rule}(${rulePayload})` : data.rule; const host = metadata.host @@ -86,7 +90,9 @@ const InnerConnectionDetail = ({ data, onClose }: InnerProps) => { { label: t("Rule"), value: rule }, { label: t("Process"), - value: `${metadata.process}${metadata.processPath ? `(${metadata.processPath})` : ""}`, + value: `${metadata.process}${ + metadata.processPath ? `(${metadata.processPath})` : "" + }`, }, { label: t("Time"), value: dayjs(data.start).fromNow() }, { @@ -101,24 +107,16 @@ const InnerConnectionDetail = ({ data, onClose }: InnerProps) => { const onDelete = useLockFn(async () => deleteConnection(data.id)); return ( - +
{information.map((each) => ( -
- {each.label} - - : {each.value} - +
+ {each.label} + : {each.value}
))} - +
- - +
+
); }; diff --git a/src/components/connection/connection-item.tsx b/src/components/connection/connection-item.tsx index fa4a48a8..8c659291 100644 --- a/src/components/connection/connection-item.tsx +++ b/src/components/connection/connection-item.tsx @@ -1,27 +1,22 @@ import dayjs from "dayjs"; import { useLockFn } from "ahooks"; -import { - styled, - ListItem, - IconButton, - ListItemText, - Box, - alpha, -} from "@mui/material"; -import { CloseRounded } from "@mui/icons-material"; +import { X } from "lucide-react"; +import { Button } from "@/components/ui/button"; import { deleteConnection } from "@/services/api"; import parseTraffic from "@/utils/parse-traffic"; -const Tag = styled("span")(({ theme }) => ({ - fontSize: "10px", - padding: "0 4px", - lineHeight: 1.375, - border: "1px solid", - borderRadius: 4, - borderColor: alpha(theme.palette.text.secondary, 0.35), - marginTop: "4px", - marginRight: "4px", -})); +interface TagProps { + children: React.ReactNode; + className?: string; +} + +const Tag: React.FC = ({ children, className }) => { + const baseClasses = + "text-[10px] px-1 leading-[1.375] border rounded-[4px] border-muted-foreground/35"; + return ( + {children} + ); +}; interface Props { value: IConnectionsItem; @@ -37,43 +32,42 @@ export const ConnectionItem = (props: Props) => { const showTraffic = curUpload! >= 100 || curDownload! >= 100; return ( - - - - } - > - +
- - {metadata.network} + > +
+ {metadata.host || metadata.destinationIP} +
+
+ + {metadata.network} + + + {metadata.type} + + {!!metadata.process && {metadata.process}} + + {chains?.length > 0 && {[...chains].reverse().join(" / ")}} + + {dayjs(start).fromNow()} + + {showTraffic && ( + + {parseTraffic(curUpload!)} / {parseTraffic(curDownload!)} - - {metadata.type} - - {!!metadata.process && {metadata.process}} - - {chains?.length > 0 && ( - {[...chains].reverse().join(" / ")} - )} - - {dayjs(start).fromNow()} - - {showTraffic && ( - - {parseTraffic(curUpload!)} / {parseTraffic(curDownload!)} - - )} - - } - /> - + )} +
+
+ +
); }; diff --git a/src/components/connection/connection-table.tsx b/src/components/connection/connection-table.tsx index eb2cd35b..1996ee7a 100644 --- a/src/components/connection/connection-table.tsx +++ b/src/components/connection/connection-table.tsx @@ -1,139 +1,73 @@ import dayjs from "dayjs"; -import { useMemo, useState, useEffect } from "react"; -import { DataGrid, GridColDef, GridColumnResizeParams } from "@mui/x-data-grid"; -import { useThemeMode } from "@/services/states"; +import relativeTime from "dayjs/plugin/relativeTime"; +import React, { useMemo, useState, useEffect, RefObject } from "react"; +import { + ColumnDef, + flexRender, + getCoreRowModel, + useReactTable, + Row, + ColumnSizingState, +} from "@tanstack/react-table"; +import { TableVirtuoso, TableComponents } from "react-virtuoso"; + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + import { truncateStr } from "@/utils/truncate-str"; import parseTraffic from "@/utils/parse-traffic"; import { t } from "i18next"; +import { cn } from "@root/lib/utils"; +dayjs.extend(relativeTime); + +// Интерфейс для строки данных, которую использует react-table +interface ConnectionRow { + id: string; + host: string; + download: number; + upload: number; + dlSpeed: number; + ulSpeed: number; + chains: string; + rule: string; + process: string; + time: string; + source: string; + remoteDestination: string; + type: string; + connectionData: IConnectionsItem; +} + +// Интерфейс для пропсов, которые компонент получает от родителя interface Props { connections: IConnectionsItem[]; onShowDetail: (data: IConnectionsItem) => void; + scrollerRef: (element: HTMLElement | Window | null) => void; } + export const ConnectionTable = (props: Props) => { - const { connections, onShowDetail } = props; - const mode = useThemeMode(); - const isDark = mode === "light" ? false : true; - const backgroundColor = isDark ? "#282A36" : "#ffffff"; + const { connections, onShowDetail, scrollerRef } = props; - const [columnVisible, setColumnVisible] = useState< - Partial> - >({}); - - const [columnWidths, setColumnWidths] = useState>( - () => { + const [columnSizing, setColumnSizing] = useState(() => { + try { const saved = localStorage.getItem("connection-table-widths"); return saved ? JSON.parse(saved) : {}; - }, - ); - - const [columns] = useState([ - { - field: "host", - headerName: t("Host"), - width: columnWidths["host"] || 220, - minWidth: 180, - }, - { - field: "download", - headerName: t("Downloaded"), - width: columnWidths["download"] || 88, - align: "right", - headerAlign: "right", - valueFormatter: (value: number) => parseTraffic(value).join(" "), - }, - { - field: "upload", - headerName: t("Uploaded"), - width: columnWidths["upload"] || 88, - align: "right", - headerAlign: "right", - valueFormatter: (value: number) => parseTraffic(value).join(" "), - }, - { - field: "dlSpeed", - headerName: t("DL Speed"), - width: columnWidths["dlSpeed"] || 88, - align: "right", - headerAlign: "right", - valueFormatter: (value: number) => parseTraffic(value).join(" ") + "/s", - }, - { - field: "ulSpeed", - headerName: t("UL Speed"), - width: columnWidths["ulSpeed"] || 88, - align: "right", - headerAlign: "right", - valueFormatter: (value: number) => parseTraffic(value).join(" ") + "/s", - }, - { - field: "chains", - headerName: t("Chains"), - width: columnWidths["chains"] || 340, - minWidth: 180, - }, - { - field: "rule", - headerName: t("Rule"), - width: columnWidths["rule"] || 280, - minWidth: 180, - }, - { - field: "process", - headerName: t("Process"), - width: columnWidths["process"] || 220, - minWidth: 180, - }, - { - field: "time", - headerName: t("Time"), - width: columnWidths["time"] || 120, - minWidth: 100, - align: "right", - headerAlign: "right", - sortComparator: (v1: string, v2: string) => - new Date(v2).getTime() - new Date(v1).getTime(), - valueFormatter: (value: number) => dayjs(value).fromNow(), - }, - { - field: "source", - headerName: t("Source"), - width: columnWidths["source"] || 200, - minWidth: 130, - }, - { - field: "remoteDestination", - headerName: t("Destination"), - width: columnWidths["remoteDestination"] || 200, - minWidth: 130, - }, - { - field: "type", - headerName: t("Type"), - width: columnWidths["type"] || 160, - minWidth: 100, - }, - ]); + } catch { return {}; } + }); useEffect(() => { - console.log("Saving column widths:", columnWidths); - localStorage.setItem( - "connection-table-widths", - JSON.stringify(columnWidths), - ); - }, [columnWidths]); + localStorage.setItem("connection-table-widths", JSON.stringify(columnSizing)); + }, [columnSizing]); - const handleColumnResize = (params: GridColumnResizeParams) => { - const { colDef, width } = params; - console.log("Column resize:", colDef.field, width); - setColumnWidths((prev) => ({ - ...prev, - [colDef.field]: width, - })); - }; - - const connRows = useMemo(() => { + const connRows = useMemo((): ConnectionRow[] => { return connections.map((each) => { const { metadata, rulePayload } = each; const chains = [...each.chains].reverse().join(" / "); @@ -148,11 +82,11 @@ export const ConnectionTable = (props: Props) => { : `${metadata.remoteDestination}:${metadata.destinationPort}`, download: each.download, upload: each.upload, - dlSpeed: each.curDownload, - ulSpeed: each.curUpload, + dlSpeed: each.curDownload ?? 0, + ulSpeed: each.curUpload ?? 0, chains, rule, - process: truncateStr(metadata.process || metadata.processPath), + process: truncateStr(metadata.process || metadata.processPath) ?? '', time: each.start, source: `${metadata.sourceIP}:${metadata.sourcePort}`, remoteDestination: Destination, @@ -162,24 +96,97 @@ export const ConnectionTable = (props: Props) => { }); }, [connections]); + const columns = useMemo[]>(() => [ + { accessorKey: "host", header: () => t("Host"), size: columnSizing?.host || 220, minSize: 180 }, + { accessorKey: "download", header: () => t("Downloaded"), size: columnSizing?.download || 88, cell: ({ getValue }) =>
{parseTraffic(getValue()).join(" ")}
}, + { accessorKey: "upload", header: () => t("Uploaded"), size: columnSizing?.upload || 88, cell: ({ getValue }) =>
{parseTraffic(getValue()).join(" ")}
}, + { accessorKey: "dlSpeed", header: () => t("DL Speed"), size: columnSizing?.dlSpeed || 88, cell: ({ getValue }) =>
{parseTraffic(getValue()).join(" ")}/s
}, + { accessorKey: "ulSpeed", header: () => t("UL Speed"), size: columnSizing?.ulSpeed || 88, cell: ({ getValue }) =>
{parseTraffic(getValue()).join(" ")}/s
}, + { accessorKey: "chains", header: () => t("Chains"), size: columnSizing?.chains || 340, minSize: 180 }, + { accessorKey: "rule", header: () => t("Rule"), size: columnSizing?.rule || 280, minSize: 180 }, + { accessorKey: "process", header: () => t("Process"), size: columnSizing?.process || 220, minSize: 180 }, + { accessorKey: "time", header: () => t("Time"), size: columnSizing?.time || 120, minSize: 100, cell: ({ getValue }) =>
{dayjs(getValue()).fromNow()}
}, + { accessorKey: "source", header: () => t("Source"), size: columnSizing?.source || 200, minSize: 130 }, + { accessorKey: "remoteDestination", header: () => t("Destination"), size: columnSizing?.remoteDestination || 200, minSize: 130 }, + { accessorKey: "type", header: () => t("Type"), size: columnSizing?.type || 160, minSize: 100 }, + ], [columnSizing]); + + const table = useReactTable({ + data: connRows, + columns, + state: { columnSizing }, + onColumnSizingChange: setColumnSizing, + getCoreRowModel: getCoreRowModel(), + columnResizeMode: 'onChange', + }); + + const VirtuosoTableComponents = useMemo>>(() => ({ + // Явно типизируем `ref` для каждого компонента + Scroller: React.forwardRef((props, ref) => ( +
+ )), + Table: (props) => ( + + ), + TableHead: React.forwardRef((props, ref) => ( + + )), + // Явно типизируем пропсы и `ref` для TableRow + TableRow: React.forwardRef } & React.HTMLAttributes>( + ({ item: row, ...props }, ref) => { + // `Virtuoso` передает нам готовую строку `row` в пропсе `item`. + // Больше не нужно искать ее по индексу! + return ( + onShowDetail(row.original.connectionData)} + /> + ); + }), + TableBody: React.forwardRef((props, ref) => ) + }), []); + return ( - onShowDetail(e.row.connectionData)} - density="compact" - sx={{ - border: "none", - "div:focus": { outline: "none !important" }, - "& .MuiDataGrid-columnHeader": { - userSelect: "none", - }, - }} - columnVisibilityModel={columnVisible} - onColumnVisibilityModelChange={(e) => setColumnVisible(e)} - onColumnResize={handleColumnResize} - disableColumnMenu={false} - /> +
+ {connRows.length > 0 ? ( + ( + table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + + ))} + + )) + )} + itemContent={(index, row) => ( + <> + {row.getVisibleCells().map((cell) => ( + onShowDetail(row.original.connectionData)} + > + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + )} + /> + ) : ( +
+

No results.

+
+ )} +
); }; diff --git a/src/components/home/proxy-selectors.tsx b/src/components/home/proxy-selectors.tsx new file mode 100644 index 00000000..a394bb0e --- /dev/null +++ b/src/components/home/proxy-selectors.tsx @@ -0,0 +1,265 @@ +import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { cn } from "@root/lib/utils"; + +// Компоненты и иконки +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; +import { ChevronsUpDown, Timer, WholeWord } from 'lucide-react'; + +// Логика +import { useVerge } from '@/hooks/use-verge'; +import { useAppData } from '@/providers/app-data-provider'; +import delayManager from '@/services/delay'; +import { updateProxy, deleteConnection } from '@/services/api'; + +// --- Типы и константы --- +const STORAGE_KEY_GROUP = 'clash-verge-selected-proxy-group'; +const STORAGE_KEY_SORT_TYPE = 'clash-verge-proxy-sort-type'; +type ProxySortType = 'default' | 'delay' | 'name'; +interface IProxyGroup { + name: string; + type: string; + now: string; + all: (string | { name: string })[]; +} + +// --- Вспомогательная функция для цвета задержки --- +function getDelayBadgeVariant(delayValue: number): 'default' | 'secondary' | 'destructive' | 'outline' { + if (delayValue < 0) return 'secondary'; + if (delayValue >= 10000) return 'destructive'; + if (delayValue >= 500) return 'destructive'; + if (delayValue >= 200) return 'outline'; + return 'default'; +} + +// --- Дочерний компонент для элемента списка с "живым" обновлением пинга --- +const ProxySelectItem = ({ proxyName, groupName }: { proxyName: string, groupName: string }) => { + const [delay, setDelay] = useState(() => delayManager.getDelay(proxyName, groupName)); + const [isJustUpdated, setIsJustUpdated] = useState(false); + + useEffect(() => { + const listener = (newDelay: number) => { + setDelay((currentDelay) => { + if (newDelay >= 0 && newDelay !== currentDelay) { + setIsJustUpdated(true); + setTimeout(() => setIsJustUpdated(false), 600); + } + return newDelay; + }); + }; + + delayManager.setListener(proxyName, groupName, listener); + return () => { + delayManager.removeListener(proxyName, groupName); + }; + }, [proxyName, groupName]); + + return ( + +
+ {proxyName} + + {delay < 0 ? '---' : delay} + +
+
+ ); +}; + + +export const ProxySelectors: React.FC = () => { + const { t } = useTranslation(); + const { verge } = useVerge(); + const { proxies, connections, clashConfig, refreshProxy } = useAppData(); + + const mode = clashConfig?.mode?.toLowerCase() || 'rule'; + const isGlobalMode = mode === 'global'; + const isDirectMode = mode ==='direct'; + + const [selectedGroup, setSelectedGroup] = useState(''); + const [selectedProxy, setSelectedProxy] = useState(''); + const [sortType, setSortType] = useState(() => (localStorage.getItem(STORAGE_KEY_SORT_TYPE) as ProxySortType) || 'default'); + + useEffect(() => { + if (!proxies?.groups) return; + if (isGlobalMode) { setSelectedGroup('GLOBAL'); return; } + if (isDirectMode) { setSelectedGroup('DIRECT'); return; } + + const savedGroup = localStorage.getItem(STORAGE_KEY_GROUP); + const primaryGroup = proxies.groups.find((g: IProxyGroup) => g.type === 'Selector' && g.name.toLowerCase().includes('auto')) || proxies.groups.find((g: IProxyGroup) => g.type === 'Selector'); + + if (savedGroup && proxies.groups.some((g: IProxyGroup) => g.name === savedGroup)) { + setSelectedGroup(savedGroup); + } else if (primaryGroup) { + setSelectedGroup(primaryGroup.name); + } else if (proxies.groups.length > 0) { + const firstSelector = proxies.groups.find((g: IProxyGroup) => g.type === 'Selector'); + if (firstSelector) { + setSelectedGroup(firstSelector.name); + } + } + }, [proxies, isGlobalMode, isDirectMode]); + + useEffect(() => { + if (!selectedGroup || !proxies) return; + if (isGlobalMode) { setSelectedProxy(proxies.global?.now || ''); return; } + if (isDirectMode) { setSelectedProxy('DIRECT'); return; } + const group = proxies.groups.find((g: IProxyGroup) => g.name === selectedGroup); + if (group) { + const current = group.now; + const firstInList = typeof group.all?.[0] === 'string' ? group.all[0] : group.all?.[0]?.name; + setSelectedProxy(current || firstInList || ''); + } + }, [selectedGroup, proxies, isGlobalMode, isDirectMode]); + + useEffect(() => { + if (!selectedGroup || !proxies?.groups || isGlobalMode || isDirectMode) return; + + const group = proxies.groups.find((g: IProxyGroup) => g.name === selectedGroup); + if (group && group.all) { + const proxyNames = group.all.map((p: any) => typeof p === 'string' ? p : p.name).filter(Boolean); + const timeout = verge?.default_latency_timeout || 5000; + delayManager.checkListDelay(proxyNames, selectedGroup, timeout); + } + }, [selectedGroup, proxies, isGlobalMode, isDirectMode, verge]); + + const handleGroupChange = (newGroup: string) => { + if (isGlobalMode || isDirectMode) return; + setSelectedGroup(newGroup); + localStorage.setItem(STORAGE_KEY_GROUP, newGroup); + }; + + const handleProxyChange = async (newProxy: string) => { + if (newProxy === selectedProxy) return; + const previousProxy = selectedProxy; + setSelectedProxy(newProxy); + try { + await updateProxy(selectedGroup, newProxy); + if (verge?.auto_close_connection && previousProxy) { + connections?.data.forEach((conn: any) => { + if (conn.chains.includes(previousProxy)) { + deleteConnection(conn.id); + } + }); + } + setTimeout(() => refreshProxy(), 300); + } catch (error) { + console.error("Failed to update proxy", error); + } + }; + + const handleSortChange = () => { + const nextSort: Record = { default: 'delay', delay: 'name', name: 'default' }; + const newSortType = nextSort[sortType]; + setSortType(newSortType); + localStorage.setItem(STORAGE_KEY_SORT_TYPE, newSortType); + }; + + const selectorGroups = useMemo(() => { + if (!proxies?.groups) return []; + return proxies.groups.filter((g: IProxyGroup) => g.type === 'Selector'); + }, [proxies]); + + const proxyOptions = useMemo(() => { + let options: { name: string }[] = []; + if (isDirectMode) return [{ name: "DIRECT" }]; + + const sourceList = isGlobalMode ? proxies?.global?.all : proxies?.groups?.find((g: IProxyGroup) => g.name === selectedGroup)?.all; + + if (sourceList) { + options = sourceList.map((proxy: any) => ({ + name: typeof proxy === 'string' ? proxy : proxy.name, + })).filter((p: { name: string }) => p.name); + } + + if (sortType === 'name') return options.sort((a, b) => a.name.localeCompare(b.name)); + if (sortType === 'delay') { + return options.sort((a, b) => { + const delayA = delayManager.getDelay(a.name, selectedGroup); + const delayB = delayManager.getDelay(b.name, selectedGroup); + if (delayA < 0) return 1; + if (delayB < 0) return -1; + return delayA - delayB; + }); + } + return options; + }, [selectedGroup, proxies, sortType, isGlobalMode, isDirectMode]); + + return ( + +
+
+ + +
+ +
+
+ + +
+ +
+
+
+ ); +}; diff --git a/src/components/layout/layout-item.tsx b/src/components/layout/layout-item.tsx index 35e686a2..f835f70e 100644 --- a/src/components/layout/layout-item.tsx +++ b/src/components/layout/layout-item.tsx @@ -1,71 +1,43 @@ -import { - alpha, - ListItem, - ListItemButton, - ListItemText, - ListItemIcon, -} from "@mui/material"; -import { useMatch, useResolvedPath, useNavigate } from "react-router-dom"; +import { Link, useMatch, useResolvedPath } from "react-router-dom"; import { useVerge } from "@/hooks/use-verge"; +import { cn } from "@root/lib/utils"; + interface Props { to: string; children: string; icon: React.ReactNode[]; } + export const LayoutItem = (props: Props) => { const { to, children, icon } = props; const { verge } = useVerge(); const { menu_icon } = verge ?? {}; const resolved = useResolvedPath(to); const match = useMatch({ path: resolved.pathname, end: true }); - const navigate = useNavigate(); return ( - - { - const bgcolor = - mode === "light" - ? alpha(primary.main, 0.15) - : alpha(primary.main, 0.35); - const color = mode === "light" ? "#1f1f1f" : "#ffffff"; - - return { - "&.Mui-selected": { bgcolor }, - "&.Mui-selected:hover": { bgcolor }, - "&.Mui-selected .MuiListItemText-primary": { color }, - }; - }, - ]} - onClick={() => navigate(to)} - > - {(menu_icon === "monochrome" || !menu_icon) && ( - - {icon[0]} - + + {(menu_icon === "monochrome" || !menu_icon) && ( + {icon[0]} + )} + {menu_icon === "colorful" && {icon[1]}} + {icon[1]}} - - - + > + {children} + + ); }; diff --git a/src/components/layout/layout-traffic.tsx b/src/components/layout/layout-traffic.tsx index 54308d86..2fdeedef 100644 --- a/src/components/layout/layout-traffic.tsx +++ b/src/components/layout/layout-traffic.tsx @@ -1,10 +1,5 @@ import { useEffect, useRef, useState } from "react"; -import { Box, Typography } from "@mui/material"; -import { - ArrowDownwardRounded, - ArrowUpwardRounded, - MemoryRounded, -} from "@mui/icons-material"; +import { ArrowDown, ArrowUp, Database } from "lucide-react"; import { useClashInfo } from "@/hooks/use-clash"; import { useVerge } from "@/hooks/use-verge"; import { TrafficGraph, type TrafficRef } from "./traffic-graph"; @@ -14,6 +9,7 @@ import useSWRSubscription from "swr/subscription"; import { createAuthSockette } from "@/utils/websocket"; import { useTranslation } from "react-i18next"; import { isDebugEnabled, gc } from "@/services/api"; +import { cn } from "@root/lib/utils"; interface MemoryUsage { inuse: number; @@ -149,77 +145,77 @@ export const LayoutTraffic = () => { const [down, downUnit] = parseTraffic(traffic.down); const [inuse, inuseUnit] = parseTraffic(memory.inuse); - const boxStyle: any = { - display: "flex", - alignItems: "center", - whiteSpace: "nowrap", - }; - const iconStyle: any = { - sx: { mr: "8px", fontSize: 16 }, - }; - const valStyle: any = { - component: "span", - textAlign: "center", - sx: { flex: "1 1 56px", userSelect: "none" }, - }; - const unitStyle: any = { - component: "span", - color: "grey.500", - fontSize: "12px", - textAlign: "right", - sx: { flex: "0 1 27px", userSelect: "none" }, - }; - return ( - +
{trafficGraph && pageVisible && (
)} - - - 0 ? "secondary" : "disabled"} +
+
+ 0 ? "text-secondary" : "text-muted-foreground", + )} /> - + {up} - - {upUnit}/s - + + + {upUnit}/s + +
- - 0 ? "primary" : "disabled"} +
+ 0 ? "text-primary" : "text-muted-foreground", + )} /> - + {down} - - {downUnit}/s - + + + {downUnit}/s + +
{displayMemory && ( - { isDebug && (await gc()); }} > - - {inuse} - {inuseUnit} - + + + {inuse} + + + {inuseUnit} + +
)} -
-
+
+ ); }; diff --git a/src/components/layout/scroll-top-button.tsx b/src/components/layout/scroll-top-button.tsx index 8813541e..9a4dcf8a 100644 --- a/src/components/layout/scroll-top-button.tsx +++ b/src/components/layout/scroll-top-button.tsx @@ -1,37 +1,26 @@ -import { IconButton, Fade, SxProps, Theme } from "@mui/material"; -import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; +import { ArrowUp } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { cn } from "@root/lib/utils"; interface Props { onClick: () => void; show: boolean; - sx?: SxProps; + className?: string; } -export const ScrollTopButton = ({ onClick, show, sx }: Props) => { +export const ScrollTopButton = ({ onClick, show, className }: Props) => { return ( - - - theme.palette.mode === "dark" - ? "rgba(255,255,255,0.1)" - : "rgba(0,0,0,0.1)", - "&:hover": { - backgroundColor: (theme) => - theme.palette.mode === "dark" - ? "rgba(255,255,255,0.2)" - : "rgba(0,0,0,0.2)", - }, - visibility: show ? "visible" : "hidden", - ...sx, - }} - > - - - + ); }; diff --git a/src/components/layout/traffic-graph.tsx b/src/components/layout/traffic-graph.tsx index 660d6298..6befc1f7 100644 --- a/src/components/layout/traffic-graph.tsx +++ b/src/components/layout/traffic-graph.tsx @@ -1,5 +1,5 @@ import { forwardRef, useEffect, useImperativeHandle, useRef } from "react"; -import { useTheme } from "@mui/material"; +import { useThemeMode } from "@/services/states"; const maxPoint = 30; @@ -32,7 +32,7 @@ export const TrafficGraph = forwardRef((props, ref) => { const cacheRef = useRef(null); - const { palette } = useTheme(); + const mode = useThemeMode(); useImperativeHandle(ref, () => ({ appendData: (data: TrafficData) => { @@ -76,10 +76,14 @@ export const TrafficGraph = forwardRef((props, ref) => { if (!context) return; - const { primary, secondary, divider } = palette; - const refLineColor = divider || "rgba(0, 0, 0, 0.12)"; - const upLineColor = secondary.main || "#9c27b0"; - const downLineColor = primary.main || "#5b5c9d"; + const computedStyle = getComputedStyle(document.documentElement); + const refLineColor = + `hsl(${computedStyle.getPropertyValue("--border")})` || + "rgba(0, 0, 0, 0.12)"; + const upLineColor = + `hsl(${computedStyle.getPropertyValue("--secondary")})` || "#9c27b0"; + const downLineColor = + `hsl(${computedStyle.getPropertyValue("--primary")})` || "#5b5c9d"; const width = canvas.width; const height = canvas.height; @@ -193,7 +197,7 @@ export const TrafficGraph = forwardRef((props, ref) => { return () => { cancelAnimationFrame(raf); }; - }, [palette]); + }, [mode]); return ; }); diff --git a/src/components/layout/update-button.tsx b/src/components/layout/update-button.tsx index 74e9e736..f91b0772 100644 --- a/src/components/layout/update-button.tsx +++ b/src/components/layout/update-button.tsx @@ -1,10 +1,10 @@ import useSWR from "swr"; import { useRef } from "react"; -import { Button } from "@mui/material"; import { check } from "@tauri-apps/plugin-updater"; import { UpdateViewer } from "../setting/mods/update-viewer"; import { DialogRef } from "../base"; import { useVerge } from "@/hooks/use-verge"; +import { Button } from "@/components/ui/button"; interface Props { className?: string; @@ -34,9 +34,8 @@ export const UpdateButton = (props: Props) => { - - - + + + + {title} + {description} + + + {t("Cancel")} + + {t("Confirm")} + + + + ); }; diff --git a/src/components/profile/editor-viewer.tsx b/src/components/profile/editor-viewer.tsx index 7a0e1dd3..584769ae 100644 --- a/src/components/profile/editor-viewer.tsx +++ b/src/components/profile/editor-viewer.tsx @@ -1,20 +1,6 @@ import { ReactNode, useEffect, useRef, useState } from "react"; import { useLockFn } from "ahooks"; import { useTranslation } from "react-i18next"; -import { - Button, - ButtonGroup, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - IconButton, -} from "@mui/material"; -import { - FormatPaintRounded, - OpenInFullRounded, - CloseFullscreenRounded, -} from "@mui/icons-material"; import { useThemeMode } from "@/services/states"; import { nanoid } from "nanoid"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; @@ -22,6 +8,7 @@ import { showNotice } from "@/services/noticeService"; import getSystem from "@/utils/get-system"; import debounce from "@/utils/debounce"; +// --- Новые импорты --- import * as monaco from "monaco-editor"; import MonacoEditor from "react-monaco-editor"; import { configureMonacoYaml } from "monaco-yaml"; @@ -29,8 +16,26 @@ import { type JSONSchema7 } from "json-schema"; import metaSchema from "meta-json-schema/schemas/meta-json-schema.json"; import mergeSchema from "meta-json-schema/schemas/clash-verge-merge-json-schema.json"; import pac from "types-pac/pac.d.ts?raw"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, + DialogClose, +} from "@/components/ui/dialog"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Wand2, Maximize, Minimize } from "lucide-react"; + const appWindow = getCurrentWebviewWindow(); +// --- Типы и интерфейсы (без изменений) --- type Language = "yaml" | "javascript" | "css"; type Schema = LanguageSchemaMap[T]; interface LanguageSchemaMap { @@ -51,11 +56,11 @@ interface Props { onClose: () => void; } +// --- Логика инициализации Monaco (без изменений) --- let initialized = false; const monacoInitialization = () => { if (initialized) return; - // configure yaml worker configureMonacoYaml(monaco, { validate: true, enableSchemaRequest: true, @@ -74,7 +79,6 @@ const monacoInitialization = () => { }, ], }); - // configure PAC definition monaco.languages.typescript.javascriptDefaults.addExtraLib(pac, "pac.d.ts"); initialized = true; @@ -170,85 +174,97 @@ export const EditorViewer = (props: Props) => { }, []); return ( - - {title} - + - = 1500, // 超过一定宽度显示minimap滚动条 - }, - mouseWheelZoom: true, // 按住Ctrl滚轮调节缩放比例 - readOnly: readOnly, // 只读模式 - readOnlyMessage: { value: t("ReadOnlyMessage") }, // 只读模式尝试编辑时的提示信息 - renderValidationDecorations: "on", // 只读模式下显示校验信息 - quickSuggestions: { - strings: true, // 字符串类型的建议 - comments: true, // 注释类型的建议 - other: true, // 其他类型的建议 - }, - padding: { - top: 33, // 顶部padding防止遮挡snippets - }, - fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${ - getSystem() === "windows" ? ", twemoji mozilla" : "" - }`, - fontLigatures: false, // 连字符 - smoothScrolling: true, // 平滑滚动 - }} - editorWillMount={editorWillMount} - editorDidMount={editorDidMount} - onChange={handleChange} - /> + + {title} + - - - editorRef.current - ?.getAction("editor.action.formatDocument") - ?.run() - } - > - - - appWindow.toggleMaximize().then(editorResize)} - > - {isMaximized ? : } - - +
+ = 1500, + }, + mouseWheelZoom: true, + readOnly: readOnly, + quickSuggestions: { strings: true, comments: true, other: true }, + padding: { top: 16 }, + fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${ + getSystem() === "windows" ? ", twemoji mozilla" : "" + }`, + fontLigatures: false, + smoothScrolling: true, + }} + editorWillMount={editorWillMount} + editorDidMount={editorDidMount} + onChange={handleChange} + /> +
+ + + + + + +

{t("Format document")}

+
+
+ + + + + +

{t(isMaximized ? "Minimize" : "Maximize")}

+
+
+
+
+
+ + + + + + {!readOnly && ( + + )} +
- - - - {!readOnly && ( - - )} -
); }; diff --git a/src/components/profile/file-input.tsx b/src/components/profile/file-input.tsx index 326dfca7..20eb2bca 100644 --- a/src/components/profile/file-input.tsx +++ b/src/components/profile/file-input.tsx @@ -1,61 +1,81 @@ -import { useRef, useState } from "react"; +import React, { useRef, useState } from "react"; import { useLockFn } from "ahooks"; import { useTranslation } from "react-i18next"; -import { Box, Button, Typography } from "@mui/material"; + +// Новые импорты +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; // Используем Input для консистентности +import { Loader2 } from "lucide-react"; // Иконка для спиннера interface Props { onChange: (file: File, value: string) => void; } -export const FileInput = (props: Props) => { +export const FileInput: React.FC = (props) => { const { onChange } = props; - const { t } = useTranslation(); - // file input - const inputRef = useRef(undefined); + + const inputRef = useRef(null); const [loading, setLoading] = useState(false); const [fileName, setFileName] = useState(""); - const onFileInput = useLockFn(async (e: any) => { - const file = e.target.files?.[0] as File; - + // Вся ваша логика для чтения файла остается без изменений + const onFileInput = useLockFn(async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; if (!file) return; setFileName(file.name); setLoading(true); - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = (event) => { - resolve(null); - onChange(file, event.target?.result as string); - }; - reader.onerror = reject; - reader.readAsText(file); - }).finally(() => setLoading(false)); + try { + const value = await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (event) => { + resolve(event.target?.result as string); + }; + reader.onerror = (err) => reject(err); + reader.readAsText(file); + }); + onChange(file, value); + } catch (error) { + console.error("File reading error:", error); + } finally { + setLoading(false); + // Очищаем value у input, чтобы можно было выбрать тот же файл еще раз + if (inputRef.current) { + inputRef.current.value = ""; + } + } }); return ( - + // Заменяем Box на div с flex и gap для отступов +
- - - {loading ? "Loading..." : fileName} - - + {/* Область для отображения имени файла или статуса загрузки */} +
+ {loading && } +

+ {loading ? t("Loading...") : fileName || t("No file selected")} +

+
+
); }; diff --git a/src/components/profile/group-item.tsx b/src/components/profile/group-item.tsx index b069d187..d52d2fa0 100644 --- a/src/components/profile/group-item.tsx +++ b/src/components/profile/group-item.tsx @@ -1,26 +1,34 @@ -import { - Box, - IconButton, - ListItem, - ListItemText, - alpha, - styled, -} from "@mui/material"; -import { DeleteForeverRounded, UndoRounded } from "@mui/icons-material"; +import { useEffect, useState } from "react"; import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { downloadIconCache } from "@/services/cmds"; import { convertFileSrc } from "@tauri-apps/api/core"; -import { useEffect, useState } from "react"; +import { cn } from "@root/lib/utils"; + +// Новые импорты +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { GripVertical, Trash2, Undo2 } from "lucide-react"; + interface Props { type: "prepend" | "original" | "delete" | "append"; group: IProxyGroupConfig; onDelete: () => void; } +// Определяем стили для каждого типа элемента +const typeStyles = { + original: "bg-secondary/50", + delete: "bg-destructive/20 text-muted-foreground line-through", + prepend: "bg-green-500/20", + append: "bg-green-500/20", +}; + export const GroupItem = (props: Props) => { - let { type, group, onDelete } = props; - const sortable = type === "prepend" || type === "append"; + const { type, group, onDelete } = props; + + // Drag-and-drop будет работать только для 'prepend' и 'append' типов + const isSortable = type === "prepend" || type === "append"; const { attributes, @@ -29,145 +37,73 @@ export const GroupItem = (props: Props) => { transform, transition, isDragging, - } = sortable - ? useSortable({ id: group.name }) - : { - attributes: {}, - listeners: {}, - setNodeRef: null, - transform: null, - transition: null, - isDragging: false, - }; + } = useSortable({ id: group.name, disabled: !isSortable }); const [iconCachePath, setIconCachePath] = useState(""); - useEffect(() => { - initIconCachePath(); - }, [group]); + const getFileName = (url: string) => url.substring(url.lastIndexOf("/") + 1); async function initIconCachePath() { if (group.icon && group.icon.trim().startsWith("http")) { - const fileName = - group.name.replaceAll(" ", "") + "-" + getFileName(group.icon); + const fileName = group.name.replaceAll(" ", "") + "-" + getFileName(group.icon); const iconPath = await downloadIconCache(group.icon, fileName); setIconCachePath(convertFileSrc(iconPath)); } } - function getFileName(url: string) { - return url.substring(url.lastIndexOf("/") + 1); - } + useEffect(() => { initIconCachePath(); }, [group.icon, group.name]); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + zIndex: isDragging ? 100 : undefined, + }; return ( - ({ - position: "relative", - background: - type === "original" - ? palette.mode === "dark" - ? alpha(palette.background.paper, 0.3) - : alpha(palette.grey[400], 0.3) - : type === "delete" - ? alpha(palette.error.main, 0.3) - : alpha(palette.success.main, 0.3), - height: "100%", - margin: "8px 0", - borderRadius: "8px", - transform: CSS.Transform.toString(transform), - transition, - zIndex: isDragging ? "calc(infinity)" : undefined, - })} +
- {group.icon && group.icon?.trim().startsWith("http") && ( - - )} - {group.icon && group.icon?.trim().startsWith("data") && ( - - )} - {group.icon && group.icon?.trim().startsWith(" - )} - - {group.name} - - } - secondary={ - - - {group.type} - - - } - secondaryTypographyProps={{ - sx: { - display: "flex", - alignItems: "center", - color: "#ccc", - }, - }} - /> - - {type === "delete" ? : } - - + className={cn("p-1 text-muted-foreground rounded-sm", isSortable ? "cursor-move hover:bg-accent" : "cursor-default")} + > + +
+ + {/* Иконка группы */} + {group.icon && ( + {group.name} + )} + + {/* Название и тип группы */} +
+

{group.name}

+
+ {group.type} +
+
+ + {/* Кнопка действия */} + + ); }; - -const StyledPrimary = styled("div")` - font-size: 15px; - font-weight: 700; - line-height: 1.5; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -`; - -const ListItemTextChild = styled("span")` - display: block; -`; - -const StyledTypeBox = styled(ListItemTextChild)(({ theme }) => ({ - display: "inline-block", - border: "1px solid #ccc", - borderColor: alpha(theme.palette.primary.main, 0.5), - color: alpha(theme.palette.primary.main, 0.8), - borderRadius: 4, - fontSize: 10, - padding: "0 4px", - lineHeight: 1.5, - marginRight: "8px", -})); diff --git a/src/components/profile/groups-editor-viewer.tsx b/src/components/profile/groups-editor-viewer.tsx index 1a7c445f..d46acd1b 100644 --- a/src/components/profile/groups-editor-viewer.tsx +++ b/src/components/profile/groups-editor-viewer.tsx @@ -14,41 +14,76 @@ import { import { SortableContext, sortableKeyboardCoordinates, + useSortable, } from "@dnd-kit/sortable"; -import { - Autocomplete, - Box, - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - InputAdornment, - List, - ListItem, - ListItemText, - TextField, - styled, -} from "@mui/material"; -import { - VerticalAlignTopRounded, - VerticalAlignBottomRounded, -} from "@mui/icons-material"; -import { GroupItem } from "@/components/profile/group-item"; +import { CSS } from "@dnd-kit/utilities"; +import { Virtuoso } from "react-virtuoso"; +import MonacoEditor from "react-monaco-editor"; +import { useForm } from "react-hook-form"; + import { getNetworkInterfaces, readProfileFile, saveProfileFile, } from "@/services/cmds"; -import { Switch } from "@/components/base"; import getSystem from "@/utils/get-system"; -import { BaseSearchBox } from "../base/base-search-box"; -import { Virtuoso } from "react-virtuoso"; -import MonacoEditor from "react-monaco-editor"; import { useThemeMode } from "@/services/states"; -import { Controller, useForm } from "react-hook-form"; +import { BaseSearchBox } from "../base/base-search-box"; import { showNotice } from "@/services/noticeService"; +import { cn } from "@root/lib/utils"; +// --- Компоненты shadcn/ui и иконки --- +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, + DialogClose, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { Label } from "@/components/ui/label"; +import { Separator } from "@/components/ui/separator"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command"; +import { Badge } from "@/components/ui/badge"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormDescription } from "@/components/ui/form"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Check, + ChevronsUpDown, + GripVertical, + Trash2, + Undo2, + ArrowDownToLine, + ArrowUpToLine, +} from "lucide-react"; + + +// --- Вспомогательные функции, константы и валидаторы --- +const portValidator = (value: string): boolean => /^(?:[1-9]\d{0,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/.test(value); +const ipv4CIDRValidator = (value: string): boolean => /^(?:(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))\.){3}(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))(?:\/(?:[12]?[0-9]|3[0-2]))?$/.test(value); +const ipv6CIDRValidator = (value: string): boolean => /^([0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){7}|::|:(?::[0-9a-fA-F]{1,4}){1,6}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,5}|(?:[0-9a-fA-F]{1,4}:){2}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){3}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){4}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){5}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,6}:)\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])$/.test(value); +const builtinProxyPolicies = ["DIRECT", "REJECT", "REJECT-DROP", "PASS"]; interface Props { proxiesUid: string; mergeUid: string; @@ -59,877 +94,351 @@ interface Props { onSave?: (prev?: string, curr?: string) => void; } -const builtinProxyPolicies = ["DIRECT", "REJECT", "REJECT-DROP", "PASS"]; - -export const GroupsEditorViewer = (props: Props) => { - const { mergeUid, proxiesUid, profileUid, property, open, onClose, onSave } = - props; - const { t } = useTranslation(); - const themeMode = useThemeMode(); - const [prevData, setPrevData] = useState(""); - const [currData, setCurrData] = useState(""); - const [visualization, setVisualization] = useState(true); - const [match, setMatch] = useState(() => (_: string) => true); - const [interfaceNameList, setInterfaceNameList] = useState([]); - const { control, watch, register, ...formIns } = useForm({ - defaultValues: { - type: "select", - name: "", - interval: 300, - timeout: 5000, - "max-failed-times": 5, - lazy: true, - }, - }); - const [groupList, setGroupList] = useState([]); - const [proxyPolicyList, setProxyPolicyList] = useState([]); - const [proxyProviderList, setProxyProviderList] = useState([]); - const [prependSeq, setPrependSeq] = useState([]); - const [appendSeq, setAppendSeq] = useState([]); - const [deleteSeq, setDeleteSeq] = useState([]); - - const filteredPrependSeq = useMemo( - () => prependSeq.filter((group) => match(group.name)), - [prependSeq, match], - ); - const filteredGroupList = useMemo( - () => groupList.filter((group) => match(group.name)), - [groupList, match], - ); - const filteredAppendSeq = useMemo( - () => appendSeq.filter((group) => match(group.name)), - [appendSeq, match], - ); - - const sensors = useSensors( - useSensor(PointerSensor), - useSensor(KeyboardSensor, { - coordinateGetter: sortableKeyboardCoordinates, - }), - ); - const reorder = ( - list: IProxyGroupConfig[], - startIndex: number, - endIndex: number, - ) => { - const result = Array.from(list); - const [removed] = result.splice(startIndex, 1); - result.splice(endIndex, 0, removed); - return result; - }; - const onPrependDragEnd = async (event: DragEndEvent) => { - const { active, over } = event; - if (over) { - if (active.id !== over.id) { - let activeIndex = 0; - let overIndex = 0; - prependSeq.forEach((item, index) => { - if (item.name === active.id) { - activeIndex = index; - } - if (item.name === over.id) { - overIndex = index; - } - }); - - setPrependSeq(reorder(prependSeq, activeIndex, overIndex)); - } - } - }; - const onAppendDragEnd = async (event: DragEndEvent) => { - const { active, over } = event; - if (over) { - if (active.id !== over.id) { - let activeIndex = 0; - let overIndex = 0; - appendSeq.forEach((item, index) => { - if (item.name === active.id) { - activeIndex = index; - } - if (item.name === over.id) { - overIndex = index; - } - }); - setAppendSeq(reorder(appendSeq, activeIndex, overIndex)); - } - } - }; - const fetchContent = async () => { - let data = await readProfileFile(property); - let obj = yaml.load(data) as ISeqProfileConfig | null; - - setPrependSeq(obj?.prepend || []); - setAppendSeq(obj?.append || []); - setDeleteSeq(obj?.delete || []); - - setPrevData(data); - setCurrData(data); - }; - - useEffect(() => { - if (currData === "") return; - if (visualization !== true) return; - - let obj = yaml.load(currData) as { - prepend: []; - append: []; - delete: []; - } | null; - setPrependSeq(obj?.prepend || []); - setAppendSeq(obj?.append || []); - setDeleteSeq(obj?.delete || []); - }, [visualization]); - - // 优化:异步处理大数据yaml.dump,避免UI卡死 - useEffect(() => { - if (prependSeq && appendSeq && deleteSeq) { - const serialize = () => { - try { - setCurrData( - yaml.dump( - { prepend: prependSeq, append: appendSeq, delete: deleteSeq }, - { forceQuotes: true }, - ), - ); - } catch (e) { - // 防止异常导致UI卡死 - } - }; - if (window.requestIdleCallback) { - window.requestIdleCallback(serialize); - } else { - setTimeout(serialize, 0); - } - } - }, [prependSeq, appendSeq, deleteSeq]); - - const fetchProxyPolicy = async () => { - let data = await readProfileFile(profileUid); - let proxiesData = await readProfileFile(proxiesUid); - let originGroupsObj = yaml.load(data) as { - "proxy-groups": IProxyGroupConfig[]; - } | null; - - let originProxiesObj = yaml.load(data) as { proxies: [] } | null; - let originProxies = originProxiesObj?.proxies || []; - let moreProxiesObj = yaml.load(proxiesData) as ISeqProfileConfig | null; - let morePrependProxies = moreProxiesObj?.prepend || []; - let moreAppendProxies = moreProxiesObj?.append || []; - let moreDeleteProxies = - moreProxiesObj?.delete || ([] as string[] | { name: string }[]); - - let proxies = morePrependProxies.concat( - originProxies.filter((proxy: any) => { - if (proxy.name) { - return !moreDeleteProxies.includes(proxy.name); - } else { - return !moreDeleteProxies.includes(proxy); - } - }), - moreAppendProxies, - ); - - setProxyPolicyList( - builtinProxyPolicies.concat( - prependSeq.map((group: IProxyGroupConfig) => group.name), - originGroupsObj?.["proxy-groups"] - .map((group: IProxyGroupConfig) => group.name) - .filter((name) => !deleteSeq.includes(name)) || [], - appendSeq.map((group: IProxyGroupConfig) => group.name), - proxies.map((proxy: any) => proxy.name), - ), - ); - }; - const fetchProfile = async () => { - let data = await readProfileFile(profileUid); - let mergeData = await readProfileFile(mergeUid); - let globalMergeData = await readProfileFile("Merge"); - - let originGroupsObj = yaml.load(data) as { - "proxy-groups": IProxyGroupConfig[]; - } | null; - - let originProviderObj = yaml.load(data) as { "proxy-providers": {} } | null; - let originProvider = originProviderObj?.["proxy-providers"] || {}; - - let moreProviderObj = yaml.load(mergeData) as { - "proxy-providers": {}; - } | null; - let moreProvider = moreProviderObj?.["proxy-providers"] || {}; - - let globalProviderObj = yaml.load(globalMergeData) as { - "proxy-providers": {}; - } | null; - let globalProvider = globalProviderObj?.["proxy-providers"] || {}; - - let provider = Object.assign( - {}, - originProvider, - moreProvider, - globalProvider, - ); - - setProxyProviderList(Object.keys(provider)); - setGroupList(originGroupsObj?.["proxy-groups"] || []); - }; - const getInterfaceNameList = async () => { - let list = await getNetworkInterfaces(); - setInterfaceNameList(list); - }; - useEffect(() => { - fetchProxyPolicy(); - }, [prependSeq, appendSeq, deleteSeq]); - useEffect(() => { - if (!open) return; - fetchContent(); - fetchProxyPolicy(); - fetchProfile(); - getInterfaceNameList(); - }, [open]); - - const validateGroup = () => { - let group = formIns.getValues(); - if (group.name === "") { - throw new Error(t("Group Name Required")); - } - }; - - const handleSave = useLockFn(async () => { - try { - await saveProfileFile(property, currData); - showNotice("success", t("Saved Successfully")); - onSave?.(prevData, currData); - onClose(); - } catch (err: any) { - showNotice("error", err.toString()); - } - }); - - return ( - - - { - - {t("Edit Groups")} - - - - - } - - - - {visualization ? ( - <> - - - ( - - - ( -
  • - {option} -
  • - )} - onChange={(_, value) => value && field.onChange(value)} - renderInput={(params) => } - /> -
    - )} - /> - ( - - - - - )} - /> - ( - - - - - )} - /> - ( - - - value && field.onChange(value)} - renderInput={(params) => } - renderOption={(props, option) => ( -
  • - {option} -
  • - )} - /> -
    - )} - /> - ( - - - value && field.onChange(value)} - renderInput={(params) => } - /> - - )} - /> - ( - - - - - )} - /> - ( - - - { - field.onChange(parseInt(e.target.value)); - }} - /> - - )} - /> - ( - - - { - field.onChange(parseInt(e.target.value)); - }} - slotProps={{ - input: { - endAdornment: ( - - {t("seconds")} - - ), - }, - }} - /> - - )} - /> - ( - - - { - field.onChange(parseInt(e.target.value)); - }} - slotProps={{ - input: { - endAdornment: ( - - {t("millis")} - - ), - }, - }} - /> - - )} - /> - ( - - - { - field.onChange(parseInt(e.target.value)); - }} - /> - - )} - /> - ( - - - value && field.onChange(value)} - renderInput={(params) => } - /> - - )} - /> - ( - - - { - field.onChange(parseInt(e.target.value)); - }} - /> - - )} - /> - ( - - - - - )} - /> - ( - - - - - )} - /> - ( - - - { - field.onChange(value.join("|")); - }} - renderInput={(params) => } - /> - - )} - /> - ( - - - - - )} - /> - ( - - - - - )} - /> - ( - - - - - )} - /> - ( - - - - - )} - /> - ( - - - - - )} - /> - ( - - - - - )} - /> -
    - - - - - - -
    - - - setMatch(() => match)} /> - 0 ? 1 : 0) + - (filteredAppendSeq.length > 0 ? 1 : 0) - } - increaseViewportBy={256} - itemContent={(index) => { - let shift = filteredPrependSeq.length > 0 ? 1 : 0; - if (filteredPrependSeq.length > 0 && index === 0) { - return ( - - { - return x.name; - })} - > - {filteredPrependSeq.map((item, index) => { - return ( - { - setPrependSeq( - prependSeq.filter( - (v) => v.name !== item.name, - ), - ); - }} - /> - ); - })} - - - ); - } else if (index < filteredGroupList.length + shift) { - let newIndex = index - shift; - return ( - { - if ( - deleteSeq.includes(filteredGroupList[newIndex].name) - ) { - setDeleteSeq( - deleteSeq.filter( - (v) => v !== filteredGroupList[newIndex].name, - ), - ); - } else { - setDeleteSeq((prev) => [ - ...prev, - filteredGroupList[newIndex].name, - ]); - } - }} - /> - ); - } else { - return ( - - { - return x.name; - })} - > - {filteredAppendSeq.map((item, index) => { - return ( - { - setAppendSeq( - appendSeq.filter( - (v) => v.name !== item.name, - ), - ); - }} - /> - ); - })} - - - ); - } - }} - /> - - - ) : ( - = 1500, // 超过一定宽度显示minimap滚动条 - }, - mouseWheelZoom: true, // 按住Ctrl滚轮调节缩放比例 - quickSuggestions: { - strings: true, // 字符串类型的建议 - comments: true, // 注释类型的建议 - other: true, // 其他类型的建议 - }, - padding: { - top: 33, // 顶部padding防止遮挡snippets - }, - fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${ - getSystem() === "windows" ? ", twemoji mozilla" : "" - }`, - fontLigatures: false, // 连字符 - smoothScrolling: true, // 平滑滚动 - }} - onChange={(value) => setCurrData(value)} - /> - )} -
    - - - - - - -
    - ); + + + + + No results found. + + {options.map((option) => ( + { onSelect(options.find(opt => opt.toLowerCase() === currentValue) || ''); setOpen(false); }}> + + {option} + + ))} + + + + + ); }; -const Item = styled(ListItem)(() => ({ - padding: "5px 2px", -})); +// --- Новый компонент MultiSelectCombobox (множественный выбор) --- +const MultiSelectCombobox = ({ options, value, onChange, placeholder }: { options: string[], value: string[], onChange: (value: string[]) => void, placeholder?: string }) => { + const [open, setOpen] = useState(false); + const selectedSet = new Set(value); + + const handleSelect = (currentValue: string) => { + const newSet = new Set(selectedSet); + if (newSet.has(currentValue)) { + newSet.delete(currentValue); + } else { + newSet.add(currentValue); + } + onChange(Array.from(newSet)); + }; + + return ( + + + + + + + + No results found. + + {options.map((option) => ( + handleSelect(option)} className="cursor-pointer"> + + {option} + + ))} + + + + + ); +}; + +// --- Новый компонент для элемента списка групп --- +const EditorGroupItem = ({ type, group, onDelete, id }: { type: string, group: IProxyGroupConfig, onDelete: () => void, id: string }) => { + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id }); + const style = { transform: CSS.Transform.toString(transform), transition, zIndex: isDragging ? 100 : undefined }; + const isDelete = type === 'delete'; + return ( +
    +
    +

    {group.name}

    + +
    + ) +}; + +export const GroupsEditorViewer = (props: Props) => { + const { mergeUid, proxiesUid, profileUid, property, open, onClose, onSave } = props; + const { t } = useTranslation(); + const themeMode = useThemeMode(); + const [prevData, setPrevData] = useState(""); + const [currData, setCurrData] = useState(""); + const [visualization, setVisualization] = useState(true); + const [match, setMatch] = useState(() => (_: string) => true); + const [interfaceNameList, setInterfaceNameList] = useState([]); + + const form = useForm({ + defaultValues: { type: "select", name: "", interval: 300, timeout: 5000, "max-failed-times": 5, lazy: true }, + }); + const { control, watch, handleSubmit, getValues } = form; + + const [groupList, setGroupList] = useState([]); + const [proxyPolicyList, setProxyPolicyList] = useState([]); + const [proxyProviderList, setProxyProviderList] = useState([]); + const [prependSeq, setPrependSeq] = useState([]); + const [appendSeq, setAppendSeq] = useState([]); + const [deleteSeq, setDeleteSeq] = useState([]); + + const filteredPrependSeq = useMemo(() => prependSeq.filter((group) => match(group.name)), [prependSeq, match]); + const filteredGroupList = useMemo(() => groupList.filter((group) => match(group.name)), [groupList, match]); + const filteredAppendSeq = useMemo(() => appendSeq.filter((group) => match(group.name)), [appendSeq, match]); + + const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })); + + const reorder = (list: IProxyGroupConfig[], startIndex: number, endIndex: number) => { + const result = Array.from(list); + const [removed] = result.splice(startIndex, 1); + result.splice(endIndex, 0, removed); + return result; + }; + + const onPrependDragEnd = async (event: DragEndEvent) => { + const { active, over } = event; + if (over && active.id !== over.id) { + let activeIndex = 0; + let overIndex = 0; + prependSeq.forEach((item, index) => { + if (item.name === active.id) activeIndex = index; + if (item.name === over.id) overIndex = index; + }); + setPrependSeq(reorder(prependSeq, activeIndex, overIndex)); + } + }; + + const onAppendDragEnd = async (event: DragEndEvent) => { + const { active, over } = event; + if (over && active.id !== over.id) { + let activeIndex = 0; + let overIndex = 0; + appendSeq.forEach((item, index) => { + if (item.name === active.id) activeIndex = index; + if (item.name === over.id) overIndex = index; + }); + setAppendSeq(reorder(appendSeq, activeIndex, overIndex)); + } + }; + + const fetchContent = async () => { + try { + let data = await readProfileFile(property); + let obj = yaml.load(data) as ISeqProfileConfig | null; + setPrependSeq(obj?.prepend || []); + setAppendSeq(obj?.append || []); + setDeleteSeq(obj?.delete || []); + setPrevData(data); + setCurrData(data); + } catch (error) { console.error("Failed to fetch or parse content:", error); } + }; + + useEffect(() => { + if (currData === "" || !visualization) return; + try { + let obj = yaml.load(currData) as { prepend: [], append: [], delete: [] } | null; + setPrependSeq(obj?.prepend || []); + setAppendSeq(obj?.append || []); + setDeleteSeq(obj?.delete || []); + } catch (e) { /* Ignore parsing errors while typing */ } + }, [visualization, currData]); + + useEffect(() => { + if (prependSeq && appendSeq && deleteSeq && visualization) { + const serialize = () => { + try { + setCurrData(yaml.dump({ prepend: prependSeq, append: appendSeq, delete: deleteSeq }, { forceQuotes: true })); + } catch (e: any) { showNotice("error", e?.message || e?.toString() || "YAML dump error"); } + }; + if (window.requestIdleCallback) { window.requestIdleCallback(serialize); } else { setTimeout(serialize, 0); } + } + }, [prependSeq, appendSeq, deleteSeq, visualization]); + + const fetchProxyPolicy = async () => { + try { + let data = await readProfileFile(profileUid); + let proxiesData = await readProfileFile(proxiesUid); + let originGroupsObj = yaml.load(data) as { "proxy-groups": IProxyGroupConfig[] } | null; + let originProxiesObj = yaml.load(data) as { proxies: [] } | null; + let originProxies = originProxiesObj?.proxies || []; + let moreProxiesObj = yaml.load(proxiesData) as ISeqProfileConfig | null; + let morePrependProxies = moreProxiesObj?.prepend || []; + let moreAppendProxies = moreProxiesObj?.append || []; + let moreDeleteProxies = moreProxiesObj?.delete || ([] as string[] | { name: string }[]); + let proxies = morePrependProxies.concat(originProxies.filter((proxy: any) => !moreDeleteProxies.some((del: any) => (del.name || del) === proxy.name)), moreAppendProxies); + + setProxyPolicyList(Array.from(new Set(builtinProxyPolicies.concat( + prependSeq.map((group) => group.name), + originGroupsObj?.["proxy-groups"].map((group) => group.name).filter((name) => !deleteSeq.includes(name)) || [], + appendSeq.map((group) => group.name), + proxies.map((proxy: any) => proxy.name), + )))); + } catch(error) { console.error("Failed to fetch proxy policy:", error) } + }; + + const fetchProfile = async () => { + try { + let data = await readProfileFile(profileUid); + let mergeData = await readProfileFile(mergeUid); + let globalMergeData = await readProfileFile("Merge"); + + let originGroupsObj = yaml.load(data) as { "proxy-groups": IProxyGroupConfig[] } | null; + let originProviderObj = yaml.load(data) as { "proxy-providers": {} } | null; + let originProvider = originProviderObj?.["proxy-providers"] || {}; + let moreProviderObj = yaml.load(mergeData) as { "proxy-providers": {} } | null; + let moreProvider = moreProviderObj?.["proxy-providers"] || {}; + let globalProviderObj = yaml.load(globalMergeData) as { "proxy-providers": {} } | null; + let globalProvider = globalProviderObj?.["proxy-providers"] || {}; + let provider = { ...originProvider, ...moreProvider, ...globalProvider }; + + setProxyProviderList(Object.keys(provider)); + setGroupList(originGroupsObj?.["proxy-groups"] || []); + } catch(error) { console.error("Failed to fetch profile:", error) } + }; + + const getInterfaceNameList = async () => { + try { + let list = await getNetworkInterfaces(); + setInterfaceNameList(list); + } catch (error) { console.error("Failed to get network interfaces:", error) } + }; + + useEffect(() => { fetchProxyPolicy(); }, [prependSeq, appendSeq, deleteSeq]); + + useEffect(() => { + if (open) { + fetchContent(); + fetchProxyPolicy(); + fetchProfile(); + getInterfaceNameList(); + } + }, [open]); + + const validateGroup = () => { + let group = getValues(); + if (group.name === "") { + throw new Error(t("Group Name Required")); + } + }; + + const handleSave = useLockFn(async () => { + try { + await saveProfileFile(property, currData); + showNotice("success", t("Saved Successfully")); + onSave?.(prevData, currData); + onClose(); + } catch (err: any) { + showNotice("error", err.toString()); + } + }); + + const groupType = watch("type"); + + return ( + + + +
    + {t("Edit Groups")} + +
    +
    + +
    + {visualization ? ( +
    + + {/* Левая панель: Конструктор групп */} +
    +

    Constructor

    + +
    + ({t("Group Type")})}/> + ({t("Group Name")})}/> + ({t("Proxy Group Icon")})}/> + ({t("Use Proxies")})}/> + ({t("Use Provider")})}/> + {(groupType === "url-test" || groupType === "fallback") && <> + ({t("Health Check Url")})}/> + ({t("Interval")}
    field.onChange(parseInt(e.target.value, 10) || 0)}/>{t("seconds")}
    )}/> + ({t("Timeout")}
    field.onChange(parseInt(e.target.value, 10) || 0)}/>{t("millis")}
    )}/> + ({t("Max Failed Times")} field.onChange(parseInt(e.target.value, 10) || 0)}/>)}/> + } + ({t("Interface Name")})}/> + ({t("Routing Mark")} field.onChange(parseInt(e.target.value, 10) || 0)}/>)}/> + {(groupType === "url-test" || groupType === "fallback" || groupType === "load-balance") && <> + ({t("Lazy")})} /> + ({t("Disable UDP")})} /> + } + ({t("Hidden")})} /> +
    +
    + + +
    +
    + + + +
    + setMatch(() => matcher)} /> +
    + 0 ? 1 : 0) + (filteredAppendSeq.length > 0 ? 1 : 0)} + itemContent={(index) => { + let shift = filteredPrependSeq.length > 0 ? 1 : 0; + if (filteredPrependSeq.length > 0 && index === 0) { + return ( x.name)}>{filteredPrependSeq.map((item) => ( setPrependSeq(prependSeq.filter(v => v.name !== item.name))} />))}); + } else if (index < filteredGroupList.length + shift) { + const newIndex = index - shift; + const currentGroup = filteredGroupList[newIndex]; + return ( { if (deleteSeq.includes(currentGroup.name)) { setDeleteSeq(deleteSeq.filter(v => v !== currentGroup.name)); } else { setDeleteSeq((prev) => [...prev, currentGroup.name]); }}} />); + } else { + return ( x.name)}>{filteredAppendSeq.map((item) => ( setAppendSeq(appendSeq.filter(v => v.name !== item.name))} />))}); + } + }} + /> +
    +
    + + + ) : ( +
    + = 1500 }, mouseWheelZoom: true, quickSuggestions: { strings: true, comments: true, other: true }, padding: { top: 16 }, fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${getSystem() === "windows" ? ", twemoji mozilla" : ""}`, fontLigatures: false, smoothScrolling: true }} onChange={(value) => setCurrData(value)} /> +
    + )} +
    + + + + + +
    +
    + ); +}; diff --git a/src/components/profile/log-viewer.tsx b/src/components/profile/log-viewer.tsx index f36f2b4c..c4796194 100644 --- a/src/components/profile/log-viewer.tsx +++ b/src/components/profile/log-viewer.tsx @@ -1,16 +1,19 @@ import { Fragment } from "react"; import { useTranslation } from "react-i18next"; + +// Новые импорты import { - Button, - Chip, Dialog, - DialogActions, DialogContent, + DialogHeader, DialogTitle, - Divider, - Typography, -} from "@mui/material"; + DialogFooter, + DialogClose, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Badge, badgeVariants } from "@/components/ui/badge"; import { BaseEmpty } from "@/components/base"; +import { cn } from "@root/lib/utils"; interface Props { open: boolean; @@ -20,50 +23,47 @@ interface Props { export const LogViewer = (props: Props) => { const { open, logInfo, onClose } = props; - const { t } = useTranslation(); + // Вспомогательная функция для определения варианта Badge + const getLogLevelVariant = (level: string): "destructive" | "secondary" => { + return level === "error" || level === "exception" ? "destructive" : "secondary"; + }; + return ( - - {t("Script Console")} + !isOpen && onClose()}> + + + {t("Script Console")} + - - {logInfo.map(([level, log], index) => ( - - - - {log} - - - - ))} + {/* Контейнер для логов с прокруткой */} +
    + {logInfo.length > 0 ? ( + logInfo.map(([level, log], index) => ( +
    +
    + + {level} + + {/* `whitespace-pre-wrap` сохраняет переносы строк и пробелы в логах */} +

    + {log} +

    +
    +
    + )) + ) : ( + + )} +
    - {logInfo.length === 0 && } + + + + +
    - - - -
    ); }; diff --git a/src/components/profile/profile-box.tsx b/src/components/profile/profile-box.tsx index da2edc6c..f11bfb3f 100644 --- a/src/components/profile/profile-box.tsx +++ b/src/components/profile/profile-box.tsx @@ -1,58 +1,41 @@ -import { alpha, Box, styled } from "@mui/material"; +import * as React from "react"; +import { cn } from "@root/lib/utils"; -export const ProfileBox = styled(Box)(({ - theme, - "aria-selected": selected, -}) => { - const { mode, primary, text } = theme.palette; - const key = `${mode}-${!!selected}`; +// Определяем пропсы: принимает все атрибуты для div и булевый пропс `selected` +export interface ProfileBoxProps extends React.HTMLAttributes { + selected?: boolean; +} - const backgroundColor = mode === "light" ? "#ffffff" : "#282A36"; +export const ProfileBox = React.forwardRef( + ({ className, selected, children, ...props }, ref) => { + return ( +
    + {children} +
    + ); + } +); - return { - position: "relative", - display: "block", - cursor: "pointer", - textAlign: "left", - padding: "8px 16px", - boxSizing: "border-box", - backgroundColor, - ...borderSelect, - borderRadius: "8px", - color, - "& h2": { color: h2color }, - }; -}); +ProfileBox.displayName = "ProfileBox"; diff --git a/src/components/profile/profile-item.tsx b/src/components/profile/profile-item.tsx index 6ffcabe0..4004b9e9 100644 --- a/src/components/profile/profile-item.tsx +++ b/src/components/profile/profile-item.tsx @@ -1,42 +1,105 @@ import dayjs from "dayjs"; +import relativeTime from "dayjs/plugin/relativeTime"; import { mutate } from "swr"; -import { useEffect, useState } from "react"; +import React, { useEffect, useState, useCallback } from "react"; import { useLockFn } from "ahooks"; import { useTranslation } from "react-i18next"; import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; -import { - Box, - Typography, - LinearProgress, - IconButton, - keyframes, - MenuItem, - Menu, - CircularProgress, -} from "@mui/material"; -import { RefreshRounded, DragIndicatorRounded } from "@mui/icons-material"; import { useLoadingCache, useSetLoadingCache } from "@/services/states"; import { viewProfile, - readProfileFile, updateProfile, + readProfileFile, saveProfileFile, - getNextUpdateTime, } from "@/services/cmds"; import { showNotice } from "@/services/noticeService"; import { GroupsEditorViewer } from "@/components/profile/groups-editor-viewer"; import { RulesEditorViewer } from "@/components/profile/rules-editor-viewer"; import { EditorViewer } from "@/components/profile/editor-viewer"; -import { ProfileBox } from "./profile-box"; import parseTraffic from "@/utils/parse-traffic"; import { ConfirmViewer } from "@/components/profile/confirm-viewer"; import { open } from "@tauri-apps/plugin-shell"; import { ProxiesEditorViewer } from "./proxies-editor-viewer"; -const round = keyframes` - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -`; +import { cn } from "@root/lib/utils"; + +// --- Компоненты shadcn/ui --- +import { Card } from "@/components/ui/card"; +import { Progress } from "@/components/ui/progress"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuSeparator, + ContextMenuSub, + ContextMenuSubTrigger, + ContextMenuSubContent, + ContextMenuPortal, + ContextMenuTrigger, +} from "@/components/ui/context-menu"; + +// --- Иконки --- +import { + GripVertical, + File as FileIcon, + Globe, + Clock, + AlertTriangle, + Loader2, + Info, + DownloadCloud, + Trash2, + Edit3, + FileText as FileTextIcon, + ExternalLink, + FolderOpen, + ListChecks, + ListFilter, + ListTree, + CheckCircle, +} from "lucide-react"; + +// Активируем плагин для dayjs +dayjs.extend(relativeTime); + +// --- Вспомогательные функции --- +const parseUrl = (url?: string): string | undefined => { + if (!url) return undefined; + try { + const parsed = new URL(url); + return parsed.hostname; + } catch (e) { + return url.length > 25 ? `${url.substring(0, 22)}...` : url; + } +}; + +const parseExpire = (expire?: number | string): string | null => { + if (!expire) return null; + const expireTimestamp = + typeof expire === "string" ? parseInt(expire, 10) : expire; + if (isNaN(expireTimestamp) || expireTimestamp === 0) return null; + const expireDate = dayjs(expireTimestamp * 1000); + if (!expireDate.isValid()) return null; + const now = dayjs(); + if (expireDate.isBefore(now)) return "Expired"; + return `Expires in ${expireDate.fromNow(true)}`; +}; + +type MenuItemAction = { + label: string; + handler: () => void; + icon: React.ElementType; + disabled?: boolean; + isDestructive?: boolean; +}; interface Props { id: string; @@ -59,166 +122,53 @@ export const ProfileItem = (props: Props) => { transform, transition, isDragging, - } = useSortable({ - id: props.id, - }); - + } = useSortable({ id: props.id }); const { t } = useTranslation(); - const [anchorEl, setAnchorEl] = useState(null); - const [position, setPosition] = useState({ left: 0, top: 0 }); + const loadingCache = useLoadingCache(); const setLoadingCache = useSetLoadingCache(); - // 新增状态:是否显示下次更新时间 - const [showNextUpdate, setShowNextUpdate] = useState(false); - const [nextUpdateTime, setNextUpdateTime] = useState(""); + const { + uid, + name = "Profile", + type, + url, + desc, + extra, + updated = 0, + option, + home, + } = itemData; - const { uid, name = "Profile", extra, updated = 0, option } = itemData; - - // 获取下次更新时间的函数 - const fetchNextUpdateTime = useLockFn(async (forceRefresh = false) => { - if ( - itemData.option?.update_interval && - itemData.option.update_interval > 0 - ) { - try { - console.log(`尝试获取配置 ${itemData.uid} 的下次更新时间`); - - // 如果需要强制刷新,先触发Timer.refresh() - if (forceRefresh) { - // 这里可以通过一个新的API来触发刷新,但目前我们依赖patch_profile中的刷新 - console.log(`强制刷新定时器任务`); - } - - const nextUpdate = await getNextUpdateTime(itemData.uid); - console.log(`获取到下次更新时间结果:`, nextUpdate); - - if (nextUpdate) { - const nextUpdateDate = dayjs(nextUpdate * 1000); - const now = dayjs(); - - // 如果已经过期,显示"更新失败" - if (nextUpdateDate.isBefore(now)) { - setNextUpdateTime(t("Last Update failed")); - } else { - // 否则显示剩余时间 - const diffMinutes = nextUpdateDate.diff(now, "minute"); - - if (diffMinutes < 60) { - if (diffMinutes <= 0) { - setNextUpdateTime(`${t("Next Up")} <1m`); - } else { - setNextUpdateTime(`${t("Next Up")} ${diffMinutes}m`); - } - } else { - const hours = Math.floor(diffMinutes / 60); - const mins = diffMinutes % 60; - setNextUpdateTime(`${t("Next Up")} ${hours}h ${mins}m`); - } - } - } else { - console.log(`返回的下次更新时间为空`); - setNextUpdateTime(t("No schedule")); - } - } catch (err) { - console.error(`获取下次更新时间出错:`, err); - setNextUpdateTime(t("Unknown")); - } - } else { - console.log(`该配置未设置更新间隔或间隔为0`); - setNextUpdateTime(t("Auto update disabled")); - } - }); - - // 切换显示模式的函数 - const toggleUpdateTimeDisplay = (e: React.MouseEvent) => { - e.stopPropagation(); - - if (!showNextUpdate) { - fetchNextUpdateTime(); - } - - setShowNextUpdate(!showNextUpdate); - }; - - // 当组件加载或更新间隔变化时更新下次更新时间 - useEffect(() => { - if (showNextUpdate) { - fetchNextUpdateTime(); - } - }, [showNextUpdate, itemData.option?.update_interval, updated]); - - // 订阅定时器更新事件 - useEffect(() => { - // 处理定时器更新事件 - 这个事件专门用于通知定时器变更 - const handleTimerUpdate = (event: any) => { - const updatedUid = event.payload as string; - - // 只有当更新的是当前配置时才刷新显示 - if (updatedUid === itemData.uid && showNextUpdate) { - console.log(`收到定时器更新事件: uid=${updatedUid}`); - setTimeout(() => { - fetchNextUpdateTime(true); - }, 1000); - } - }; - - // 只注册定时器更新事件监听 - window.addEventListener( - "verge://timer-updated", - handleTimerUpdate as EventListener, - ); - - return () => { - // 清理事件监听 - window.removeEventListener( - "verge://timer-updated", - handleTimerUpdate as EventListener, - ); - }; - }, [showNextUpdate, itemData.uid]); - - // local file mode - // remote file mode - // remote file mode - const hasUrl = !!itemData.url; - const hasExtra = !!extra; // only subscription url has extra info - const hasHome = !!itemData.home; // only subscription url has home page + const hasUrl = !!url; + const hasExtra = !!extra; + const hasHome = !!home; const { upload = 0, download = 0, total = 0 } = extra ?? {}; - const from = parseUrl(itemData.url); - const description = itemData.desc; - const expire = parseExpire(extra?.expire); - const progress = Math.min( - Math.round(((download + upload) * 100) / (total + 0.01)) + 1, - 100, - ); + const parsedHostname = parseUrl(url); + const description = desc; + const expireInfo = parseExpire(extra?.expire); + const progress = + total > 0 + ? Math.min(Math.round(((download + upload) * 100) / total), 100) + : 0; + const isLoading = loadingCache[itemData.uid] ?? false; - const loading = loadingCache[itemData.uid] ?? false; - - // interval update fromNow field const [, setRefresh] = useState({}); useEffect(() => { - if (!hasUrl) return; - + if (!updated) return; let timer: any = null; - const handler = () => { const now = Date.now(); const lastUpdate = updated * 1000; - // 大于一天的不管 if (now - lastUpdate >= 24 * 36e5) return; - const wait = now - lastUpdate >= 36e5 ? 30e5 : 5e4; - timer = setTimeout(() => { setRefresh({}); handler(); }, wait); }; - handler(); - return () => { if (timer) clearTimeout(timer); }; @@ -228,57 +178,17 @@ export const ProfileItem = (props: Props) => { const [rulesOpen, setRulesOpen] = useState(false); const [proxiesOpen, setProxiesOpen] = useState(false); const [groupsOpen, setGroupsOpen] = useState(false); - const [mergeOpen, setMergeOpen] = useState(false); - const [scriptOpen, setScriptOpen] = useState(false); const [confirmOpen, setConfirmOpen] = useState(false); - const onOpenHome = () => { - setAnchorEl(null); - open(itemData.home ?? ""); - }; - - const onEditInfo = () => { - setAnchorEl(null); - onEdit(); - }; - - const onEditFile = () => { - setAnchorEl(null); - setFileOpen(true); - }; - - const onEditRules = () => { - setAnchorEl(null); - setRulesOpen(true); - }; - - const onEditProxies = () => { - setAnchorEl(null); - setProxiesOpen(true); - }; - - const onEditGroups = () => { - setAnchorEl(null); - setGroupsOpen(true); - }; - - const onEditMerge = () => { - setAnchorEl(null); - setMergeOpen(true); - }; - - const onEditScript = () => { - setAnchorEl(null); - setScriptOpen(true); - }; - - const onForceSelect = () => { - setAnchorEl(null); - onSelect(true); - }; + const onOpenHome = () => open(home ?? ""); + const onEditInfo = onEdit; + const onEditFile = () => setFileOpen(true); + const onEditRules = () => setRulesOpen(true); + const onEditProxies = () => setProxiesOpen(true); + const onEditGroups = () => setGroupsOpen(true); + const onForceSelect = () => onSelect(true); const onOpenFile = useLockFn(async () => { - setAnchorEl(null); try { await viewProfile(itemData.uid); } catch (err: any) { @@ -286,152 +196,43 @@ export const ProfileItem = (props: Props) => { } }); - /// 0 不使用任何代理 - /// 1 使用订阅好的代理 - /// 2 至少使用一个代理,根据订阅,如果没订阅,默认使用系统代理 - const onUpdate = useLockFn(async (type: 0 | 1 | 2): Promise => { - setAnchorEl(null); + const onUpdate = useLockFn(async (updateType: 0 | 1 | 2): Promise => { setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true })); - - // 根据类型设置初始更新选项 - const option: Partial = {}; - if (type === 0) { - option.with_proxy = false; - option.self_proxy = false; - } else if (type === 2) { + const updateOption: Partial = {}; + if (updateType === 0) { + updateOption.with_proxy = false; + updateOption.self_proxy = false; + } else if (updateType === 2) { if (itemData.option?.self_proxy) { - option.with_proxy = false; - option.self_proxy = true; + updateOption.with_proxy = false; + updateOption.self_proxy = true; } else { - option.with_proxy = true; - option.self_proxy = false; + updateOption.with_proxy = true; + updateOption.self_proxy = false; } } - try { - // 调用后端更新(后端会自动处理回退逻辑) - await updateProfile(itemData.uid, option); - - // 更新成功,刷新列表 + await updateProfile(itemData.uid, updateOption); showNotice("success", t("Update subscription successfully")); mutate("getProfiles"); } catch (err: any) { - // 更新完全失败(包括后端的回退尝试) - // 不需要做处理,后端会通过事件通知系统发送错误 + // Errors handled by global notice listeners } finally { setLoadingCache((cache) => ({ ...cache, [itemData.uid]: false })); } }); - const urlModeMenu = ( - hasHome ? [{ label: "Home", handler: onOpenHome, disabled: false }] : [] - ).concat([ - { label: "Select", handler: onForceSelect, disabled: false }, - { label: "Edit Info", handler: onEditInfo, disabled: false }, - { label: "Edit File", handler: onEditFile, disabled: false }, - { - label: "Edit Rules", - handler: onEditRules, - disabled: !option?.rules, - }, - { - label: "Edit Proxies", - handler: onEditProxies, - disabled: !option?.proxies, - }, - { - label: "Edit Groups", - handler: onEditGroups, - disabled: !option?.groups, - }, - { - label: "Extend Config", - handler: onEditMerge, - disabled: !option?.merge, - }, - { - label: "Extend Script", - handler: onEditScript, - disabled: !option?.script, - }, - { label: "Open File", handler: onOpenFile, disabled: false }, - { label: "Update", handler: () => onUpdate(0), disabled: false }, - { label: "Update via proxy", handler: () => onUpdate(2), disabled: false }, - { - label: "Delete", - handler: () => { - setAnchorEl(null); - setConfirmOpen(true); - }, - disabled: false, - }, - ]); - const fileModeMenu = [ - { label: "Select", handler: onForceSelect, disabled: false }, - { label: "Edit Info", handler: onEditInfo, disabled: false }, - { label: "Edit File", handler: onEditFile, disabled: false }, - { - label: "Edit Rules", - handler: onEditRules, - disabled: !option?.rules, - }, - { - label: "Edit Proxies", - handler: onEditProxies, - disabled: !option?.proxies, - }, - { - label: "Edit Groups", - handler: onEditGroups, - disabled: !option?.groups, - }, - { - label: "Extend Config", - handler: onEditMerge, - disabled: !option?.merge, - }, - { - label: "Extend Script", - handler: onEditScript, - disabled: !option?.script, - }, - { label: "Open File", handler: onOpenFile, disabled: false }, - { - label: "Delete", - handler: () => { - setAnchorEl(null); - setConfirmOpen(true); - }, - disabled: false, - }, - ]; - - const boxStyle = { - height: 26, - display: "flex", - alignItems: "center", - justifyContent: "space-between", - }; - - // 监听自动更新事件 useEffect(() => { const handleUpdateStarted = (event: CustomEvent) => { if (event.detail.uid === itemData.uid) { setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true })); } }; - const handleUpdateCompleted = (event: CustomEvent) => { if (event.detail.uid === itemData.uid) { setLoadingCache((cache) => ({ ...cache, [itemData.uid]: false })); - // 更新完成后刷新显示 - if (showNextUpdate) { - fetchNextUpdateTime(); - } } }; - - // 注册事件监听 window.addEventListener( "profile-update-started", handleUpdateStarted as EventListener, @@ -440,9 +241,7 @@ export const ProfileItem = (props: Props) => { "profile-update-completed", handleUpdateCompleted as EventListener, ); - return () => { - // 清理事件监听 window.removeEventListener( "profile-update-started", handleUpdateStarted as EventListener, @@ -452,328 +251,194 @@ export const ProfileItem = (props: Props) => { handleUpdateCompleted as EventListener, ); }; - }, [itemData.uid, showNextUpdate]); + }, [itemData.uid, setLoadingCache]); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + zIndex: isDragging ? 100 : undefined, + }; + + const homeMenuItem: MenuItemAction[] = hasHome ? [{ label: "Home", handler: onOpenHome, icon: ExternalLink }] : []; + + const mainMenuItems: MenuItemAction[] = [ + { label: "Select", handler: onForceSelect, icon: CheckCircle }, + { label: "Edit Info", handler: onEditInfo, icon: Edit3 }, + { label: "Edit File", handler: onEditFile, icon: FileTextIcon }, + { label: "Open File", handler: onOpenFile, icon: FolderOpen }, + ]; + + const editMenuItems: MenuItemAction[] = [ + { label: "Edit Rules", handler: onEditRules, disabled: !option?.rules, icon: ListChecks }, + { label: "Edit Proxies", handler: onEditProxies, disabled: !option?.proxies, icon: ListFilter }, + { label: "Edit Groups", handler: onEditGroups, disabled: !option?.groups, icon: ListTree }, + ]; + + const deleteMenuItem: MenuItemAction = { label: "Delete", handler: () => setConfirmOpen(true), icon: Trash2, isDestructive: true }; return ( - - { - // 如果正在激活中,阻止重复点击 - if (activating) { - e.preventDefault(); - e.stopPropagation(); - return; - } - onSelect(false); - }} - onContextMenu={(event) => { - const { clientX, clientY } = event; - setPosition({ top: clientY, left: clientX }); - setAnchorEl(event.currentTarget); - event.preventDefault(); - }} - > - {activating && ( - + + + !activating && onSelect(false)} > - - - )} - - - - { - return { color: text.primary }; - }, - ]} - /> - + {activating && ( +
    + +
    + )} - - {name} - -
    - - {/* only if has url can it be updated */} - {hasUrl && ( - { - e.stopPropagation(); - // 如果正在激活或加载中,阻止更新操作 - if (activating || loading) { - return; - } - onUpdate(1); - }} - > - - - )} -
    - {/* the second line show url's info or description */} - - { - <> - {description ? ( - - {description} - - ) : ( - hasUrl && ( - - {from} - - ) - )} - {hasUrl && ( - - +
    +
    +
    - {showNextUpdate - ? nextUpdateTime - : updated > 0 - ? dayjs(updated * 1000).fromNow() - : ""} - - - )} - - } - - {/* the third line show extra info or last updated time */} - {hasExtra ? ( - - - {parseTraffic(upload + download)} / {parseTraffic(total)} - - {expire} - - ) : ( - - {parseExpire(updated)} - - )} - 0 ? 1 : 0 }} - /> - + +
    +

    + {name} +

    +
    +
    + + {type} + +
    +
    + +
    +

    + + {desc || parsedHostname || t("Local File")} +

    +
    +
    + + + {updated > 0 + ? dayjs(updated * 1000).fromNow() + : t("Never")} + + {isLoading && ( + + )} +
    + {expireInfo && ( + + + {expireInfo} + + )} +
    +
    + + + {hasExtra && total > 0 && ( +
    + +
    + + {parseTraffic(download)}↓ / {parseTraffic(upload)}↑ + + {parseTraffic(total)} +
    +
    + )} + + + + e.stopPropagation()}> + {/* Объединяем все части меню */} + {[...homeMenuItem, ...mainMenuItems].map(item => ( + + {t(item.label)} + + ))} + + + {t("Update")} + + onUpdate(0)}>{t("Update")} + onUpdate(2)}>{t("Update via proxy")} + + + + {editMenuItems.map(item => ( + + {t(item.label)} + + ))} + + + {t(deleteMenuItem.label)} + + + + + {/* Модальные окна для редактирования */} + {fileOpen && setFileOpen(false)} initialData={readProfileFile(uid)} language="yaml" schema="clash" onSave={async (p, c) => { await saveProfileFile(uid, c || ""); onSave?.(p, c); }} />} - setAnchorEl(null)} - anchorPosition={position} - anchorReference="anchorPosition" - transitionDuration={225} - MenuListProps={{ sx: { py: 0.5 } }} - onContextMenu={(e) => { - setAnchorEl(null); - e.preventDefault(); - }} - > - {(hasUrl ? urlModeMenu : fileModeMenu).map((item) => ( - { - return { - color: - item.label === "Delete" - ? theme.palette.error.main - : undefined, - }; - }, - ]} - dense - > - {t(item.label)} - - ))} - - {fileOpen && ( - { - await saveProfileFile(uid, curr ?? ""); - onSave && onSave(prev, curr); - }} - onClose={() => setFileOpen(false)} - /> - )} {rulesOpen && ( setRulesOpen(false)} + profileUid={uid} // <-- Был 'uid', стал 'profileUid' + property={option?.rules ?? ""} + groupsUid={option?.groups ?? ""} // <-- Добавлен недостающий пропс + mergeUid={option?.merge ?? ""} // <-- Добавлен недостающий пропс + onSave={onSave} /> )} + {proxiesOpen && ( setProxiesOpen(false)} + profileUid={uid} // <-- Был 'uid', стал 'profileUid' + property={option?.proxies ?? ""} + onSave={onSave} /> )} + {groupsOpen && ( setGroupsOpen(false)} + profileUid={uid} // <-- Был 'uid', стал 'profileUid' property={option?.groups ?? ""} - open={true} + proxiesUid={option?.proxies ?? ""} // <-- Добавлен недостающий пропс + mergeUid={option?.merge ?? ""} // <-- Добавлен недостающий пропс onSave={onSave} - onClose={() => { - setGroupsOpen(false); - }} - /> - )} - {mergeOpen && ( - { - await saveProfileFile(option?.merge ?? "", curr ?? ""); - onSave && onSave(prev, curr); - }} - onClose={() => setMergeOpen(false)} - /> - )} - {scriptOpen && ( - { - await saveProfileFile(option?.script ?? "", curr ?? ""); - onSave && onSave(prev, curr); - }} - onClose={() => setScriptOpen(false)} /> )} setConfirmOpen(false)} - onConfirm={() => { - onDelete(); - setConfirmOpen(false); - }} + onOpenChange={setConfirmOpen} + onConfirm={onDelete} + title={t(`Delete Profile ${name}?`)} + description={t("This action cannot be undone.")} /> -
    + ); }; - -function parseUrl(url?: string) { - if (!url) return ""; - const regex = /https?:\/\/(.+?)\//; - const result = url.match(regex); - return result ? result[1] : "local file"; -} - -function parseExpire(expire?: number) { - if (!expire) return "-"; - return dayjs(expire * 1000).format("YYYY-MM-DD"); -} diff --git a/src/components/profile/profile-more.tsx b/src/components/profile/profile-more.tsx index 8bce9be0..a83fa9bc 100644 --- a/src/components/profile/profile-more.tsx +++ b/src/components/profile/profile-more.tsx @@ -1,21 +1,24 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useLockFn } from "ahooks"; -import { - Box, - Badge, - Chip, - Typography, - MenuItem, - Menu, - IconButton, -} from "@mui/material"; -import { FeaturedPlayListRounded } from "@mui/icons-material"; +import { UnlistenFn } from "@tauri-apps/api/event"; import { viewProfile, readProfileFile, saveProfileFile } from "@/services/cmds"; -import { EditorViewer } from "@/components/profile/editor-viewer"; -import { ProfileBox } from "./profile-box"; -import { LogViewer } from "./log-viewer"; import { showNotice } from "@/services/noticeService"; +import { EditorViewer } from "@/components/profile/editor-viewer"; +import { ProfileBox } from "./profile-box"; // Наш рефакторенный компонент +import { LogViewer } from "./log-viewer"; // Наш рефакторенный компонент + +// Новые импорты +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from "@/components/ui/context-menu"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { ScrollText, FileText, FolderOpen } from "lucide-react"; interface Props { logInfo?: [string, string][]; @@ -23,23 +26,18 @@ interface Props { onSave?: (prev?: string, curr?: string) => void; } -// profile enhanced item export const ProfileMore = (props: Props) => { const { id, logInfo = [], onSave } = props; - const { t } = useTranslation(); - const [anchorEl, setAnchorEl] = useState(null); - const [position, setPosition] = useState({ left: 0, top: 0 }); + const [fileOpen, setFileOpen] = useState(false); const [logOpen, setLogOpen] = useState(false); const onEditFile = () => { - setAnchorEl(null); setFileOpen(true); }; const onOpenFile = useLockFn(async () => { - setAnchorEl(null); try { await viewProfile(id); } catch (err: any) { @@ -47,126 +45,72 @@ export const ProfileMore = (props: Props) => { } }); - const fnWrapper = (fn: () => void) => () => { - setAnchorEl(null); - return fn(); - }; - const hasError = !!logInfo.find((e) => e[0] === "exception"); - const itemMenu = [ - { label: "Edit File", handler: onEditFile }, - { label: "Open File", handler: onOpenFile }, + const menuItems = [ + { label: "Edit File", handler: onEditFile, icon: FileText }, + { label: "Open File", handler: onOpenFile, icon: FolderOpen }, ]; - const boxStyle = { - height: 26, - display: "flex", - alignItems: "center", - justifyContent: "space-between", - lineHeight: 1, - }; - return ( <> - { - const { clientX, clientY } = event; - setPosition({ top: clientY, left: clientX }); - setAnchorEl(event.currentTarget); - event.preventDefault(); - }} - > - - - {t(`Global ${id}`)} - + + + {/* Используем наш готовый ProfileBox */} + + {/* Верхняя строка: Название и Бейдж */} +
    +

    {t(`Global ${id}`)}

    + {id} +
    - -
    + {/* Нижняя строка: Кнопка логов или заглушка для сохранения высоты */} +
    + {id === "Script" && ( + + + + {/* Контейнер для позиционирования точки-индикатора */} +
    + + {/* Точка-индикатор ошибки с анимацией */} + {hasError && ( + + + + + )} +
    +
    + +

    {t("Script Console")}

    +
    +
    +
    + )} +
    +
    + - - {id === "Script" && - (hasError ? ( - - setLogOpen(true)} - > - - - - ) : ( - setLogOpen(true)} - > - - - ))} - -
    - - setAnchorEl(null)} - anchorPosition={position} - anchorReference="anchorPosition" - transitionDuration={225} - MenuListProps={{ sx: { py: 0.5 } }} - onContextMenu={(e) => { - setAnchorEl(null); - e.preventDefault(); - }} - > - {itemMenu - .filter((item: any) => item.show !== false) - .map((item) => ( - { - return { - color: - item.label === "Delete" - ? theme.palette.error.main - : undefined, - }; - }, - ]} - dense - > - {t(item.label)} - + {/* Содержимое контекстного меню */} + + {menuItems.map((item) => ( + + + {t(item.label)} + ))} - + + + + {/* Модальные окна, которые мы уже переделали */} {fileOpen && ( { schema={id === "Merge" ? "clash" : undefined} onSave={async (prev, curr) => { await saveProfileFile(id, curr ?? ""); - onSave && onSave(prev, curr); + onSave?.(prev, curr); }} onClose={() => setFileOpen(false)} /> diff --git a/src/components/profile/profile-viewer.tsx b/src/components/profile/profile-viewer.tsx index d6ecc750..4f4043e1 100644 --- a/src/components/profile/profile-viewer.tsx +++ b/src/components/profile/profile-viewer.tsx @@ -1,29 +1,42 @@ -import { - forwardRef, - useEffect, - useImperativeHandle, - useRef, - useState, -} from "react"; +import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react"; import { useLockFn } from "ahooks"; import { useTranslation } from "react-i18next"; -import { useForm, Controller } from "react-hook-form"; -import { - Box, - FormControl, - InputAdornment, - InputLabel, - MenuItem, - Select, - styled, - TextField, -} from "@mui/material"; +import { useForm } from "react-hook-form"; import { createProfile, patchProfile } from "@/services/cmds"; -import { BaseDialog, Switch } from "@/components/base"; -import { version } from "@root/package.json"; -import { FileInput } from "./file-input"; import { useProfiles } from "@/hooks/use-profiles"; import { showNotice } from "@/services/noticeService"; +import { version } from "@root/package.json"; + +// --- Новые импорты --- +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, + DialogClose, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { Textarea } from "@/components/ui/textarea"; +import { Loader2 } from "lucide-react"; + interface Props { onChange: (isActivating?: boolean) => void; @@ -34,361 +47,253 @@ export interface ProfileViewerRef { edit: (item: IProfileItem) => void; } -// create or edit the profile -// remote / local -export const ProfileViewer = forwardRef( - (props, ref) => { - const { t } = useTranslation(); - const [open, setOpen] = useState(false); - const [openType, setOpenType] = useState<"new" | "edit">("new"); - const [loading, setLoading] = useState(false); - const { profiles } = useProfiles(); +export const ProfileViewer = forwardRef((props, ref) => { + const { t } = useTranslation(); + const [open, setOpen] = useState(false); + const [openType, setOpenType] = useState<"new" | "edit">("new"); + const [loading, setLoading] = useState(false); + const { profiles } = useProfiles(); + const fileDataRef = useRef(null); - // file input - const fileDataRef = useRef(null); - - const { control, watch, register, ...formIns } = useForm({ - defaultValues: { - type: "remote", - name: "", - desc: "", - url: "", - option: { - with_proxy: false, - self_proxy: false, - }, + const form = useForm({ + defaultValues: { + type: "remote", + name: "", + desc: "", + url: "", + option: { + with_proxy: false, + self_proxy: false, + danger_accept_invalid_certs: false, }, - }); + }, + }); - useImperativeHandle(ref, () => ({ - create: () => { - setOpenType("new"); - setOpen(true); - }, - edit: (item) => { - if (item) { - Object.entries(item).forEach(([key, value]) => { - formIns.setValue(key as any, value); - }); - } - setOpenType("edit"); - setOpen(true); - }, - })); + const { control, watch, handleSubmit, reset, setValue } = form; - const selfProxy = watch("option.self_proxy"); - const withProxy = watch("option.with_proxy"); + useImperativeHandle(ref, () => ({ + create: () => { + reset({ type: "remote", name: "", desc: "", url: "", option: { with_proxy: false, self_proxy: false, danger_accept_invalid_certs: false } }); + fileDataRef.current = null; + setOpenType("new"); + setOpen(true); + }, + edit: (item) => { + reset(item); + fileDataRef.current = null; + setOpenType("edit"); + setOpen(true); + }, + })); - useEffect(() => { - if (selfProxy) formIns.setValue("option.with_proxy", false); - }, [selfProxy]); + const selfProxy = watch("option.self_proxy"); + const withProxy = watch("option.with_proxy"); - useEffect(() => { - if (withProxy) formIns.setValue("option.self_proxy", false); - }, [withProxy]); + useEffect(() => { + if (selfProxy) setValue("option.with_proxy", false); + }, [selfProxy, setValue]); - const handleOk = useLockFn( - formIns.handleSubmit(async (form) => { - if (form.option?.timeout_seconds) { - form.option.timeout_seconds = +form.option.timeout_seconds; + useEffect(() => { + if (withProxy) setValue("option.self_proxy", false); + }, [withProxy, setValue]); + + const handleOk = useLockFn( + handleSubmit(async (form) => { + if (form.option?.timeout_seconds) { + form.option.timeout_seconds = +form.option.timeout_seconds; + } + + setLoading(true); + try { + if (!form.type) throw new Error("`Type` should not be null"); + if (form.type === "remote" && !form.url) { + throw new Error("The URL should not be null"); } - setLoading(true); - try { - // 基本验证 - if (!form.type) throw new Error("`Type` should not be null"); - if (form.type === "remote" && !form.url) { - throw new Error("The URL should not be null"); - } + if (form.option?.update_interval) { + form.option.update_interval = +form.option.update_interval; + } else { + delete form.option?.update_interval; + } + if (form.option?.user_agent === "") { + delete form.option.user_agent; + } - // 处理表单数据 - if (form.option?.update_interval) { - form.option.update_interval = +form.option.update_interval; + const name = form.name || `${form.type} file`; + const item = { ...form, name }; + const isRemote = form.type === "remote"; + const isUpdate = openType === "edit"; + const isActivating = isUpdate && form.uid === (profiles?.current ?? ""); + const originalOptions = { with_proxy: form.option?.with_proxy, self_proxy: form.option?.self_proxy }; + + if (!isRemote) { + if (openType === "new") { + await createProfile(item, fileDataRef.current); } else { - delete form.option?.update_interval; + if (!form.uid) throw new Error("UID not found"); + await patchProfile(form.uid, item); } - if (form.option?.user_agent === "") { - delete form.option.user_agent; - } - - const name = form.name || `${form.type} file`; - const item = { ...form, name }; - const isRemote = form.type === "remote"; - const isUpdate = openType === "edit"; - - // 判断是否是当前激活的配置 - const isActivating = - isUpdate && form.uid === (profiles?.current ?? ""); - - // 保存原始代理设置以便回退成功后恢复 - const originalOptions = { - with_proxy: form.option?.with_proxy, - self_proxy: form.option?.self_proxy, - }; - - // 执行创建或更新操作,本地配置不需要回退机制 - if (!isRemote) { + } else { + try { if (openType === "new") { await createProfile(item, fileDataRef.current); } else { if (!form.uid) throw new Error("UID not found"); await patchProfile(form.uid, item); } - } else { - // 远程配置使用回退机制 - try { - // 尝试正常操作 - if (openType === "new") { - await createProfile(item, fileDataRef.current); - } else { - if (!form.uid) throw new Error("UID not found"); - await patchProfile(form.uid, item); - } - } catch (err) { - // 首次创建/更新失败,尝试使用自身代理 - showNotice( - "info", - t("Profile creation failed, retrying with Clash proxy..."), - ); - - // 使用自身代理的配置 - const retryItem = { - ...item, - option: { - ...item.option, - with_proxy: false, - self_proxy: true, - }, - }; - - // 使用自身代理再次尝试 - if (openType === "new") { - await createProfile(retryItem, fileDataRef.current); - } else { - if (!form.uid) throw new Error("UID not found"); - await patchProfile(form.uid, retryItem); - - // 编辑模式下恢复原始代理设置 - await patchProfile(form.uid, { option: originalOptions }); - } - - showNotice( - "success", - t("Profile creation succeeded with Clash proxy"), - ); + } catch (err) { + showNotice("info", t("Profile creation failed, retrying with Clash proxy...")); + const retryItem = { ...item, option: { ...item.option, with_proxy: false, self_proxy: true } }; + if (openType === "new") { + await createProfile(retryItem, fileDataRef.current); + } else { + if (!form.uid) throw new Error("UID not found"); + await patchProfile(form.uid, retryItem); + await patchProfile(form.uid, { option: originalOptions }); } + showNotice("success", t("Profile creation succeeded with Clash proxy")); } - - // 成功后的操作 - setOpen(false); - setTimeout(() => formIns.reset(), 500); - fileDataRef.current = null; - - // 优化:UI先关闭,异步通知父组件 - setTimeout(() => { - props.onChange(isActivating); - }, 0); - } catch (err: any) { - showNotice("error", err.message || err.toString()); - } finally { - setLoading(false); } - }), - ); - const handleClose = () => { - try { setOpen(false); - fileDataRef.current = null; - setTimeout(() => formIns.reset(), 500); - } catch {} - }; + props.onChange(isActivating); + } catch (err: any) { + showNotice("error", err.message || err.toString()); + } finally { + setLoading(false); + } + }), + ); - const text = { - fullWidth: true, - size: "small", - margin: "normal", - variant: "outlined", - autoComplete: "off", - autoCorrect: "off", - } as const; + const formType = watch("type"); + const isRemote = formType === "remote"; + const isLocal = formType === "local"; - const formType = watch("type"); - const isRemote = formType === "remote"; - const isLocal = formType === "local"; + return ( + + + + {openType === "new" ? t("Create Profile") : t("Edit Profile")} + - return ( - - ( - - {t("Type")} - - - )} - /> +
    + { e.preventDefault(); handleOk(); }} className="space-y-4 max-h-[70vh] overflow-y-auto px-1"> + ( + + {t("Type")} + + + + )}/> - ( - - )} - /> + ( + + {t("Name")} + + + + )}/> - ( - - )} - /> + ( + + {t("Descriptions")} + + + + )}/> - {isRemote && ( - <> - ( - - )} - /> - - ( - - )} - /> - - ( - - {t("seconds")} - - ), - }, - }} - /> - )} - /> - - )} - - {(isRemote || isLocal) && ( - ( - - {t("mins")} - - ), - }, - }} - /> + {isRemote && ( + <> + ( + + {t("Subscription URL")} +