diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bc7242..be91d52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,10 @@
### Features
+- feat: |Frontend Next| 新增 `frontend-next` Vite React + shadcn 命令生成组件的非 admin 邮件客户端,按参考稿 B 风格实现创建/恢复地址、收件箱、写信、地址管理、用户账号集成、设置与亮色/暗色切换
+
+- docs: |前端/文档| 统一邮箱地址、用户账号与 Admin 权限的表述,并明确发信权限和额度按邮箱地址独立申请与管理
+
### Bug Fixes
### Improvements
diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md
index 1c91ec0..a4c5543 100644
--- a/CHANGELOG_EN.md
+++ b/CHANGELOG_EN.md
@@ -10,6 +10,10 @@
### Features
+- feat: |Frontend Next| Add a `frontend-next` Vite React non-admin mail client using shadcn CLI-generated components in the reference B style, covering address create/restore, inbox, compose, address management, user account integration, settings, and light/dark theme support
+
+- docs: |Frontend/Docs| Clarify mailbox address, user account, and Admin permission terminology, including address-specific send access and balances
+
### Bug Fixes
### Improvements
diff --git a/frontend-next/.gitignore b/frontend-next/.gitignore
new file mode 100644
index 0000000..21bacb3
--- /dev/null
+++ b/frontend-next/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+dist
+.DS_Store
+.env
+.env.*
+!.env.example
diff --git a/frontend-next/components.json b/frontend-next/components.json
new file mode 100644
index 0000000..0b64de1
--- /dev/null
+++ b/frontend-next/components.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/index.css",
+ "baseColor": "zinc",
+ "cssVariables": true
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/frontend-next/index.html b/frontend-next/index.html
new file mode 100644
index 0000000..fdac733
--- /dev/null
+++ b/frontend-next/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Temp Email Next
+
+
+
+
+
+
diff --git a/frontend-next/package.json b/frontend-next/package.json
new file mode 100644
index 0000000..6f0dfa9
--- /dev/null
+++ b/frontend-next/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "cloudflare-temp-email-next",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@tailwindcss/vite": "^4.1.17",
+ "@vitejs/plugin-react": "^5.1.2",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^0.561.0",
+ "next-themes": "^0.4.6",
+ "radix-ui": "^1.6.0",
+ "react": "^19.2.3",
+ "react-dom": "^19.2.3",
+ "sonner": "^2.0.7",
+ "tailwind-merge": "^3.4.0",
+ "tailwindcss": "^4.1.17",
+ "typescript": "^5.9.3",
+ "vite": "^7.3.5"
+ },
+ "devDependencies": {
+ "@types/node": "^26.0.1",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3"
+ },
+ "packageManager": "pnpm@10.10.0"
+}
diff --git a/frontend-next/pnpm-lock.yaml b/frontend-next/pnpm-lock.yaml
new file mode 100644
index 0000000..4407a85
--- /dev/null
+++ b/frontend-next/pnpm-lock.yaml
@@ -0,0 +1,3194 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@tailwindcss/vite':
+ specifier: ^4.1.17
+ version: 4.3.1(vite@7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0))
+ '@vitejs/plugin-react':
+ specifier: ^5.1.2
+ version: 5.2.0(vite@7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0))
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ lucide-react:
+ specifier: ^0.561.0
+ version: 0.561.0(react@19.2.7)
+ next-themes:
+ specifier: ^0.4.6
+ version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ radix-ui:
+ specifier: ^1.6.0
+ version: 1.6.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react:
+ specifier: ^19.2.3
+ version: 19.2.7
+ react-dom:
+ specifier: ^19.2.3
+ version: 19.2.7(react@19.2.7)
+ sonner:
+ specifier: ^2.0.7
+ version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ tailwind-merge:
+ specifier: ^3.4.0
+ version: 3.6.0
+ tailwindcss:
+ specifier: ^4.1.17
+ version: 4.3.1
+ typescript:
+ specifier: ^5.9.3
+ version: 5.9.3
+ vite:
+ specifier: ^7.3.5
+ version: 7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)
+ devDependencies:
+ '@types/node':
+ specifier: ^26.0.1
+ version: 26.0.1
+ '@types/react':
+ specifier: ^19.2.7
+ version: 19.2.17
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3(@types/react@19.2.17)
+
+packages:
+
+ '@babel/code-frame@7.29.7':
+ resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.29.7':
+ resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.29.7':
+ resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.29.7':
+ resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.29.7':
+ resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.29.7':
+ resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.29.7':
+ resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.29.7':
+ resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.29.7':
+ resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.29.7':
+ resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.29.7':
+ resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.29.7':
+ resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.29.7':
+ resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.7':
+ resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-react-jsx-self@7.29.7':
+ resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.29.7':
+ resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.29.7':
+ resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.29.7':
+ resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.29.7':
+ resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
+ engines: {node: '>=6.9.0'}
+
+ '@esbuild/aix-ppc64@0.28.1':
+ resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.28.1':
+ resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.28.1':
+ resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.28.1':
+ resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.28.1':
+ resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.28.1':
+ resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.28.1':
+ resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.28.1':
+ resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.28.1':
+ resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.28.1':
+ resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.28.1':
+ resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.28.1':
+ resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.28.1':
+ resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.28.1':
+ resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.28.1':
+ resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.28.1':
+ resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.28.1':
+ resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.28.1':
+ resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.28.1':
+ resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.28.1':
+ resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.28.1':
+ resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.28.1':
+ resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.28.1':
+ resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.28.1':
+ resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.28.1':
+ resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.28.1':
+ resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@floating-ui/core@1.7.5':
+ resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
+
+ '@floating-ui/dom@1.7.6':
+ resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
+
+ '@floating-ui/react-dom@2.1.8':
+ resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/utils@0.2.11':
+ resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@radix-ui/number@1.1.2':
+ resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==}
+
+ '@radix-ui/primitive@1.1.4':
+ resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==}
+
+ '@radix-ui/react-accessible-icon@1.1.10':
+ resolution: {integrity: sha512-TraSwZUqTcVbiDV2/RXzAXC7aeVVXchq0daPFZE7zAxYFaMzjOUggLOfQH9KFLgRizuwVKZO/crveV1eeO3/ZQ==}
+ 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-accordion@1.2.14':
+ resolution: {integrity: sha512-iE8YB9nmTBH8zd73ofBISZ8JCzgMoMkATJr7qDwa6u5F1+7mTM81V6fa71jgZ65rpjVpecDf1vSnwIFP9Ly1zw==}
+ 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-alert-dialog@1.1.17':
+ resolution: {integrity: sha512-563ygGeyWPrxyVCNp7OV4rE2aIXhFPknpFyo4wbDlcyMMPZ6ySh+zC5WTvY0ZFLgPTg/QB6tA8PyDQyJ2b4cPg==}
+ 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.10':
+ resolution: {integrity: sha512-j2VTDz1vgCsmuG0k5lBfOcM8n5JPFqZBcMryasFjHYMhwxYL5SRUV5lMSUpRdNtw3D/Sv8pzJtrlAgkssYSsQQ==}
+ 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-aspect-ratio@1.1.10':
+ resolution: {integrity: sha512-kbI7NrqhDeuytYrq7JjAsoXczvL8wgj2tc1MyaYWm+50bMKHCHQtVWCryslx4cCpmCTTkBcwQckE4CmmGV2haQ==}
+ 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-avatar@1.2.0':
+ resolution: {integrity: sha512-am/CwltXtmtdtP+5FbYblYDnMa/zuKcMJP1i3/SJMDXXfj2mG+BTqLH2wucqeyyiQMursUtg/5cK+Nh2pCaSOA==}
+ 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-checkbox@1.3.5':
+ resolution: {integrity: sha512-pREzrmNnVwGvYaBoM64huTRK7B3lrTRuwj8A9nwhPiEtMb+yudiWh6zWAqEtP0Dzd5+iBa1Ki7V1pCxV8ExMdA==}
+ 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-collapsible@1.1.14':
+ resolution: {integrity: sha512-9bT+FvifX1FK2Mj6UEsTdyu0cN3JaA3KdfhaBao+ONrYFy/pyOy3TU1TNw7iOk1o+0hOEq67RojlUUmoFGwxyA==}
+ 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.10':
+ resolution: {integrity: sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g==}
+ 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.3':
+ resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==}
+ 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.3.1':
+ resolution: {integrity: sha512-XbrxS68W5dyiE4fAb96yvJwSVU5x66B20A99sD5Mk3xSWK/LqeOnx6TZnim1KieMjXS/CTFq8reOAjWxas2G8Q==}
+ 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.4':
+ resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==}
+ 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.17':
+ resolution: {integrity: sha512-TDTYmpdq8dI2+Xgvgj9AJ8Ghqq+Eph/TRVEdaFQPDItIY+6QSkU7MJMeevw1568Yw/2Ijz8BTphPSP2XejKphw==}
+ 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.2':
+ resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==}
+ 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.13':
+ resolution: {integrity: sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==}
+ 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.18':
+ resolution: {integrity: sha512-PZGV82gFk0WltDRI//SsG28ZIjlo9ANTmoNYg0jLNzXXiDsAy5PkOOYQaVD1pPxY6t7gxffb1QMD6qaUvsBZdw==}
+ 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.4':
+ resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==}
+ 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.10':
+ resolution: {integrity: sha512-Fas/lXQqhVvqwAb64s5RFeHiHYElZ6SUQbZaNd6EkfhP/Al7wTIQ9WIR4QVX475tlu5yFCEdDcJH6/UwsZjMWw==}
+ 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-form@0.1.10':
+ resolution: {integrity: sha512-1NfuvctVtX4sU3Mmq/IdrR8UunxiCMiVg3A5UENKhFzxUBeOyaQQ+lmaQaV7Tc8cqvBKsJL3/KGBsixK0D8WFg==}
+ 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-hover-card@1.1.17':
+ resolution: {integrity: sha512-GjZQIEANVkuuWeztlKz6QEHe31ZX2iDfHzcTMCQVZXC0JyQrgfKWSC+LOOEw6aVV64zyjzobIzSA4AU4eKWrHA==}
+ 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-id@1.1.2':
+ resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==}
+ 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.10':
+ resolution: {integrity: sha512-ib0zvq2ZsAqKm5tRnqGJn3vOxSgIts5ToxsXT0q1S/GfLD1Zj7UOEnkw8u2w6sRmn47djpQWuSU1DCL1R29/yw==}
+ 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.18':
+ resolution: {integrity: sha512-lj8Rxjtn6zJq1oSbE/uDtAwCbB9BnxgHD+8MwJMuTh6u1dPamYhW9iuELr/Z8d0D/UysFblYYHeBPwi7T4k0YQ==}
+ 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-menubar@1.1.18':
+ resolution: {integrity: sha512-hX7EGx/oFq6DPY27GQuP/2wP48GHf5LG6r06VgNJlG+znmDS8OfopZcRcGly3L4lsB9FqpmLx6JQSE9P3BUpyw==}
+ 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-navigation-menu@1.2.16':
+ resolution: {integrity: sha512-nJ0SkrSQgudyYhMiYeHA1ayLVuduEJCFLan1RZZN7c9kqzzCFLaU9kuy81uNtqzweM9YaQPgWzxi9MwQ9jZ04g==}
+ 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-one-time-password-field@0.1.10':
+ resolution: {integrity: sha512-GHkcJ+WVj91At+OvUVTD4R3W0/wxw9t/sG5xFUBYXaCbtWiooZX5Md376QjJqgH4VsVyXrbVNHO2O4NYcmjfVg==}
+ 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-password-toggle-field@0.1.5':
+ resolution: {integrity: sha512-fVuA82u0b/fClpbEJv8yp1nU9eSvoSEOERsU/hhf3FXGPIvkmE7oEaHEu8poowoXO39/Va7zq2E0TUcYr1dBRg==}
+ 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.17':
+ resolution: {integrity: sha512-/YSAOdJ7YJvdn7bn5sdSx2egW+SKY+u7O5RyAVs94Ymrg2fg5QTSFPMRkzvhGyFuE4/qsmPBdrwYoZMZh/4f+g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-popper@1.3.1':
+ resolution: {integrity: sha512-bhnq/0DEPTi2lsOD3J5rTL65qUKHbKbhqHsmN9TMiclSXpipi651ooUKPPp6G5lF/WiHBdn1s0Wuqsn+myVAvw==}
+ 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.12':
+ resolution: {integrity: sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==}
+ 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.6':
+ resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==}
+ 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.6':
+ resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==}
+ 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.10':
+ resolution: {integrity: sha512-JYzEg60lk79PwKM27WZyKd7PW8O4OM5jOaFfRPfOyeXmMw7tLJh5kSj+CEjVTehszuwml/AdCzPGMXBTGf4BBw==}
+ 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-radio-group@1.4.1':
+ resolution: {integrity: sha512-/SSxZdKEo2Eo29FFRKd06EfFDYp8HryKg0WYg7QLXaydPzl52YfSvCH2a3QDBRdtcuwACroJT8UVjQVgOJ7P9A==}
+ 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.13':
+ resolution: {integrity: sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw==}
+ 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-scroll-area@1.2.12':
+ resolution: {integrity: sha512-xuafVzQiTCLsyEjakowTdG3OgTXsmO7IdCiO77otIa+z44xoLNs9Do5eg7POFumIOCjtG6djfm6RKUKpUa/csA==}
+ 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.3.1':
+ resolution: {integrity: sha512-w6eDvY78LE9ZUiNnXCA1QVK8RYN7k9galFv09kjVydJqBAgHd7Y9A6h0UJ/6DCZNGZMZrB2ohcSW1Bo9d8+wWA==}
+ 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.10':
+ resolution: {integrity: sha512-Y6K6jLQCVfCnTL2MEtGxDLffkhNfEfHsEg3Wa8JU+IWdn3EWbLXd3OuOfQRN7p/W/cUce1WyTk3QeuAoDBzN9g==}
+ 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-slider@1.4.1':
+ resolution: {integrity: sha512-r91WSpQucNGFKAIxT8FT0H0zyjd5tJlqObLp7LOMV4z49KoDCwjy01w3vDOU4e1wxhF9IgjYco7SB6byOW7Buw==}
+ 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.3.0':
+ resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==}
+ 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.3.1':
+ resolution: {integrity: sha512-55bQtCnOB0BohomSHi6qvQXpJEEqUGDm6hRrM0Bph5OXwhSegqkd8IqgBAQkM1IlgUlWZIxpxRcpOEfRIgimyw==}
+ 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-tabs@1.1.15':
+ resolution: {integrity: sha512-kxc9gI6/HfcU4nfMMVS3AmQK414kbU1IE6UCJmMmxjhO3cRPXOyYnmvyKD+ODt7q56nRq9l7Wovi6uaGwKgMlg==}
+ 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-toast@1.2.17':
+ resolution: {integrity: sha512-uL4kyyWy000pPL43fGGCV5qT6ZchCWEQZOSlkYiPwPt8Hy1iW38RjeptIvz1/SZesrW6Vn58Ct3sV7tfEfiAbw==}
+ 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-toggle-group@1.1.13':
+ resolution: {integrity: sha512-Xb9PLtlvU66F36LiKba6dFswu6V2mDkgidO4fNSbQHQwmZ9ObxMIO17MN/LJ4aWJecVuSVLAHPZjyeMzJrgeiA==}
+ 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-toggle@1.1.12':
+ resolution: {integrity: sha512-AsAVsYNZIlRBsci7BhE+QyQeKd1h6TffJYt+lF0QQkd5OpQ3klfIByPsCb4G0h/Fq6PJwh1FYNluzBFYzhk4+w==}
+ 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-toolbar@1.1.13':
+ resolution: {integrity: sha512-Za1l4f6fzTkGgz/iynAMN8iaqiKff2wm2/QwiLmHPtDQreWEBrvSimgQFIekxMUdRPhILM7xdIXxuS/o/DGZag==}
+ 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.10':
+ resolution: {integrity: sha512-NlNe8D0dWEpVfXFli90IO6X07Josx/b1iu98tDnx9Xv0HT4wLIL+m2VOheMHhK7qbp2HoTBqALEFzGyZs/levw==}
+ 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.2':
+ resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==}
+ 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.3':
+ resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==}
+ 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.3':
+ resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==}
+ 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.2':
+ resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==}
+ 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-is-hydrated@0.1.1':
+ resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==}
+ 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.2':
+ resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==}
+ 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.2':
+ resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==}
+ 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.2':
+ resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==}
+ 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.2':
+ resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==}
+ 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.6':
+ resolution: {integrity: sha512-jCE0WljWifTI4niIMCll06kGpsJTAPiZVU9H4WR1N6qW7At9ystHbN7dDB+we2xH535roFHj7qKS+RGj0FMDWQ==}
+ 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.2':
+ resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==}
+
+ '@rolldown/pluginutils@1.0.0-rc.3':
+ resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==}
+
+ '@rollup/rollup-android-arm-eabi@4.62.2':
+ resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.62.2':
+ resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.62.2':
+ resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.62.2':
+ resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.62.2':
+ resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.62.2':
+ resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.62.2':
+ resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.62.2':
+ resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.62.2':
+ resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.62.2':
+ resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.62.2':
+ resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-musl@4.62.2':
+ resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.62.2':
+ resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-musl@4.62.2':
+ resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.62.2':
+ resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.62.2':
+ resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.62.2':
+ resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.62.2':
+ resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.62.2':
+ resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openbsd-x64@4.62.2':
+ resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@rollup/rollup-openharmony-arm64@4.62.2':
+ resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.62.2':
+ resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.62.2':
+ resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.62.2':
+ resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.62.2':
+ resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/node@4.3.1':
+ resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==}
+
+ '@tailwindcss/oxide-android-arm64@4.3.1':
+ resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.1':
+ resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.3.1':
+ resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.1':
+ resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1':
+ resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==}
+ engines: {node: '>= 20'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.1':
+ resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.1':
+ resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.1':
+ resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.1':
+ resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.1':
+ resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==}
+ 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.3.1':
+ resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.1':
+ resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.3.1':
+ resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==}
+ engines: {node: '>= 20'}
+
+ '@tailwindcss/vite@4.3.1':
+ resolution: {integrity: sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7 || ^8
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+ '@types/node@26.0.1':
+ resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==}
+
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.17':
+ resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}
+
+ '@vitejs/plugin-react@5.2.0':
+ resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ baseline-browser-mapping@2.10.40:
+ resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ browserslist@4.28.4:
+ resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ caniuse-lite@1.0.30001799:
+ resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==}
+
+ class-variance-authority@0.7.1:
+ resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ detect-node-es@1.1.0:
+ resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+
+ electron-to-chromium@1.5.379:
+ resolution: {integrity: sha512-v/qV5aV5EUA2pGilzUCq5/eyOloZAqDZBu9UMBIzgPpLlprjSR6zswsWBTv0KpqxLGUAZEwhO95ZCt7srymNVA==}
+
+ enhanced-resolve@5.21.6:
+ resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==}
+ engines: {node: '>=10.13.0'}
+
+ esbuild@0.28.1:
+ resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-nonce@1.0.1:
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ jiti@2.7.0:
+ resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ lightningcss-android-arm64@1.32.0:
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.32.0:
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.32.0:
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.32.0:
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.32.0:
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.32.0:
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
+ engines: {node: '>= 12.0.0'}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lucide-react@0.561.0:
+ resolution: {integrity: sha512-Y59gMY38tl4/i0qewcqohPdEbieBy7SovpBL9IFebhc2mDd8x4PZSOsiFRkpPcOq6bj1r/mjH/Rk73gSlIJP2A==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.15:
+ resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ 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
+
+ node-releases@2.0.50:
+ resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==}
+ engines: {node: '>=18'}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.4:
+ resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.15:
+ resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ radix-ui@1.6.0:
+ resolution: {integrity: sha512-EUEC70O03EgxWMP5aoqfBZ6iLC5bczFagGy7zhSYRt8o5DP7IWNiP3ywetse3L9b8843ExB0OGWZvgbYVJuNeg==}
+ 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
+
+ react-dom@19.2.7:
+ resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==}
+ peerDependencies:
+ react: ^19.2.7
+
+ react-refresh@0.18.0:
+ resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
+ 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.2:
+ resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==}
+ 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-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@19.2.7:
+ resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}
+ engines: {node: '>=0.10.0'}
+
+ rollup@4.62.2:
+ resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ sonner@2.0.7:
+ resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
+ 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-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ tailwind-merge@3.6.0:
+ resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==}
+
+ tailwindcss@4.3.1:
+ resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==}
+
+ tapable@2.3.3:
+ resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
+ engines: {node: '>=6'}
+
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@8.3.0:
+ resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ 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
+
+ vite@7.3.6:
+ resolution: {integrity: sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+snapshots:
+
+ '@babel/code-frame@7.29.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.29.7
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.29.7': {}
+
+ '@babel/core@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/helper-compilation-targets': 7.29.7
+ '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
+ '@babel/helpers': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/template': 7.29.7
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.29.7':
+ dependencies:
+ '@babel/parser': 7.29.7
+ '@babel/types': 7.29.7
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.29.7':
+ dependencies:
+ '@babel/compat-data': 7.29.7
+ '@babel/helper-validator-option': 7.29.7
+ browserslist: 4.28.4
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.29.7': {}
+
+ '@babel/helper-module-imports@7.29.7':
+ dependencies:
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/helper-module-imports': 7.29.7
+ '@babel/helper-validator-identifier': 7.29.7
+ '@babel/traverse': 7.29.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.29.7': {}
+
+ '@babel/helper-string-parser@7.29.7': {}
+
+ '@babel/helper-validator-identifier@7.29.7': {}
+
+ '@babel/helper-validator-option@7.29.7': {}
+
+ '@babel/helpers@7.29.7':
+ dependencies:
+ '@babel/template': 7.29.7
+ '@babel/types': 7.29.7
+
+ '@babel/parser@7.29.7':
+ dependencies:
+ '@babel/types': 7.29.7
+
+ '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/helper-plugin-utils': 7.29.7
+
+ '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/helper-plugin-utils': 7.29.7
+
+ '@babel/template@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/types': 7.29.7
+
+ '@babel/traverse@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/helper-globals': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/template': 7.29.7
+ '@babel/types': 7.29.7
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.29.7':
+ dependencies:
+ '@babel/helper-string-parser': 7.29.7
+ '@babel/helper-validator-identifier': 7.29.7
+
+ '@esbuild/aix-ppc64@0.28.1':
+ optional: true
+
+ '@esbuild/android-arm64@0.28.1':
+ optional: true
+
+ '@esbuild/android-arm@0.28.1':
+ optional: true
+
+ '@esbuild/android-x64@0.28.1':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.28.1':
+ optional: true
+
+ '@esbuild/darwin-x64@0.28.1':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.28.1':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.28.1':
+ optional: true
+
+ '@esbuild/linux-arm64@0.28.1':
+ optional: true
+
+ '@esbuild/linux-arm@0.28.1':
+ optional: true
+
+ '@esbuild/linux-ia32@0.28.1':
+ optional: true
+
+ '@esbuild/linux-loong64@0.28.1':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.28.1':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.28.1':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.28.1':
+ optional: true
+
+ '@esbuild/linux-s390x@0.28.1':
+ optional: true
+
+ '@esbuild/linux-x64@0.28.1':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.28.1':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.28.1':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.28.1':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.28.1':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.28.1':
+ optional: true
+
+ '@esbuild/sunos-x64@0.28.1':
+ optional: true
+
+ '@esbuild/win32-arm64@0.28.1':
+ optional: true
+
+ '@esbuild/win32-ia32@0.28.1':
+ optional: true
+
+ '@esbuild/win32-x64@0.28.1':
+ optional: true
+
+ '@floating-ui/core@1.7.5':
+ dependencies:
+ '@floating-ui/utils': 0.2.11
+
+ '@floating-ui/dom@1.7.6':
+ dependencies:
+ '@floating-ui/core': 1.7.5
+ '@floating-ui/utils': 0.2.11
+
+ '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@floating-ui/dom': 1.7.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+
+ '@floating-ui/utils@0.2.11': {}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@radix-ui/number@1.1.2': {}
+
+ '@radix-ui/primitive@1.1.4': {}
+
+ '@radix-ui/react-accessible-icon@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-accordion@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collapsible': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-alert-dialog@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-arrow@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-aspect-ratio@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-avatar@1.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-checkbox@1.3.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-collapsible@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-collection@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-context-menu@2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-dialog@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-dismissable-layer@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-dropdown-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-focus-scope@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-form@0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-label': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-hover-card@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-label@2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-menubar@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-navigation-menu@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-one-time-password-field@0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-password-toggle-field@0.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-popover@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-popper@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/rect': 1.1.2
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-portal@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-progress@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-radio-group@1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-roving-focus@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-scroll-area@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-select@2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-separator@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-slider@1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-switch@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-tabs@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-toast@1.2.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-toggle-group@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toggle': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-toggle@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-toolbar@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-separator': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toggle-group': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-tooltip@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/rect': 1.1.2
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-visually-hidden@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/rect@1.1.2': {}
+
+ '@rolldown/pluginutils@1.0.0-rc.3': {}
+
+ '@rollup/rollup-android-arm-eabi@4.62.2':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.62.2':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.62.2':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.62.2':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.62.2':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-musl@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-musl@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.62.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.62.2':
+ optional: true
+
+ '@rollup/rollup-openbsd-x64@4.62.2':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.62.2':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.62.2':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.62.2':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.62.2':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.62.2':
+ optional: true
+
+ '@tailwindcss/node@4.3.1':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.21.6
+ jiti: 2.7.0
+ lightningcss: 1.32.0
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.3.1
+
+ '@tailwindcss/oxide-android-arm64@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.1':
+ optional: true
+
+ '@tailwindcss/oxide@4.3.1':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.3.1
+ '@tailwindcss/oxide-darwin-arm64': 4.3.1
+ '@tailwindcss/oxide-darwin-x64': 4.3.1
+ '@tailwindcss/oxide-freebsd-x64': 4.3.1
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1
+ '@tailwindcss/oxide-linux-arm64-musl': 4.3.1
+ '@tailwindcss/oxide-linux-x64-gnu': 4.3.1
+ '@tailwindcss/oxide-linux-x64-musl': 4.3.1
+ '@tailwindcss/oxide-wasm32-wasi': 4.3.1
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1
+ '@tailwindcss/oxide-win32-x64-msvc': 4.3.1
+
+ '@tailwindcss/vite@4.3.1(vite@7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0))':
+ dependencies:
+ '@tailwindcss/node': 4.3.1
+ '@tailwindcss/oxide': 4.3.1
+ tailwindcss: 4.3.1
+ vite: 7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.29.7
+ '@babel/types': 7.29.7
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.29.7
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.29.7
+ '@babel/types': 7.29.7
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.29.7
+
+ '@types/estree@1.0.9': {}
+
+ '@types/node@26.0.1':
+ dependencies:
+ undici-types: 8.3.0
+
+ '@types/react-dom@19.2.3(@types/react@19.2.17)':
+ dependencies:
+ '@types/react': 19.2.17
+
+ '@types/react@19.2.17':
+ dependencies:
+ csstype: 3.2.3
+
+ '@vitejs/plugin-react@5.2.0(vite@7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0))':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7)
+ '@rolldown/pluginutils': 1.0.0-rc.3
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.18.0
+ vite: 7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ baseline-browser-mapping@2.10.40: {}
+
+ browserslist@4.28.4:
+ dependencies:
+ baseline-browser-mapping: 2.10.40
+ caniuse-lite: 1.0.30001799
+ electron-to-chromium: 1.5.379
+ node-releases: 2.0.50
+ update-browserslist-db: 1.2.3(browserslist@4.28.4)
+
+ caniuse-lite@1.0.30001799: {}
+
+ class-variance-authority@0.7.1:
+ dependencies:
+ clsx: 2.1.1
+
+ clsx@2.1.1: {}
+
+ convert-source-map@2.0.0: {}
+
+ csstype@3.2.3: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ detect-libc@2.1.2: {}
+
+ detect-node-es@1.1.0: {}
+
+ electron-to-chromium@1.5.379: {}
+
+ enhanced-resolve@5.21.6:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.3
+
+ esbuild@0.28.1:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.28.1
+ '@esbuild/android-arm': 0.28.1
+ '@esbuild/android-arm64': 0.28.1
+ '@esbuild/android-x64': 0.28.1
+ '@esbuild/darwin-arm64': 0.28.1
+ '@esbuild/darwin-x64': 0.28.1
+ '@esbuild/freebsd-arm64': 0.28.1
+ '@esbuild/freebsd-x64': 0.28.1
+ '@esbuild/linux-arm': 0.28.1
+ '@esbuild/linux-arm64': 0.28.1
+ '@esbuild/linux-ia32': 0.28.1
+ '@esbuild/linux-loong64': 0.28.1
+ '@esbuild/linux-mips64el': 0.28.1
+ '@esbuild/linux-ppc64': 0.28.1
+ '@esbuild/linux-riscv64': 0.28.1
+ '@esbuild/linux-s390x': 0.28.1
+ '@esbuild/linux-x64': 0.28.1
+ '@esbuild/netbsd-arm64': 0.28.1
+ '@esbuild/netbsd-x64': 0.28.1
+ '@esbuild/openbsd-arm64': 0.28.1
+ '@esbuild/openbsd-x64': 0.28.1
+ '@esbuild/openharmony-arm64': 0.28.1
+ '@esbuild/sunos-x64': 0.28.1
+ '@esbuild/win32-arm64': 0.28.1
+ '@esbuild/win32-ia32': 0.28.1
+ '@esbuild/win32-x64': 0.28.1
+
+ escalade@3.2.0: {}
+
+ fdir@6.5.0(picomatch@4.0.4):
+ optionalDependencies:
+ picomatch: 4.0.4
+
+ fsevents@2.3.3:
+ optional: true
+
+ gensync@1.0.0-beta.2: {}
+
+ get-nonce@1.0.1: {}
+
+ graceful-fs@4.2.11: {}
+
+ jiti@2.7.0: {}
+
+ js-tokens@4.0.0: {}
+
+ jsesc@3.1.0: {}
+
+ json5@2.2.3: {}
+
+ lightningcss-android-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.32.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.32.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ optional: true
+
+ lightningcss@1.32.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.32.0
+ lightningcss-darwin-arm64: 1.32.0
+ lightningcss-darwin-x64: 1.32.0
+ lightningcss-freebsd-x64: 1.32.0
+ lightningcss-linux-arm-gnueabihf: 1.32.0
+ lightningcss-linux-arm64-gnu: 1.32.0
+ lightningcss-linux-arm64-musl: 1.32.0
+ lightningcss-linux-x64-gnu: 1.32.0
+ lightningcss-linux-x64-musl: 1.32.0
+ lightningcss-win32-arm64-msvc: 1.32.0
+ lightningcss-win32-x64-msvc: 1.32.0
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lucide-react@0.561.0(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.15: {}
+
+ next-themes@0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+
+ node-releases@2.0.50: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.4: {}
+
+ postcss@8.5.15:
+ dependencies:
+ nanoid: 3.3.15
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ radix-ui@1.6.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-accessible-icon': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-accordion': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-alert-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-aspect-ratio': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-avatar': 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-checkbox': 1.3.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-collapsible': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context-menu': 2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-dropdown-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-form': 0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-hover-card': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-label': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-menubar': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-navigation-menu': 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-one-time-password-field': 0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-password-toggle-field': 0.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-popover': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-progress': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-radio-group': 1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-scroll-area': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-select': 2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-separator': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slider': 1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-switch': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-tabs': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toast': 1.2.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toggle': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toggle-group': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toolbar': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-tooltip': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ react-dom@19.2.7(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ scheduler: 0.27.0
+
+ react-refresh@0.18.0: {}
+
+ react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7)
+ react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7)
+ tslib: 2.8.1
+ use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7)
+ use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ get-nonce: 1.0.1
+ react: 19.2.7
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react@19.2.7: {}
+
+ rollup@4.62.2:
+ dependencies:
+ '@types/estree': 1.0.9
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.62.2
+ '@rollup/rollup-android-arm64': 4.62.2
+ '@rollup/rollup-darwin-arm64': 4.62.2
+ '@rollup/rollup-darwin-x64': 4.62.2
+ '@rollup/rollup-freebsd-arm64': 4.62.2
+ '@rollup/rollup-freebsd-x64': 4.62.2
+ '@rollup/rollup-linux-arm-gnueabihf': 4.62.2
+ '@rollup/rollup-linux-arm-musleabihf': 4.62.2
+ '@rollup/rollup-linux-arm64-gnu': 4.62.2
+ '@rollup/rollup-linux-arm64-musl': 4.62.2
+ '@rollup/rollup-linux-loong64-gnu': 4.62.2
+ '@rollup/rollup-linux-loong64-musl': 4.62.2
+ '@rollup/rollup-linux-ppc64-gnu': 4.62.2
+ '@rollup/rollup-linux-ppc64-musl': 4.62.2
+ '@rollup/rollup-linux-riscv64-gnu': 4.62.2
+ '@rollup/rollup-linux-riscv64-musl': 4.62.2
+ '@rollup/rollup-linux-s390x-gnu': 4.62.2
+ '@rollup/rollup-linux-x64-gnu': 4.62.2
+ '@rollup/rollup-linux-x64-musl': 4.62.2
+ '@rollup/rollup-openbsd-x64': 4.62.2
+ '@rollup/rollup-openharmony-arm64': 4.62.2
+ '@rollup/rollup-win32-arm64-msvc': 4.62.2
+ '@rollup/rollup-win32-ia32-msvc': 4.62.2
+ '@rollup/rollup-win32-x64-gnu': 4.62.2
+ '@rollup/rollup-win32-x64-msvc': 4.62.2
+ fsevents: 2.3.3
+
+ scheduler@0.27.0: {}
+
+ semver@6.3.1: {}
+
+ sonner@2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+
+ source-map-js@1.2.1: {}
+
+ tailwind-merge@3.6.0: {}
+
+ tailwindcss@4.3.1: {}
+
+ tapable@2.3.3: {}
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+
+ tslib@2.8.1: {}
+
+ typescript@5.9.3: {}
+
+ undici-types@8.3.0: {}
+
+ update-browserslist-db@1.2.3(browserslist@4.28.4):
+ dependencies:
+ browserslist: 4.28.4
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ detect-node-es: 1.1.0
+ react: 19.2.7
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ vite@7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0):
+ dependencies:
+ esbuild: 0.28.1
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+ postcss: 8.5.15
+ rollup: 4.62.2
+ tinyglobby: 0.2.17
+ optionalDependencies:
+ '@types/node': 26.0.1
+ fsevents: 2.3.3
+ jiti: 2.7.0
+ lightningcss: 1.32.0
+
+ yallist@3.1.1: {}
diff --git a/frontend-next/src/App.tsx b/frontend-next/src/App.tsx
new file mode 100644
index 0000000..950c504
--- /dev/null
+++ b/frontend-next/src/App.tsx
@@ -0,0 +1,1562 @@
+import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react"
+import {
+ Archive,
+ Check,
+ Copy,
+ Inbox,
+ Loader2,
+ LogOut,
+ Mail,
+ Moon,
+ MoreHorizontal,
+ PanelLeftClose,
+ PanelLeftOpen,
+ Pencil,
+ Plus,
+ RefreshCw,
+ Search,
+ Send,
+ Settings,
+ Sun,
+ Trash2,
+} from "lucide-react"
+import {
+ ApiClient,
+ defaultUserOpenSettings,
+ defaultUserSettings,
+ defaultAddressSettings,
+ defaultOpenSettings,
+ formatDate,
+ parseJwtAddress,
+ type BoundAddress,
+ type AddressSettings,
+ type MailItem,
+ type OpenSettings,
+ type SendMailPayload,
+ type UserOpenSettings,
+ type UserSettings,
+} from "./api"
+import { TurnstileWidget } from "./components/TurnstileWidget"
+import { Alert, AlertDescription } from "./components/ui/alert"
+import { Badge } from "./components/ui/badge"
+import { Button } from "./components/ui/button"
+import { Card, CardContent, CardHeader, CardTitle } from "./components/ui/card"
+import { Checkbox } from "./components/ui/checkbox"
+import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./components/ui/dialog"
+import { Input } from "./components/ui/input"
+import { Label } from "./components/ui/label"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./components/ui/select"
+import { Tabs, TabsList, TabsTrigger } from "./components/ui/tabs"
+import { Textarea } from "./components/ui/textarea"
+import { cn } from "./lib/utils"
+
+type Screen = "inbox" | "compose" | "addresses" | "settings"
+type Notice = { type: "success" | "error"; text: string } | null
+
+const PAGE_SIZE = 20
+
+function App() {
+ const [screen, setScreen] = useState("inbox")
+ const [theme, setTheme] = useLocalStorageState<"light" | "dark">("frontendNextTheme", "light")
+ const [jwt, setJwt] = useLocalStorageState("jwt", "")
+ const [userJwt, setUserJwt] = useLocalStorageState("userJwt", "")
+ const [customAuth, setCustomAuth] = useLocalStorageState("auth", "")
+ const [localCache, setLocalCache] = useLocalStorageArray("LocalAddressCache")
+ const [openSettings, setOpenSettings] = useState(defaultOpenSettings)
+ const [settings, setSettings] = useState(defaultAddressSettings)
+ const [userOpenSettings, setUserOpenSettings] = useState(defaultUserOpenSettings)
+ const [userSettings, setUserSettings] = useState(defaultUserSettings)
+ const [boundAddresses, setBoundAddresses] = useState([])
+ const [mails, setMails] = useState([])
+ const [selectedId, setSelectedId] = useState("")
+ const [search, setSearch] = useState("")
+ const [page, setPage] = useState(1)
+ const [totalCount, setTotalCount] = useState(0)
+ const [loading, setLoading] = useState(false)
+ const [notice, setNotice] = useState(null)
+ const [showUserDialog, setShowUserDialog] = useState(false)
+ const [sidebarCollapsed, setSidebarCollapsed] = useLocalStorageState("frontendNextSidebarCollapsed", "false")
+ const isSidebarCollapsed = sidebarCollapsed === "true"
+
+ const api = useMemo(() => new ApiClient(() => jwt, () => customAuth, () => userJwt), [customAuth, jwt, userJwt])
+ const selected = mails.find((mail) => String(mail.id) === selectedId) ?? mails[0] ?? null
+ const address = settings.address || parseJwtAddress(jwt)
+ const hasAddress = Boolean(settings.address)
+
+ const showNotice = useCallback((nextNotice: Notice) => {
+ setNotice(nextNotice)
+ if (nextNotice) window.setTimeout(() => setNotice(null), 3200)
+ }, [])
+
+ const loadOpenSettings = useCallback(async () => {
+ const nextOpenSettings = await api.getOpenSettings()
+ setOpenSettings(nextOpenSettings)
+ }, [api])
+
+ const loadSettings = useCallback(async () => {
+ const nextSettings = await api.getSettings()
+ setSettings(nextSettings)
+ if (nextSettings.address && jwt && !localCache.includes(jwt)) {
+ setLocalCache([jwt, ...localCache])
+ }
+ }, [api, jwt, localCache, setLocalCache])
+
+ const loadUserOpenSettings = useCallback(async () => {
+ const nextUserOpenSettings = await api.getUserOpenSettings()
+ setUserOpenSettings(nextUserOpenSettings)
+ }, [api])
+
+ const loadUserSettings = useCallback(async () => {
+ const nextUserSettings = await api.getUserSettings()
+ setUserSettings(nextUserSettings)
+ if (nextUserSettings.new_user_token) {
+ setUserJwt(nextUserSettings.new_user_token)
+ }
+ }, [api, setUserJwt])
+
+ const loadBoundAddresses = useCallback(async () => {
+ if (!userJwt) {
+ setBoundAddresses([])
+ return
+ }
+ setBoundAddresses(await api.listBoundAddresses())
+ }, [api, userJwt])
+
+ const refreshMails = useCallback(async () => {
+ if (!settings.address) return
+ setLoading(true)
+ try {
+ const response = await api.listMails(PAGE_SIZE, (page - 1) * PAGE_SIZE)
+ const results = response.results || []
+ setMails(results)
+ setTotalCount(Number(response.count || results.length))
+ setSelectedId((current) => {
+ if (results.some((mail) => String(mail.id) === current)) return current
+ return results[0] ? String(results[0].id) : ""
+ })
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setLoading(false)
+ }
+ }, [api, page, settings.address, showNotice])
+
+ useEffect(() => {
+ loadOpenSettings().catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))
+ }, [loadOpenSettings, showNotice])
+
+ useEffect(() => {
+ loadUserOpenSettings().catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))
+ }, [loadUserOpenSettings, showNotice])
+
+ useEffect(() => {
+ loadSettings().catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))
+ }, [loadSettings, showNotice])
+
+ useEffect(() => {
+ loadUserSettings().catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))
+ }, [loadUserSettings, showNotice])
+
+ useEffect(() => {
+ loadBoundAddresses().catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))
+ }, [loadBoundAddresses, showNotice])
+
+ useEffect(() => {
+ refreshMails()
+ }, [refreshMails])
+
+ const switchJwt = (nextJwt: string) => {
+ setJwt(nextJwt)
+ setScreen("inbox")
+ setMails([])
+ setSelectedId("")
+ setSettings(defaultAddressSettings)
+ }
+
+ const logout = () => {
+ setJwt("")
+ setSettings({ ...defaultAddressSettings, fetched: true })
+ setMails([])
+ setSelectedId("")
+ }
+
+ return (
+
+
+ {notice &&
}
+
+
+
+ User account
+
+ Optional login for binding and syncing mailbox addresses. Mail and send access stay with each address.
+
+
+ {
+ await loadUserSettings()
+ await loadBoundAddresses()
+ }}
+ />
+
+
+ {!hasAddress ? (
+
setShowUserDialog(true)}
+ sidebarCollapsed={isSidebarCollapsed}
+ toggleSidebar={() => setSidebarCollapsed(isSidebarCollapsed ? "false" : "true")}
+ userEmail={userSettings.user_email}
+ />
+ ) : (
+
+
setSidebarCollapsed(isSidebarCollapsed ? "false" : "true")}
+ userEmail={userSettings.user_email}
+ onOpenUser={() => setShowUserDialog(true)}
+ />
+
+
+
+ {screen === "inbox" && (
+
+ setPage((current) => current + 1)}
+ onPrevPage={() => setPage((current) => Math.max(1, current - 1))}
+ onRefresh={refreshMails}
+ onSelect={setSelectedId}
+ />
+ {
+ await api.deleteMail(mail.id)
+ showNotice({ type: "success", text: "Mail deleted" })
+ await refreshMails()
+ }}
+ showNotice={showNotice}
+ />
+
+ )}
+ {screen === "compose" && (
+
+ )}
+ {screen === "addresses" && (
+ logout()}
+ />
+ )}
+ {screen === "settings" && (
+ {
+ await api.clearInbox()
+ await refreshMails()
+ showNotice({ type: "success", text: "Inbox cleared" })
+ }}
+ onClearSentItems={async () => {
+ await api.clearSentItems()
+ showNotice({ type: "success", text: "Sent items cleared" })
+ }}
+ onDeleteAddress={async () => {
+ await api.deleteAddress()
+ logout()
+ showNotice({ type: "success", text: "Address deleted" })
+ }}
+ onLogout={logout}
+ showNotice={showNotice}
+ />
+ )}
+
+
+ )}
+
+
+ )
+}
+
+function AuthScreen({
+ api,
+ customAuth,
+ openSettings,
+ setCustomAuth,
+ setJwt,
+ theme,
+ showNotice,
+ reloadSettings,
+ onOpenUser,
+ sidebarCollapsed,
+ toggleSidebar,
+ userEmail,
+}: {
+ api: ApiClient
+ customAuth: string
+ openSettings: OpenSettings
+ setCustomAuth: (value: string) => void
+ setJwt: (jwt: string) => void
+ theme: "light" | "dark"
+ showNotice: (notice: Notice) => void
+ reloadSettings: () => Promise
+ onOpenUser: () => void
+ sidebarCollapsed: boolean
+ toggleSidebar: () => void
+ userEmail: string
+}) {
+ const [showRestore, setShowRestore] = useState(false)
+ const [name, setName] = useState("")
+ const [domain, setDomain] = useState("")
+ const [credential, setCredential] = useState("")
+ const [loginMode, setLoginMode] = useState<"credential" | "password">("credential")
+ const [loginAddress, setLoginAddress] = useState("")
+ const [loginPassword, setLoginPassword] = useState("")
+ const [createToken, setCreateToken] = useState("")
+ const [loginToken, setLoginToken] = useState("")
+ const [randomSubdomain, setRandomSubdomain] = useState(false)
+ const [busy, setBusy] = useState(false)
+ const [generateNameLoading, setGenerateNameLoading] = useState(false)
+
+ const domains = useMemo(() => {
+ if (!openSettings.defaultDomains.length) return openSettings.domains
+ return openSettings.domains.filter((item) => openSettings.defaultDomains.includes(item.value))
+ }, [openSettings.defaultDomains, openSettings.domains])
+
+ useEffect(() => {
+ if (!domain && domains[0]) setDomain(domains[0].value)
+ }, [domain, domains])
+
+ const canUseRandomSubdomain = domain && openSettings.randomSubdomainDomains.includes(domain)
+
+ const generateName = async () => {
+ setGenerateNameLoading(true)
+ try {
+ const fakerModuleUrl = "https://esm.sh/@faker-js/faker"
+ const module = (await import(/* @vite-ignore */ fakerModuleUrl)) as {
+ faker: { internet: { email: () => string } }
+ }
+ const nextName = module.faker.internet
+ .email()
+ .split("@")[0]
+ .replace(/\s+/g, ".")
+ .replace(/\.{2,}/g, ".")
+ setName(normalizeAddressName(nextName, openSettings.addressRegex).slice(0, openSettings.maxAddressLen))
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setGenerateNameLoading(false)
+ }
+ }
+
+ const createAddress = async () => {
+ if (!domain) {
+ showNotice({ type: "error", text: "No domain is available" })
+ return
+ }
+ setBusy(true)
+ try {
+ const response = await api.createAddress(openSettings.disableCustomAddressName ? "" : name, domain, createToken, randomSubdomain)
+ setJwt(response.jwt)
+ await reloadSettings()
+ showNotice({ type: "success", text: response.password ? `Address created. Password: ${response.password}` : "Address created" })
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setBusy(false)
+ }
+ }
+
+ const restoreAddress = async () => {
+ setBusy(true)
+ try {
+ const nextJwt =
+ loginMode === "password"
+ ? await api.passwordLogin(loginAddress, loginPassword, loginToken)
+ : await api.credentialLogin(credential.trim(), loginToken)
+ setJwt(nextJwt)
+ await reloadSettings()
+ showNotice({ type: "success", text: "Address restored" })
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setBusy(false)
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
{openSettings.title || "Temp Email"}
+
+ {sidebarCollapsed ? : }
+
+
+
+
+ {userEmail ? userEmail.slice(0, 2).toUpperCase() : "U"}
+
+ {userEmail || "User login"}
+ {userEmail ? "Account settings" : "Sync addresses"}
+
+
+
+
+
+
+
+
+
Address studio
+
+
Create a disposable inbox
+
+ {domains.length > 0
+ ? `${domains.length} domain${domains.length > 1 ? "s" : ""} available`
+ : "Waiting for domain settings"}
+
+
+
+
+ {openSettings.needAuth && (
+
+ setCustomAuth(event.target.value)}
+ placeholder="Access password"
+ type="password"
+ />
+
+ )}
+
+
+
Choose your address
+
+ {openSettings.prefix && {openSettings.prefix} }
+ setName(normalizeAddressName(event.target.value, openSettings.addressRegex))}
+ placeholder={openSettings.disableCustomAddressName ? "auto generated" : "quiet-forest-4821"}
+ />
+ @
+
+
+
+
+
+ {domains.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+
+ {generateNameLoading ? : }
+ Generate name
+
+ setShowRestore(true)}>
+ Restore existing
+
+
+
+ {canUseRandomSubdomain && (
+
+ setRandomSubdomain(checked === true)}
+ />
+ Use random subdomain
+
+ )}
+
+
+ {busy && }
+ Create address
+
+
+
+
+
+
+
+ User account
+
+
+
+
{userEmail ? userEmail.slice(0, 2).toUpperCase() : "U"}
+
+
{userEmail || "Not signed in"}
+
+ {userEmail
+ ? "Manage addresses bound to this account."
+ : "Optional login for binding and syncing mailbox addresses."}
+
+
+
+
+ {userEmail ? "Manage account" : "User login"}
+
+
+
+
+
+
+ Restore address
+
+
+ Use an address credential or password when you already have a mailbox.
+ setShowRestore(true)}>
+ Restore
+
+
+
+
+
+
+
Mailbox addresses receive mail independently. A user account only helps sync and manage them.
+
+
+
+
+
+
+
+ Restore address
+ Paste a credential or use an address password.
+
+
+ {openSettings.enableAddressPassword && (
+ setLoginMode(value as "credential" | "password")}>
+
+
+ Credential
+
+
+ Password
+
+
+
+ )}
+ {loginMode === "credential" ? (
+
+
+
+
+ )
+}
+
+function Sidebar({
+ screen,
+ setScreen,
+ unread,
+ loading,
+ refresh,
+ collapsed,
+ toggleCollapsed,
+ userEmail,
+ onOpenUser,
+}: {
+ screen: Screen
+ setScreen: (screen: Screen) => void
+ unread: number
+ loading: boolean
+ refresh: () => void
+ collapsed: boolean
+ toggleCollapsed: () => void
+ userEmail: string
+ onOpenUser: () => void
+}) {
+ return (
+
+
+
+
+
+
Temp Email
+
+ {collapsed ? : }
+
+
+ setScreen("addresses")}>
+
+ New address
+
+
+ setScreen("inbox")} icon={ }>
+ Inbox
+ {unread}
+
+ setScreen("compose")} icon={ }>
+ Compose
+
+ setScreen("addresses")}
+ icon={ }
+ >
+ Addresses
+
+ setScreen("settings")}
+ icon={ }
+ >
+ Settings
+
+
+
+ {loading ? : }
+ Refresh
+
+
+ {userEmail ? userEmail.slice(0, 2).toUpperCase() : "U"}
+
+ {userEmail || "User login"}
+ {userEmail ? "Account settings" : "Sync addresses"}
+
+
+
+ )
+}
+
+function UserAccountPanel({
+ api,
+ boundAddresses,
+ openSettings,
+ setJwt,
+ setUserJwt,
+ showNotice,
+ theme,
+ userJwt,
+ userOpenSettings,
+ userSettings,
+ reloadUser,
+}: {
+ api: ApiClient
+ boundAddresses: BoundAddress[]
+ openSettings: OpenSettings
+ setJwt: (jwt: string) => void
+ setUserJwt: (jwt: string) => void
+ showNotice: (notice: Notice) => void
+ theme: "light" | "dark"
+ userJwt: string
+ userOpenSettings: UserOpenSettings
+ userSettings: UserSettings
+ reloadUser: () => Promise
+}) {
+ const [mode, setMode] = useState<"login" | "register">("login")
+ const [email, setEmail] = useState("")
+ const [password, setPassword] = useState("")
+ const [code, setCode] = useState("")
+ const [cfToken, setCfToken] = useState("")
+ const [busy, setBusy] = useState(false)
+
+ const login = async () => {
+ setBusy(true)
+ try {
+ const nextUserJwt = await api.userLogin(email, password, cfToken)
+ setUserJwt(nextUserJwt)
+ showNotice({ type: "success", text: "User logged in" })
+ await reloadUser()
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setBusy(false)
+ }
+ }
+
+ const register = async () => {
+ setBusy(true)
+ try {
+ await api.userRegister(email, password, code, cfToken)
+ setMode("login")
+ showNotice({ type: "success", text: "Registration complete. Please log in." })
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setBusy(false)
+ }
+ }
+
+ const sendCode = async () => {
+ setBusy(true)
+ try {
+ const response = await api.sendUserVerifyCode(email, cfToken)
+ showNotice({ type: "success", text: `Verification code sent${response.expirationTtl ? ` for ${response.expirationTtl}s` : ""}` })
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setBusy(false)
+ }
+ }
+
+ if (userJwt && userSettings.user_email) {
+ return (
+
+
+
+
+
{userSettings.user_email}
+
Signed-in user account · addresses keep separate mail and send access
+
+
+ {boundAddresses.length === 0 &&
No bound addresses yet.
}
+ {boundAddresses.map((item) => {
+ const address = item.address || item.name || String(item.id)
+ return (
+
{
+ const nextJwt = await api.getBoundAddressJwt(item.id)
+ setJwt(nextJwt)
+ }}
+ >
+ {address}
+ {item.mail_count ?? 0}
+
+ )
+ })}
+
+ {
+ setUserJwt("")
+ showNotice({ type: "success", text: "User logged out" })
+ }}
+ >
+ Log out user
+
+
+
+
+ )
+ }
+
+ return (
+
+ )
+}
+
+function NavItem({
+ active,
+ icon,
+ children,
+ onClick,
+}: {
+ active: boolean
+ icon: ReactNode
+ children: ReactNode
+ onClick: () => void
+}) {
+ return (
+
+ {icon}
+ {children}
+
+ )
+}
+
+function AddressPill({ address, showNotice }: { address: string; showNotice: (notice: Notice) => void }) {
+ const [copied, setCopied] = useState(false)
+ const copyAddress = async () => {
+ await navigator.clipboard.writeText(address)
+ setCopied(true)
+ showNotice({ type: "success", text: "Address copied" })
+ window.setTimeout(() => setCopied(false), 1400)
+ }
+
+ return (
+
+
+ {address}
+
+ {copied ? : }
+
+
+ )
+}
+
+function MailList({
+ loading,
+ mails,
+ page,
+ selectedId,
+ totalCount,
+ onNextPage,
+ onPrevPage,
+ onRefresh,
+ onSelect,
+}: {
+ loading: boolean
+ mails: MailItem[]
+ page: number
+ selectedId: string
+ totalCount: number
+ onNextPage: () => void
+ onPrevPage: () => void
+ onRefresh: () => void
+ onSelect: (id: string) => void
+}) {
+ const canNext = totalCount > page * PAGE_SIZE
+ return (
+
+
+
+
Inbox
+ {totalCount} messages
+
+
+
+ {loading ? : }
+
+
+
+
+
+
+
+ {mails.length === 0 &&
{loading ? "Loading mail..." : "No mail yet"}
}
+ {mails.map((mail) => (
+
onSelect(String(mail.id))}
+ >
+ {senderInitials(mail.source)}
+
+
+ {senderName(mail.source)}
+ {formatDate(mail.created_at)}
+
+ {mail.subject || "No Subject"}
+ {mail.text || stripHtml(mail.message) || mail.raw || ""}
+
+
+ ))}
+
+
+
+ Prev
+
+ Page {page}
+
+ Next
+
+
+
+ )
+}
+
+function MessageReader({
+ mail,
+ address,
+ canDelete,
+ onDelete,
+ showNotice,
+}: {
+ mail: MailItem | null
+ address: string
+ canDelete: boolean
+ onDelete: (mail: MailItem) => Promise
+ showNotice: (notice: Notice) => void
+}) {
+ if (!mail) {
+ return (
+
+ )
+ }
+
+ const body = mail.message || mail.text || mail.raw || ""
+ const isHtml = /<\/?[a-z][\s\S]*>/i.test(body)
+
+ return (
+
+
+
+
+
+
+ {canDelete && (
+
onDelete(mail).catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))}
+ >
+
+
+ )}
+
+
+
+
+
{senderInitials(mail.source)}
+
+
{mail.subject || "No Subject"}
+
{mail.source || "Unknown sender"}
+
+ to {address} · {formatDate(mail.created_at)}
+
+
+
+
+
+ {isHtml ? (
+
+ ) : (
+ {body}
+ )}
+
+
+
+
+ )
+}
+
+function Compose({
+ address,
+ api,
+ sendBalance,
+ sendEnabled,
+ reloadSettings,
+ showNotice,
+}: {
+ address: string
+ api: ApiClient
+ sendBalance: number
+ sendEnabled: boolean
+ reloadSettings: () => Promise
+ showNotice: (notice: Notice) => void
+}) {
+ const [payload, setPayload] = useState({
+ from_name: "",
+ to_name: "",
+ to_mail: "",
+ subject: "",
+ is_html: false,
+ content: "",
+ })
+ const [busy, setBusy] = useState(false)
+
+ const send = async () => {
+ if (!payload.subject.trim() || !payload.to_mail.trim() || !payload.content.trim()) {
+ showNotice({ type: "error", text: "Recipient, subject, and content are required" })
+ return
+ }
+ setBusy(true)
+ try {
+ await api.sendMail(payload)
+ setPayload({ from_name: "", to_name: "", to_mail: "", subject: "", is_html: false, content: "" })
+ await reloadSettings()
+ showNotice({ type: "success", text: "Mail sent" })
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setBusy(false)
+ }
+ }
+
+ const requestAccess = async () => {
+ setBusy(true)
+ try {
+ await api.requestSendAccess()
+ await reloadSettings()
+ showNotice({ type: "success", text: "Send access requested" })
+ } catch (error) {
+ showNotice({ type: "error", text: getErrorMessage(error) })
+ } finally {
+ setBusy(false)
+ }
+ }
+
+ if (!sendEnabled) {
+ return (
+
+ Send mail
+ Sending mail is not enabled on this deployment.
+
+ )
+ }
+
+ if (sendBalance <= 0) {
+ return (
+
+ Send mail
+
+
+
+ Send access and balance belong to this mailbox address, not your user account. Request access for {address}.
+
+
+ Request access for this address
+
+
+
+
+ )
+ }
+
+ return (
+
+ )
+}
+
+function AddressStack({
+ currentJwt,
+ localCache,
+ boundAddresses,
+ userEmail,
+ openSettings,
+ api,
+ setJwt,
+ setLocalCache,
+ showNotice,
+ reloadBoundAddresses,
+ onCreateNew,
+}: {
+ currentJwt: string
+ localCache: string[]
+ boundAddresses: BoundAddress[]
+ userEmail: string
+ openSettings: OpenSettings
+ api: ApiClient
+ setJwt: (jwt: string) => void
+ setLocalCache: (cache: string[]) => void
+ showNotice: (notice: Notice) => void
+ reloadBoundAddresses: () => Promise
+ onCreateNew: () => void
+}) {
+ const addresses = localCache
+ .map((cachedJwt) => ({ jwt: cachedJwt, address: parseJwtAddress(cachedJwt) }))
+ .filter((item) => item.address)
+
+ return (
+
+
+
+
Your addresses
+
Each mailbox address has its own inbox, credential, and send access. User accounts only bind and sync addresses.
+
+
+ {userEmail && currentJwt && (
+
{
+ await api.bindCurrentAddress()
+ await reloadBoundAddresses()
+ showNotice({ type: "success", text: "Address bound to user" })
+ }}
+ >
+ Bind current
+
+ )}
+
+
+ New address
+
+
+
+ {!openSettings.enableUserCreateEmail && (
+ Anonymous address creation is disabled on this deployment.
+ )}
+
+ {userEmail && boundAddresses.length > 0 && (
+
Addresses bound to your user account
+ )}
+ {boundAddresses.map((item) => {
+ const address = item.address || item.name || String(item.id)
+ return (
+
{
+ const nextJwt = await api.getBoundAddressJwt(item.id)
+ setJwt(nextJwt)
+ }}
+ role="button"
+ tabIndex={0}
+ >
+
+
+
+
+
+ {address}
+ {item.mail_count ?? 0}
+
+ Bound to {userEmail}
+
+ {
+ event.stopPropagation()
+ await api.unbindAddress(item.id)
+ await reloadBoundAddresses()
+ showNotice({ type: "success", text: "Address unbound" })
+ }}
+ >
+
+
+
+ )
+ })}
+ {addresses.length > 0 &&
Local addresses
}
+ {addresses.map((item) => (
+
setJwt(item.jwt)}
+ role="button"
+ tabIndex={0}
+ onKeyDown={(event) => {
+ if (event.key === "Enter" || event.key === " ") setJwt(item.jwt)
+ }}
+ >
+
+
+
+
+
+ {item.address}
+ {item.jwt === currentJwt && Active }
+
+ Local credential
+
+ {
+ event.stopPropagation()
+ if (item.jwt === currentJwt) {
+ showNotice({ type: "error", text: "Switch away before removing the active address" })
+ return
+ }
+ setLocalCache(localCache.filter((cachedJwt) => cachedJwt !== item.jwt))
+ }}
+ >
+
+
+
+ ))}
+
+
+ )
+}
+
+function SettingsPanel({
+ address,
+ canDelete,
+ canSend,
+ customAuth,
+ enablePassword,
+ jwt,
+ userEmail,
+ userJwt,
+ setCustomAuth,
+ setUserJwt,
+ theme,
+ setTheme,
+ onClearInbox,
+ onClearSentItems,
+ onDeleteAddress,
+ onLogout,
+ showNotice,
+}: {
+ address: string
+ canDelete: boolean
+ canSend: boolean
+ customAuth: string
+ enablePassword: boolean
+ jwt: string
+ userEmail: string
+ userJwt: string
+ setCustomAuth: (value: string) => void
+ setUserJwt: (value: string) => void
+ theme: "light" | "dark"
+ setTheme: (theme: "light" | "dark") => void
+ onClearInbox: () => Promise
+ onClearSentItems: () => Promise
+ onDeleteAddress: () => Promise
+ onLogout: () => void
+ showNotice: (notice: Notice) => void
+}) {
+ return (
+
+ Settings
+
+
+ Appearance
+
+
+
+
+
Theme
+
Switch between light and dark.
+
+
setTheme(value as "light" | "dark")}>
+
+
+ Light
+
+
+ Dark
+
+
+
+
+
+
+
+
+ Address
+
+
+
+
+
{address}
+
Current address credential.
+
+
{
+ await navigator.clipboard.writeText(jwt)
+ showNotice({ type: "success", text: "Credential copied" })
+ }}
+ >
+
+ Copy credential
+
+
+ {enablePassword && Password changes are not implemented in this React preview yet.
}
+
+
+
Access password
+
Used when global access auth is enabled.
+
+
setCustomAuth(event.target.value)} placeholder="Access password" type="password" />
+
+
+
+
+
+ User account (optional)
+
+
+
+
{userEmail || "Not logged in"}
+
+ {userJwt
+ ? "This account binds and syncs mailbox addresses; it does not own inboxes or send access itself."
+ : "Log in to bind and sync addresses. Each address remains an independent mailbox."}
+
+
+ {userJwt && (
+ setUserJwt("")}>
+ Log out user
+
+ )}
+
+
+
+
+ Danger zone
+
+
+ {canDelete && (
+ <>
+ onClearInbox().catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))}>
+ Clear inbox
+
+ {canSend && (
+ onClearSentItems().catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))}
+ >
+ Clear sent items
+
+ )}
+ onDeleteAddress().catch((error) => showNotice({ type: "error", text: getErrorMessage(error) }))}>
+ Delete address
+
+ >
+ )}
+
+
+ Log out
+
+
+
+
+ )
+}
+
+function NoticeToast({ notice }: { notice: NonNullable }) {
+ return (
+
+ {notice.text}
+
+ )
+}
+
+function useLocalStorageState(key: string, defaultValue: T) {
+ const [value, setValue] = useState(() => localStorage.getItem(key) ?? defaultValue)
+ const setStoredValue = useCallback(
+ (nextValue: string) => {
+ setValue(nextValue)
+ localStorage.setItem(key, nextValue)
+ },
+ [key],
+ )
+ return [value as T, setStoredValue] as const
+}
+
+function useLocalStorageArray(key: string) {
+ const [value, setValue] = useState(() => {
+ try {
+ const raw = localStorage.getItem(key)
+ if (!raw) return []
+ const parsed = JSON.parse(raw)
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : []
+ } catch {
+ return []
+ }
+ })
+ const setStoredValue = useCallback(
+ (nextValue: string[]) => {
+ const deduped = Array.from(new Set(nextValue.filter(Boolean)))
+ setValue(deduped)
+ localStorage.setItem(key, JSON.stringify(deduped))
+ },
+ [key],
+ )
+ return [value, setStoredValue] as const
+}
+
+function filterMails(mails: MailItem[], search: string) {
+ const keyword = search.trim().toLowerCase()
+ if (!keyword) return mails
+ return mails.filter((mail) =>
+ [mail.source, mail.subject, mail.text, mail.message, mail.raw].some((value) => String(value || "").toLowerCase().includes(keyword)),
+ )
+}
+
+function normalizeAddressName(value: string, pattern: string) {
+ try {
+ return value.toLowerCase().replace(new RegExp(pattern || "[^a-z0-9]", "g"), "")
+ } catch {
+ return value.toLowerCase().replace(/[^a-z0-9]/g, "")
+ }
+}
+
+function senderName(source?: string) {
+ if (!source) return "Unknown"
+ return source.replace(/<.*?>/g, "").trim() || source
+}
+
+function senderInitials(source?: string) {
+ return senderName(source)
+ .split(/\s+/)
+ .map((part) => part[0])
+ .join("")
+ .slice(0, 2)
+ .toUpperCase()
+}
+
+function stripHtml(value?: string) {
+ if (!value) return ""
+ return value.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim()
+}
+
+function getErrorMessage(error: unknown) {
+ return error instanceof Error ? error.message : String(error)
+}
+
+export default App
diff --git a/frontend-next/src/api.ts b/frontend-next/src/api.ts
new file mode 100644
index 0000000..56c3a30
--- /dev/null
+++ b/frontend-next/src/api.ts
@@ -0,0 +1,397 @@
+const API_BASE = import.meta.env.VITE_API_BASE || ""
+
+export type DomainOption = {
+ label: string
+ value: string
+}
+
+export type OpenSettings = {
+ fetched: boolean
+ title: string
+ prefix: string
+ addressRegex: string
+ minAddressLen: number
+ maxAddressLen: number
+ needAuth: boolean
+ enableUserCreateEmail: boolean
+ disableAnonymousUserCreateEmail: boolean
+ disableCustomAddressName: boolean
+ enableUserDeleteEmail: boolean
+ enableSendMail: boolean
+ enableAddressPassword: boolean
+ defaultDomains: string[]
+ randomSubdomainDomains: string[]
+ domains: DomainOption[]
+ cfTurnstileSiteKey: string
+ enableGlobalTurnstileCheck: boolean
+}
+
+export type AddressSettings = {
+ fetched: boolean
+ address: string
+ send_balance: number
+ auto_reply?: unknown
+}
+
+export type UserOpenSettings = {
+ fetched: boolean
+ enable: boolean
+ enableMailVerify: boolean
+ oauth2ClientIDs: { clientID: string; name: string; icon?: string }[]
+}
+
+export type UserSettings = {
+ fetched: boolean
+ user_email: string
+ user_id: number
+ is_admin: boolean
+ access_token: string | null
+ new_user_token: string | null
+ user_role: { domains?: string[] | null; role: string; prefix?: string | null } | null
+}
+
+export type BoundAddress = {
+ id: number | string
+ name?: string
+ address?: string
+ mail_count?: number
+ send_count?: number
+}
+
+export type MailItem = {
+ id: number | string
+ source?: string
+ address?: string
+ subject?: string
+ message?: string
+ text?: string
+ raw?: string
+ created_at?: string
+ metadata?: unknown
+}
+
+export type MailListResponse = {
+ results: MailItem[]
+ count: number
+}
+
+export type SendMailPayload = {
+ from_name: string
+ to_name: string
+ to_mail: string
+ subject: string
+ is_html: boolean
+ content: string
+}
+
+export const defaultOpenSettings: OpenSettings = {
+ fetched: false,
+ title: "",
+ prefix: "",
+ addressRegex: "",
+ minAddressLen: 1,
+ maxAddressLen: 30,
+ needAuth: false,
+ enableUserCreateEmail: false,
+ disableAnonymousUserCreateEmail: false,
+ disableCustomAddressName: false,
+ enableUserDeleteEmail: false,
+ enableSendMail: false,
+ enableAddressPassword: false,
+ defaultDomains: [],
+ randomSubdomainDomains: [],
+ domains: [],
+ cfTurnstileSiteKey: "",
+ enableGlobalTurnstileCheck: false,
+}
+
+export const defaultAddressSettings: AddressSettings = {
+ fetched: false,
+ address: "",
+ send_balance: 0,
+}
+
+export const defaultUserOpenSettings: UserOpenSettings = {
+ fetched: false,
+ enable: false,
+ enableMailVerify: false,
+ oauth2ClientIDs: [],
+}
+
+export const defaultUserSettings: UserSettings = {
+ fetched: false,
+ user_email: "",
+ user_id: 0,
+ is_admin: false,
+ access_token: null,
+ new_user_token: null,
+ user_role: null,
+}
+
+const hasControlChar = (value: string) => {
+ for (let index = 0; index < value.length; index += 1) {
+ const code = value.charCodeAt(index)
+ if (code < 32 || code === 127) return true
+ }
+ return false
+}
+
+const safeHeaderValue = (value: string | null | undefined) => {
+ if (!value) return undefined
+ const trimmed = value.trim()
+ if (!trimmed || trimmed === "undefined" || trimmed === "null") return undefined
+ if (hasControlChar(trimmed)) return undefined
+ return trimmed
+}
+
+const safeBearerHeader = (jwt: string | null | undefined) => {
+ const safe = safeHeaderValue(jwt)
+ return safe ? `Bearer ${safe}` : undefined
+}
+
+const normalizeOpenSettings = (payload: Record): OpenSettings => {
+ const domains = Array.isArray(payload.domains) ? (payload.domains as string[]) : []
+ const domainLabels = Array.isArray(payload.domainLabels) ? (payload.domainLabels as string[]) : []
+ return {
+ ...defaultOpenSettings,
+ fetched: true,
+ title: String(payload.title || ""),
+ prefix: String(payload.prefix || ""),
+ addressRegex: String(payload.addressRegex || ""),
+ minAddressLen: Number(payload.minAddressLen || 1),
+ maxAddressLen: Number(payload.maxAddressLen || 30),
+ needAuth: Boolean(payload.needAuth),
+ enableUserCreateEmail: Boolean(payload.enableUserCreateEmail),
+ disableAnonymousUserCreateEmail: Boolean(payload.disableAnonymousUserCreateEmail),
+ disableCustomAddressName: Boolean(payload.disableCustomAddressName),
+ enableUserDeleteEmail: Boolean(payload.enableUserDeleteEmail),
+ enableSendMail: Boolean(payload.enableSendMail),
+ enableAddressPassword: Boolean(payload.enableAddressPassword),
+ defaultDomains: Array.isArray(payload.defaultDomains) ? (payload.defaultDomains as string[]) : [],
+ randomSubdomainDomains: Array.isArray(payload.randomSubdomainDomains)
+ ? (payload.randomSubdomainDomains as string[])
+ : [],
+ domains: domains.map((domain, index) => ({
+ label: domainLabels[index] || domain,
+ value: domain,
+ })),
+ cfTurnstileSiteKey: String(payload.cfTurnstileSiteKey || ""),
+ enableGlobalTurnstileCheck: Boolean(payload.enableGlobalTurnstileCheck),
+ }
+}
+
+export async function hashPassword(password: string) {
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(password))
+ return Array.from(new Uint8Array(digest))
+ .map((byte) => byte.toString(16).padStart(2, "0"))
+ .join("")
+}
+
+export function parseJwtAddress(jwt: string) {
+ try {
+ const payload = JSON.parse(decodeURIComponent(atob(jwt.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"))))
+ return typeof payload.address === "string" ? payload.address : ""
+ } catch {
+ return ""
+ }
+}
+
+export function formatDate(value?: string) {
+ if (!value) return ""
+ const date = new Date(`${value} UTC`)
+ if (Number.isNaN(date.getTime())) return value
+ return date.toLocaleString()
+}
+
+export class ApiClient {
+ constructor(
+ private readonly getJwt: () => string,
+ private readonly getCustomAuth: () => string,
+ private readonly getUserJwt: () => string,
+ ) {}
+
+ async request(path: string, options: RequestInit = {}) {
+ const headers: Record = {
+ "Content-Type": "application/json",
+ "x-lang": "en",
+ }
+ const customAuth = safeHeaderValue(this.getCustomAuth())
+ const userJwt = safeHeaderValue(this.getUserJwt())
+ const authorization = safeBearerHeader(this.getJwt())
+ if (customAuth) headers["x-custom-auth"] = customAuth
+ if (userJwt) headers["x-user-token"] = userJwt
+ if (authorization) headers.Authorization = authorization
+
+ const response = await fetch(`${API_BASE}${path}`, {
+ ...options,
+ headers: {
+ ...headers,
+ ...(options.headers as Record | undefined),
+ },
+ })
+ const text = await response.text()
+ const data = text ? tryParseJson(text) : null
+ if (response.status >= 300) {
+ throw new Error(typeof data === "string" ? data : text || `[${response.status}] request failed`)
+ }
+ return data as T
+ }
+
+ async getOpenSettings() {
+ return normalizeOpenSettings(await this.request>("/open_api/settings"))
+ }
+
+ async getSettings() {
+ if (!safeHeaderValue(this.getJwt())) return { ...defaultAddressSettings, fetched: true }
+ const payload = await this.request>("/api/settings")
+ return {
+ fetched: true,
+ address: String(payload.address || ""),
+ send_balance: Number(payload.send_balance || 0),
+ auto_reply: payload.auto_reply,
+ } satisfies AddressSettings
+ }
+
+ async getUserOpenSettings() {
+ return {
+ ...defaultUserOpenSettings,
+ ...(await this.request>("/user_api/open_settings")),
+ fetched: true,
+ } satisfies UserOpenSettings
+ }
+
+ async getUserSettings() {
+ if (!safeHeaderValue(this.getUserJwt())) return { ...defaultUserSettings, fetched: true }
+ return {
+ ...defaultUserSettings,
+ ...(await this.request>("/user_api/settings")),
+ fetched: true,
+ } satisfies UserSettings
+ }
+
+ async userLogin(email: string, password: string, cfToken: string) {
+ const response = await this.request<{ jwt: string }>("/user_api/login", {
+ method: "POST",
+ body: JSON.stringify({
+ email,
+ password: await hashPassword(password),
+ cf_token: cfToken,
+ }),
+ })
+ return response.jwt
+ }
+
+ async sendUserVerifyCode(email: string, cfToken: string) {
+ return this.request<{ expirationTtl?: number }>("/user_api/verify_code", {
+ method: "POST",
+ body: JSON.stringify({ email, cf_token: cfToken }),
+ })
+ }
+
+ async userRegister(email: string, password: string, code: string, cfToken: string) {
+ await this.request("/user_api/register", {
+ method: "POST",
+ body: JSON.stringify({
+ email,
+ password: await hashPassword(password),
+ code,
+ cf_token: cfToken,
+ }),
+ })
+ }
+
+ async listBoundAddresses() {
+ const response = await this.request<{ results: BoundAddress[] }>("/user_api/bind_address")
+ return response.results || []
+ }
+
+ async bindCurrentAddress() {
+ await this.request("/user_api/bind_address", { method: "POST" })
+ }
+
+ async getBoundAddressJwt(addressId: string | number) {
+ const response = await this.request<{ jwt: string }>(`/user_api/bind_address_jwt/${addressId}`)
+ return response.jwt
+ }
+
+ async unbindAddress(addressId: string | number) {
+ await this.request("/user_api/unbind_address", {
+ method: "POST",
+ body: JSON.stringify({ address_id: addressId }),
+ })
+ }
+
+ async createAddress(name: string, domain: string, cfToken: string, enableRandomSubdomain: boolean) {
+ return this.request<{ jwt: string; password?: string }>("/api/new_address", {
+ method: "POST",
+ body: JSON.stringify({
+ name,
+ domain,
+ cf_token: cfToken,
+ enableRandomSubdomain,
+ }),
+ })
+ }
+
+ async credentialLogin(credential: string, cfToken: string) {
+ await this.request("/open_api/credential_login", {
+ method: "POST",
+ body: JSON.stringify({ credential, cf_token: cfToken }),
+ })
+ return credential
+ }
+
+ async passwordLogin(email: string, password: string, cfToken: string) {
+ const response = await this.request<{ jwt: string }>("/api/address_login", {
+ method: "POST",
+ body: JSON.stringify({
+ email,
+ password: await hashPassword(password),
+ cf_token: cfToken,
+ }),
+ })
+ return response.jwt
+ }
+
+ async listMails(limit: number, offset: number) {
+ return this.request(`/api/mails?limit=${limit}&offset=${offset}`)
+ }
+
+ async deleteMail(id: string | number) {
+ await this.request(`/api/mails/${id}`, { method: "DELETE" })
+ }
+
+ async sendMail(payload: SendMailPayload) {
+ await this.request("/api/send_mail", {
+ method: "POST",
+ body: JSON.stringify(payload),
+ })
+ }
+
+ async requestSendAccess() {
+ await this.request("/api/request_send_mail_access", {
+ method: "POST",
+ body: JSON.stringify({}),
+ })
+ }
+
+ async clearInbox() {
+ await this.request("/api/clear_inbox", { method: "DELETE" })
+ }
+
+ async clearSentItems() {
+ await this.request("/api/clear_sent_items", { method: "DELETE" })
+ }
+
+ async deleteAddress() {
+ await this.request("/api/delete_address", { method: "DELETE" })
+ }
+}
+
+function tryParseJson(value: string) {
+ try {
+ return JSON.parse(value)
+ } catch {
+ return value
+ }
+}
diff --git a/frontend-next/src/components/TurnstileWidget.tsx b/frontend-next/src/components/TurnstileWidget.tsx
new file mode 100644
index 0000000..4dc10a0
--- /dev/null
+++ b/frontend-next/src/components/TurnstileWidget.tsx
@@ -0,0 +1,98 @@
+import { useEffect, useId, useRef, useState } from "react"
+import { Button } from "./ui/button"
+
+declare global {
+ interface Window {
+ turnstile?: {
+ render: (
+ selector: string,
+ options: {
+ sitekey: string
+ theme: "light" | "dark"
+ callback: (token: string) => void
+ },
+ ) => string
+ remove: (id: string) => void
+ }
+ }
+}
+
+type TurnstileWidgetProps = {
+ siteKey: string
+ theme: "light" | "dark"
+ onToken: (token: string) => void
+}
+
+let scriptPromise: Promise | null = null
+
+function loadTurnstileScript() {
+ if (window.turnstile) return Promise.resolve()
+ if (scriptPromise) return scriptPromise
+ scriptPromise = new Promise((resolve, reject) => {
+ const existing = document.querySelector("script[data-turnstile]")
+ if (existing) {
+ existing.addEventListener("load", () => resolve(), { once: true })
+ existing.addEventListener("error", () => reject(new Error("Turnstile script failed")), { once: true })
+ return
+ }
+ const script = document.createElement("script")
+ script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
+ script.async = true
+ script.defer = true
+ script.dataset.turnstile = "true"
+ script.onload = () => resolve()
+ script.onerror = () => reject(new Error("Turnstile script failed"))
+ document.head.appendChild(script)
+ })
+ return scriptPromise
+}
+
+export function TurnstileWidget({ siteKey, theme, onToken }: TurnstileWidgetProps) {
+ const id = `turnstile-${useId().replace(/:/g, "")}`
+ const widgetId = useRef("")
+ const [failed, setFailed] = useState(false)
+
+ useEffect(() => {
+ if (!siteKey) return undefined
+ let mounted = true
+
+ const renderWidget = async () => {
+ setFailed(false)
+ onToken("")
+ try {
+ await loadTurnstileScript()
+ if (!mounted || !window.turnstile) return
+ if (widgetId.current) window.turnstile.remove(widgetId.current)
+ widgetId.current = window.turnstile.render(`#${id}`, {
+ sitekey: siteKey,
+ theme,
+ callback: onToken,
+ })
+ } catch {
+ if (mounted) setFailed(true)
+ }
+ }
+
+ renderWidget()
+ return () => {
+ mounted = false
+ if (widgetId.current && window.turnstile) {
+ window.turnstile.remove(widgetId.current)
+ widgetId.current = ""
+ }
+ }
+ }, [id, onToken, siteKey, theme])
+
+ if (!siteKey) return null
+
+ return (
+
+
+ {failed && (
+
window.location.reload()}>
+ Reload challenge
+
+ )}
+
+ )
+}
diff --git a/frontend-next/src/components/ui/alert.tsx b/frontend-next/src/components/ui/alert.tsx
new file mode 100644
index 0000000..f99164e
--- /dev/null
+++ b/frontend-next/src/components/ui/alert.tsx
@@ -0,0 +1,66 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const alertVariants = cva(
+ "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
+ {
+ variants: {
+ variant: {
+ default: "bg-card text-card-foreground",
+ destructive:
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Alert({
+ className,
+ variant,
+ ...props
+}: React.ComponentProps<"div"> & VariantProps) {
+ return (
+
+ )
+}
+
+function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDescription({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export { Alert, AlertTitle, AlertDescription }
diff --git a/frontend-next/src/components/ui/badge.tsx b/frontend-next/src/components/ui/badge.tsx
new file mode 100644
index 0000000..6eb2a05
--- /dev/null
+++ b/frontend-next/src/components/ui/badge.tsx
@@ -0,0 +1,48 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+import { Slot } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
+ secondary:
+ "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
+ destructive:
+ "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
+ outline:
+ "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 [a&]:hover:underline",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Badge({
+ className,
+ variant = "default",
+ asChild = false,
+ ...props
+}: React.ComponentProps<"span"> &
+ VariantProps & { asChild?: boolean }) {
+ const Comp = asChild ? Slot.Root : "span"
+
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/frontend-next/src/components/ui/button.tsx b/frontend-next/src/components/ui/button.tsx
new file mode 100644
index 0000000..4d38506
--- /dev/null
+++ b/frontend-next/src/components/ui/button.tsx
@@ -0,0 +1,64 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+import { Slot } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ icon: "size-9",
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
+ "icon-sm": "size-8",
+ "icon-lg": "size-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+function Button({
+ className,
+ variant = "default",
+ size = "default",
+ asChild = false,
+ ...props
+}: React.ComponentProps<"button"> &
+ VariantProps & {
+ asChild?: boolean
+ }) {
+ const Comp = asChild ? Slot.Root : "button"
+
+ return (
+
+ )
+}
+
+export { Button, buttonVariants }
diff --git a/frontend-next/src/components/ui/card.tsx b/frontend-next/src/components/ui/card.tsx
new file mode 100644
index 0000000..acf57dc
--- /dev/null
+++ b/frontend-next/src/components/ui/card.tsx
@@ -0,0 +1,92 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Card({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+}
diff --git a/frontend-next/src/components/ui/checkbox.tsx b/frontend-next/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..98ac83e
--- /dev/null
+++ b/frontend-next/src/components/ui/checkbox.tsx
@@ -0,0 +1,30 @@
+import * as React from "react"
+import { CheckIcon } from "lucide-react"
+import { Checkbox as CheckboxPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Checkbox({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+ )
+}
+
+export { Checkbox }
diff --git a/frontend-next/src/components/ui/dialog.tsx b/frontend-next/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..84bdef4
--- /dev/null
+++ b/frontend-next/src/components/ui/dialog.tsx
@@ -0,0 +1,158 @@
+"use client"
+
+import * as React from "react"
+import { XIcon } from "lucide-react"
+import { Dialog as DialogPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+
+function Dialog({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogPortal({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogClose({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogOverlay({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DialogContent({
+ className,
+ children,
+ showCloseButton = true,
+ ...props
+}: React.ComponentProps & {
+ showCloseButton?: boolean
+}) {
+ return (
+
+
+
+ {children}
+ {showCloseButton && (
+
+
+ Close
+
+ )}
+
+
+ )
+}
+
+function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function DialogFooter({
+ className,
+ showCloseButton = false,
+ children,
+ ...props
+}: React.ComponentProps<"div"> & {
+ showCloseButton?: boolean
+}) {
+ return (
+
+ {children}
+ {showCloseButton && (
+
+ Close
+
+ )}
+
+ )
+}
+
+function DialogTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DialogDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogOverlay,
+ DialogPortal,
+ DialogTitle,
+ DialogTrigger,
+}
diff --git a/frontend-next/src/components/ui/dropdown-menu.tsx b/frontend-next/src/components/ui/dropdown-menu.tsx
new file mode 100644
index 0000000..492bef2
--- /dev/null
+++ b/frontend-next/src/components/ui/dropdown-menu.tsx
@@ -0,0 +1,255 @@
+import * as React from "react"
+import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
+import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function DropdownMenu({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DropdownMenuPortal({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuTrigger({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuContent({
+ className,
+ sideOffset = 4,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function DropdownMenuGroup({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuItem({
+ className,
+ inset,
+ variant = "default",
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+ variant?: "default" | "destructive"
+}) {
+ return (
+
+ )
+}
+
+function DropdownMenuCheckboxItem({
+ className,
+ children,
+ checked,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function DropdownMenuRadioGroup({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuRadioItem({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function DropdownMenuLabel({
+ className,
+ inset,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+}) {
+ return (
+
+ )
+}
+
+function DropdownMenuSeparator({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuShortcut({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+ )
+}
+
+function DropdownMenuSub({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DropdownMenuSubTrigger({
+ className,
+ inset,
+ children,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+}) {
+ return (
+
+ {children}
+
+
+ )
+}
+
+function DropdownMenuSubContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ DropdownMenu,
+ DropdownMenuPortal,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuLabel,
+ DropdownMenuItem,
+ DropdownMenuCheckboxItem,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubTrigger,
+ DropdownMenuSubContent,
+}
diff --git a/frontend-next/src/components/ui/input.tsx b/frontend-next/src/components/ui/input.tsx
new file mode 100644
index 0000000..f1124ae
--- /dev/null
+++ b/frontend-next/src/components/ui/input.tsx
@@ -0,0 +1,21 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Input({ className, type, ...props }: React.ComponentProps<"input">) {
+ return (
+
+ )
+}
+
+export { Input }
diff --git a/frontend-next/src/components/ui/label.tsx b/frontend-next/src/components/ui/label.tsx
new file mode 100644
index 0000000..f752f82
--- /dev/null
+++ b/frontend-next/src/components/ui/label.tsx
@@ -0,0 +1,22 @@
+import * as React from "react"
+import { Label as LabelPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Label({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Label }
diff --git a/frontend-next/src/components/ui/scroll-area.tsx b/frontend-next/src/components/ui/scroll-area.tsx
new file mode 100644
index 0000000..e38a492
--- /dev/null
+++ b/frontend-next/src/components/ui/scroll-area.tsx
@@ -0,0 +1,56 @@
+import * as React from "react"
+import { ScrollArea as ScrollAreaPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function ScrollArea({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+ {children}
+
+
+
+
+ )
+}
+
+function ScrollBar({
+ className,
+ orientation = "vertical",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+export { ScrollArea, ScrollBar }
diff --git a/frontend-next/src/components/ui/select.tsx b/frontend-next/src/components/ui/select.tsx
new file mode 100644
index 0000000..c0dc712
--- /dev/null
+++ b/frontend-next/src/components/ui/select.tsx
@@ -0,0 +1,190 @@
+"use client"
+
+import * as React from "react"
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
+import { Select as SelectPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Select({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SelectGroup({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SelectValue({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SelectTrigger({
+ className,
+ size = "default",
+ children,
+ ...props
+}: React.ComponentProps & {
+ size?: "sm" | "default"
+}) {
+ return (
+
+ {children}
+
+
+
+
+ )
+}
+
+function SelectContent({
+ className,
+ children,
+ position = "item-aligned",
+ align = "center",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ )
+}
+
+function SelectLabel({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function SelectItem({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function SelectSeparator({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function SelectScrollUpButton({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function SelectScrollDownButton({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+export {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectScrollDownButton,
+ SelectScrollUpButton,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
+}
diff --git a/frontend-next/src/components/ui/separator.tsx b/frontend-next/src/components/ui/separator.tsx
new file mode 100644
index 0000000..db91e97
--- /dev/null
+++ b/frontend-next/src/components/ui/separator.tsx
@@ -0,0 +1,26 @@
+import * as React from "react"
+import { Separator as SeparatorPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Separator({
+ className,
+ orientation = "horizontal",
+ decorative = true,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Separator }
diff --git a/frontend-next/src/components/ui/sheet.tsx b/frontend-next/src/components/ui/sheet.tsx
new file mode 100644
index 0000000..cb53bb2
--- /dev/null
+++ b/frontend-next/src/components/ui/sheet.tsx
@@ -0,0 +1,143 @@
+"use client"
+
+import * as React from "react"
+import { XIcon } from "lucide-react"
+import { Dialog as SheetPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Sheet({ ...props }: React.ComponentProps) {
+ return
+}
+
+function SheetTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SheetClose({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SheetPortal({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SheetOverlay({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function SheetContent({
+ className,
+ children,
+ side = "right",
+ showCloseButton = true,
+ ...props
+}: React.ComponentProps & {
+ side?: "top" | "right" | "bottom" | "left"
+ showCloseButton?: boolean
+}) {
+ return (
+
+
+
+ {children}
+ {showCloseButton && (
+
+
+ Close
+
+ )}
+
+
+ )
+}
+
+function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function SheetTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function SheetDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ Sheet,
+ SheetTrigger,
+ SheetClose,
+ SheetContent,
+ SheetHeader,
+ SheetFooter,
+ SheetTitle,
+ SheetDescription,
+}
diff --git a/frontend-next/src/components/ui/sonner.tsx b/frontend-next/src/components/ui/sonner.tsx
new file mode 100644
index 0000000..9f46e06
--- /dev/null
+++ b/frontend-next/src/components/ui/sonner.tsx
@@ -0,0 +1,38 @@
+import {
+ CircleCheckIcon,
+ InfoIcon,
+ Loader2Icon,
+ OctagonXIcon,
+ TriangleAlertIcon,
+} from "lucide-react"
+import { useTheme } from "next-themes"
+import { Toaster as Sonner, type ToasterProps } from "sonner"
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme()
+
+ return (
+ ,
+ info: ,
+ warning: ,
+ error: ,
+ loading: ,
+ }}
+ style={
+ {
+ "--normal-bg": "var(--popover)",
+ "--normal-text": "var(--popover-foreground)",
+ "--normal-border": "var(--border)",
+ "--border-radius": "var(--radius)",
+ } as React.CSSProperties
+ }
+ {...props}
+ />
+ )
+}
+
+export { Toaster }
diff --git a/frontend-next/src/components/ui/switch.tsx b/frontend-next/src/components/ui/switch.tsx
new file mode 100644
index 0000000..8baa844
--- /dev/null
+++ b/frontend-next/src/components/ui/switch.tsx
@@ -0,0 +1,35 @@
+"use client"
+
+import * as React from "react"
+import { Switch as SwitchPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Switch({
+ className,
+ size = "default",
+ ...props
+}: React.ComponentProps & {
+ size?: "sm" | "default"
+}) {
+ return (
+
+
+
+ )
+}
+
+export { Switch }
diff --git a/frontend-next/src/components/ui/tabs.tsx b/frontend-next/src/components/ui/tabs.tsx
new file mode 100644
index 0000000..b463afd
--- /dev/null
+++ b/frontend-next/src/components/ui/tabs.tsx
@@ -0,0 +1,91 @@
+"use client"
+
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+import { Tabs as TabsPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Tabs({
+ className,
+ orientation = "horizontal",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+const tabsListVariants = cva(
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
+ {
+ variants: {
+ variant: {
+ default: "bg-muted",
+ line: "gap-1 bg-transparent",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function TabsList({
+ className,
+ variant = "default",
+ ...props
+}: React.ComponentProps &
+ VariantProps) {
+ return (
+
+ )
+}
+
+function TabsTrigger({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function TabsContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
diff --git a/frontend-next/src/components/ui/textarea.tsx b/frontend-next/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..e67d8fe
--- /dev/null
+++ b/frontend-next/src/components/ui/textarea.tsx
@@ -0,0 +1,18 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
+ return (
+
+ )
+}
+
+export { Textarea }
diff --git a/frontend-next/src/index.css b/frontend-next/src/index.css
new file mode 100644
index 0000000..8077d3b
--- /dev/null
+++ b/frontend-next/src/index.css
@@ -0,0 +1,1052 @@
+@import "tailwindcss";
+
+@theme {
+ --font-sans: "Geist", "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif;
+ --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+ --color-background: hsl(var(--background));
+ --color-foreground: hsl(var(--foreground));
+ --color-card: hsl(var(--card));
+ --color-card-foreground: hsl(var(--card-foreground));
+ --color-popover: hsl(var(--popover));
+ --color-popover-foreground: hsl(var(--popover-foreground));
+ --color-primary: hsl(var(--primary));
+ --color-primary-foreground: hsl(var(--primary-foreground));
+ --color-secondary: hsl(var(--secondary));
+ --color-secondary-foreground: hsl(var(--secondary-foreground));
+ --color-muted: hsl(var(--muted));
+ --color-muted-foreground: hsl(var(--muted-foreground));
+ --color-accent: hsl(var(--accent));
+ --color-accent-foreground: hsl(var(--accent-foreground));
+ --color-destructive: hsl(var(--destructive));
+ --color-border: hsl(var(--border));
+ --color-input: hsl(var(--input));
+ --color-ring: hsl(var(--ring));
+}
+
+:root {
+ --background: 210 17% 96%;
+ --foreground: 220 13% 10%;
+ --card: 0 0% 100%;
+ --card-foreground: 220 13% 10%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 220 13% 10%;
+ --primary: 160 84% 30%;
+ --primary-foreground: 0 0% 100%;
+ --secondary: 210 14% 94%;
+ --secondary-foreground: 220 13% 16%;
+ --muted: 210 14% 94%;
+ --muted-foreground: 215 12% 43%;
+ --accent: 154 56% 91%;
+ --accent-foreground: 160 84% 25%;
+ --destructive: 0 72% 51%;
+ --border: 214 15% 88%;
+ --input: 214 15% 88%;
+ --ring: 160 84% 35%;
+}
+
+.style-b {
+ --background: 44 27% 94%;
+ --foreground: 40 15% 12%;
+ --card: 0 0% 100%;
+ --card-foreground: 40 15% 12%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 40 15% 12%;
+ --primary: 142 72% 35%;
+ --primary-foreground: 0 0% 100%;
+ --secondary: 38 30% 93%;
+ --secondary-foreground: 40 15% 18%;
+ --muted: 38 30% 93%;
+ --muted-foreground: 37 10% 39%;
+ --accent: 137 55% 90%;
+ --accent-foreground: 142 72% 29%;
+ --destructive: 6 73% 59%;
+ --border: 37 24% 90%;
+ --input: 37 24% 90%;
+ --ring: 142 72% 35%;
+}
+
+.dark {
+ --background: 210 16% 6%;
+ --foreground: 210 20% 92%;
+ --card: 210 14% 9%;
+ --card-foreground: 210 20% 92%;
+ --popover: 210 14% 9%;
+ --popover-foreground: 210 20% 92%;
+ --primary: 158 64% 52%;
+ --primary-foreground: 160 80% 8%;
+ --secondary: 210 12% 13%;
+ --secondary-foreground: 210 20% 88%;
+ --muted: 210 12% 13%;
+ --muted-foreground: 214 12% 64%;
+ --accent: 160 64% 12%;
+ --accent-foreground: 158 64% 58%;
+ --destructive: 0 84% 65%;
+ --border: 214 12% 18%;
+ --input: 214 12% 18%;
+ --ring: 158 64% 52%;
+ --color-background: hsl(var(--background));
+ --color-foreground: hsl(var(--foreground));
+ --color-card: hsl(var(--card));
+ --color-card-foreground: hsl(var(--card-foreground));
+ --color-popover: hsl(var(--popover));
+ --color-popover-foreground: hsl(var(--popover-foreground));
+ --color-primary: hsl(var(--primary));
+ --color-primary-foreground: hsl(var(--primary-foreground));
+ --color-secondary: hsl(var(--secondary));
+ --color-secondary-foreground: hsl(var(--secondary-foreground));
+ --color-muted: hsl(var(--muted));
+ --color-muted-foreground: hsl(var(--muted-foreground));
+ --color-accent: hsl(var(--accent));
+ --color-accent-foreground: hsl(var(--accent-foreground));
+ --color-destructive: hsl(var(--destructive));
+ --color-border: hsl(var(--border));
+ --color-input: hsl(var(--input));
+ --color-ring: hsl(var(--ring));
+}
+
+.style-b.dark {
+ --background: 30 20% 8%;
+ --foreground: 38 35% 93%;
+ --card: 30 16% 12%;
+ --card-foreground: 38 35% 93%;
+ --popover: 30 16% 12%;
+ --popover-foreground: 38 35% 93%;
+ --primary: 135 63% 51%;
+ --primary-foreground: 141 57% 5%;
+ --secondary: 31 16% 15%;
+ --secondary-foreground: 38 25% 86%;
+ --muted: 31 16% 15%;
+ --muted-foreground: 35 13% 66%;
+ --accent: 136 58% 12%;
+ --accent-foreground: 135 63% 55%;
+ --destructive: 3 83% 71%;
+ --border: 31 19% 16%;
+ --input: 31 19% 16%;
+ --ring: 135 63% 51%;
+}
+
+.style-b,
+.style-b.dark {
+ --color-background: hsl(var(--background));
+ --color-foreground: hsl(var(--foreground));
+ --color-card: hsl(var(--card));
+ --color-card-foreground: hsl(var(--card-foreground));
+ --color-popover: hsl(var(--popover));
+ --color-popover-foreground: hsl(var(--popover-foreground));
+ --color-primary: hsl(var(--primary));
+ --color-primary-foreground: hsl(var(--primary-foreground));
+ --color-secondary: hsl(var(--secondary));
+ --color-secondary-foreground: hsl(var(--secondary-foreground));
+ --color-muted: hsl(var(--muted));
+ --color-muted-foreground: hsl(var(--muted-foreground));
+ --color-accent: hsl(var(--accent));
+ --color-accent-foreground: hsl(var(--accent-foreground));
+ --color-destructive: hsl(var(--destructive));
+ --color-border: hsl(var(--border));
+ --color-input: hsl(var(--input));
+ --color-ring: hsl(var(--ring));
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-width: 320px;
+ background: hsl(var(--background));
+ font-family: var(--font-sans);
+ -webkit-font-smoothing: antialiased;
+}
+
+.notice-toast {
+ position: fixed;
+ top: 14px;
+ right: 14px;
+ z-index: 50;
+ width: min(360px, calc(100vw - 28px));
+ background: hsl(var(--card));
+ box-shadow: 0 18px 40px -24px rgb(24 22 16 / 55%);
+}
+
+button,
+input,
+textarea {
+ font: inherit;
+}
+
+.mail-shell {
+ display: flex;
+ height: 100vh;
+ overflow: hidden;
+}
+
+.auth-shell {
+ display: flex;
+ height: 100vh;
+ overflow: hidden;
+ background:
+ linear-gradient(90deg, color-mix(in srgb, hsl(var(--foreground)) 4%, transparent) 1px, transparent 1px),
+ linear-gradient(180deg, color-mix(in srgb, hsl(var(--foreground)) 4%, transparent) 1px, transparent 1px),
+ linear-gradient(135deg, color-mix(in srgb, hsl(var(--accent)) 58%, transparent), transparent 42%),
+ hsl(var(--background));
+ background-size: 42px 42px, 42px 42px, auto;
+}
+
+.auth-sidebar {
+ background: color-mix(in srgb, hsl(var(--card)) 88%, hsl(var(--background)) 12%);
+}
+
+.auth-main {
+ display: flex;
+ min-width: 0;
+ flex: 1;
+ align-items: center;
+ justify-content: center;
+ padding: 34px;
+}
+
+.auth-stage {
+ display: grid;
+ width: min(100%, 1060px);
+ grid-template-columns: minmax(420px, 1fr) 320px;
+ gap: 18px;
+ align-items: stretch;
+}
+
+.auth-card {
+ border: 1px solid hsl(var(--border));
+ border-radius: 8px;
+ background:
+ linear-gradient(180deg, color-mix(in srgb, hsl(var(--card)) 94%, white 6%), hsl(var(--card)));
+ box-shadow: 0 22px 70px -28px rgb(24 22 16 / 38%);
+ padding: 28px;
+}
+
+.dark .auth-card {
+ background:
+ linear-gradient(180deg, color-mix(in srgb, hsl(var(--card)) 94%, white 3%), hsl(var(--card)));
+}
+
+.auth-card-head {
+ display: grid;
+ gap: 16px;
+ margin-bottom: 24px;
+}
+
+.auth-card-head h1 {
+ margin: 0;
+ max-width: 520px;
+ font-size: 54px;
+ font-weight: 900;
+ line-height: .95;
+ letter-spacing: 0;
+}
+
+.auth-card-head p,
+.context-card p,
+.auth-note p {
+ margin: 8px 0 0;
+ color: hsl(var(--muted-foreground));
+ font-size: 14px;
+ line-height: 1.55;
+}
+
+.auth-access {
+ margin-bottom: 14px;
+}
+
+.auth-tabs {
+ display: none;
+}
+
+.auth-form {
+ display: grid;
+ gap: 14px;
+}
+
+.auth-form label {
+ color: hsl(var(--muted-foreground));
+ font-size: 12.5px;
+ font-weight: 700;
+}
+
+.address-builder {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 10px;
+ border: 1px solid hsl(var(--border));
+ border-radius: 8px;
+ background: color-mix(in srgb, hsl(var(--background)) 82%, hsl(var(--card)) 18%);
+ padding: 10px 12px;
+ min-height: 58px;
+}
+
+.address-builder input {
+ border: 0;
+ background: transparent;
+ padding: 0;
+ height: auto;
+ min-width: 90px;
+ font-size: 18px;
+ font-weight: 800;
+ box-shadow: none;
+}
+
+.address-builder span {
+ color: hsl(var(--muted-foreground));
+ font-size: 14px;
+ font-weight: 700;
+}
+
+.address-builder select {
+ min-width: 98px;
+ border: 0;
+ background: transparent;
+ color: hsl(var(--foreground));
+ outline: none;
+ font-weight: 700;
+}
+
+.auth-inline-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.auth-submit {
+ min-height: 46px;
+ font-weight: 800;
+}
+
+.auth-submit:disabled {
+ background: hsl(var(--muted));
+ color: hsl(var(--muted-foreground));
+ opacity: 1;
+}
+
+.auth-context {
+ display: grid;
+ gap: 12px;
+ align-content: start;
+}
+
+.context-card {
+ gap: 12px;
+ padding-block: 20px;
+ border-radius: 8px;
+ background: color-mix(in srgb, hsl(var(--card)) 88%, hsl(var(--background)) 12%);
+ box-shadow: 0 18px 42px -32px rgb(24 22 16 / 42%);
+}
+
+.context-card [data-slot="card-header"] {
+ padding: 0 24px;
+}
+
+.context-card [data-slot="card-title"] {
+ font-size: 15px;
+ font-weight: 850;
+}
+
+.context-card [data-slot="card-content"] {
+ display: grid;
+ gap: 14px;
+ padding: 0 24px;
+}
+
+.context-row {
+ display: flex;
+ min-width: 0;
+ align-items: flex-start;
+ gap: 12px;
+}
+
+.context-row strong {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-size: 14px;
+}
+
+.auth-note {
+ display: flex;
+ gap: 10px;
+ border: 1px dashed hsl(var(--border));
+ border-radius: 8px;
+ background: color-mix(in srgb, hsl(var(--background)) 76%, transparent);
+ padding: 13px 14px;
+}
+
+.auth-note span {
+ margin-top: 7px;
+ width: 8px;
+ height: 8px;
+ flex: 0 0 8px;
+ border-radius: 999px;
+ background: hsl(var(--primary));
+}
+
+.auth-note p {
+ margin: 0;
+ font-size: 12.5px;
+}
+
+.credential-input {
+ min-height: 116px;
+ resize: vertical;
+ border: 1px solid hsl(var(--border));
+ border-radius: 8px;
+ background: hsl(var(--background));
+ color: hsl(var(--foreground));
+ outline: none;
+ padding: 11px 12px;
+}
+
+.checkbox-line,
+.compose-html {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: hsl(var(--muted-foreground));
+ font-size: 13px;
+ font-weight: 700;
+}
+
+.turnstile-box {
+ display: grid;
+ gap: 8px;
+ justify-items: start;
+ min-height: 0;
+}
+
+.verify-row {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ gap: 8px;
+}
+
+.mail-sidebar {
+ display: flex;
+ width: 236px;
+ flex: 0 0 236px;
+ flex-direction: column;
+ border-right: 1px solid hsl(var(--border));
+ background: hsl(var(--card));
+ padding: 14px 12px;
+ transition: width .18s ease, flex-basis .18s ease;
+}
+
+.mail-sidebar.collapsed {
+ width: 68px;
+ flex-basis: 68px;
+ padding-inline: 10px;
+}
+
+.mail-sidebar.collapsed .sidebar-label {
+ display: none;
+}
+
+.mail-sidebar.collapsed .sidebar-brand,
+.mail-sidebar.collapsed .nav-item,
+.mail-sidebar.collapsed > button {
+ justify-content: center;
+}
+
+.mail-sidebar.collapsed .sidebar-session {
+ padding-inline: 8px;
+}
+
+.mail-sidebar.collapsed .sidebar-brand .brand-mark {
+ display: none;
+}
+
+.sidebar-brand {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 6px 8px 14px;
+ font-weight: 800;
+}
+
+.sidebar-toggle {
+ margin-left: auto;
+ flex: 0 0 auto;
+}
+
+.mail-sidebar.collapsed .sidebar-toggle {
+ margin: 0 auto;
+}
+
+.brand-mark,
+.address-icon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 8px;
+ background: hsl(var(--primary));
+ color: hsl(var(--primary-foreground));
+}
+
+.brand-mark {
+ width: 32px;
+ height: 32px;
+}
+
+.sidebar-nav {
+ display: flex;
+ min-height: 0;
+ flex: 1;
+ flex-direction: column;
+ gap: 3px;
+ overflow: auto;
+}
+
+.nav-item {
+ display: flex;
+ width: 100%;
+ align-items: center;
+ gap: 11px;
+ border: 0;
+ border-radius: 8px;
+ background: transparent;
+ color: hsl(var(--muted-foreground));
+ cursor: pointer;
+ padding: 9px 11px;
+ text-align: left;
+ font-size: 13.5px;
+ font-weight: 650;
+}
+
+.nav-item.active,
+.nav-item:hover {
+ background: hsl(var(--accent));
+ color: hsl(var(--accent-foreground));
+}
+
+.sidebar-session {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ border: 1px solid hsl(var(--border));
+ border-radius: 8px;
+ background: hsl(var(--background));
+ padding: 9px 10px;
+}
+
+.session-avatar {
+ display: inline-flex;
+ width: 30px;
+ height: 30px;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ background: hsl(var(--accent));
+ color: hsl(var(--accent-foreground));
+ font-size: 12px;
+ font-weight: 800;
+}
+
+.mail-main {
+ display: flex;
+ min-width: 0;
+ flex: 1;
+ flex-direction: column;
+}
+
+.mail-topbar {
+ display: flex;
+ height: 62px;
+ flex: 0 0 62px;
+ align-items: center;
+ gap: 14px;
+ border-bottom: 1px solid hsl(var(--border));
+ background: hsl(var(--card));
+ padding: 0 18px;
+}
+
+.address-pill {
+ display: flex;
+ min-width: 220px;
+ max-width: 360px;
+ flex: 1;
+ align-items: center;
+ gap: 9px;
+ border: 1px solid hsl(var(--border));
+ border-radius: 999px;
+ background: hsl(var(--background));
+ padding: 0 6px 0 14px;
+ height: 40px;
+}
+
+.status-dot,
+.unread-dot {
+ border-radius: 999px;
+ background: hsl(var(--primary));
+}
+
+.status-dot {
+ width: 7px;
+ height: 7px;
+ box-shadow: 0 0 0 3px hsl(var(--accent));
+}
+
+.mail-search {
+ display: flex;
+ width: 248px;
+ align-items: center;
+ gap: 8px;
+ border: 1px solid hsl(var(--border));
+ border-radius: 8px;
+ background: hsl(var(--background));
+ padding: 0 13px;
+ height: 40px;
+}
+
+.mail-search input {
+ border: 0;
+ background: transparent;
+ padding: 0;
+ height: auto;
+}
+
+.inbox-grid {
+ display: flex;
+ min-height: 0;
+ flex: 1;
+ overflow: hidden;
+}
+
+.mail-list {
+ display: flex;
+ width: 372px;
+ flex: 0 0 372px;
+ flex-direction: column;
+ border-right: 1px solid hsl(var(--border));
+ background: hsl(var(--card));
+}
+
+.list-heading {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ border-bottom: 1px solid hsl(var(--border));
+ padding: 15px 18px 13px;
+}
+
+.list-actions {
+ margin-left: auto;
+ display: flex;
+ gap: 2px;
+}
+
+.list-heading h2 {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 800;
+}
+
+.list-heading span {
+ color: hsl(var(--muted-foreground));
+ font-size: 12px;
+ font-weight: 650;
+}
+
+.list-scroll {
+ min-height: 0;
+ flex: 1;
+ overflow-y: auto;
+}
+
+.empty-state {
+ padding: 24px 18px;
+ color: hsl(var(--muted-foreground));
+ font-size: 13px;
+}
+
+.mail-row {
+ display: flex;
+ width: 100%;
+ align-items: flex-start;
+ gap: 12px;
+ border: 0;
+ border-bottom: 1px solid hsl(var(--border));
+ background: transparent;
+ color: inherit;
+ cursor: pointer;
+ padding: 13px 16px;
+ text-align: left;
+}
+
+.mail-row.selected,
+.mail-row:hover {
+ background: hsl(var(--accent));
+}
+
+.sender-avatar {
+ display: inline-flex;
+ width: 38px;
+ height: 38px;
+ flex: 0 0 38px;
+ align-items: center;
+ justify-content: center;
+ border-radius: 8px;
+ background: hsl(var(--primary));
+ color: hsl(var(--primary-foreground));
+ font-size: 13px;
+ font-weight: 800;
+}
+
+.sender-avatar.large {
+ width: 46px;
+ height: 46px;
+ flex-basis: 46px;
+ font-size: 16px;
+}
+
+.mail-row-top {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 8px;
+ font-size: 13.5px;
+}
+
+.time {
+ margin-left: auto;
+ flex: 0 0 auto;
+ color: hsl(var(--muted-foreground));
+ font-size: 11.5px;
+ font-weight: 650;
+}
+
+.subject,
+.preview {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.subject {
+ margin: 2px 0;
+ font-size: 13px;
+ font-weight: 650;
+}
+
+.preview {
+ color: hsl(var(--muted-foreground));
+ font-size: 12.5px;
+}
+
+.unread-dot {
+ margin-top: 6px;
+ width: 8px;
+ height: 8px;
+ flex: 0 0 8px;
+}
+
+.reader {
+ display: flex;
+ min-width: 0;
+ flex: 1;
+ flex-direction: column;
+}
+
+.empty-reader {
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ color: hsl(var(--muted-foreground));
+ font-weight: 700;
+}
+
+.reader-toolbar {
+ display: flex;
+ height: 56px;
+ flex: 0 0 56px;
+ align-items: center;
+ justify-content: space-between;
+ border-bottom: 1px solid hsl(var(--border));
+ padding: 0 16px;
+}
+
+.reader-body {
+ min-height: 0;
+ overflow-y: auto;
+ padding: 34px 32px 60px;
+}
+
+.reader-meta {
+ display: flex;
+ max-width: 680px;
+ align-items: flex-start;
+ gap: 14px;
+ margin: 0 auto 22px;
+}
+
+.reader-meta h1,
+.page-panel h1 {
+ margin: 0;
+ font-size: 24px;
+ font-weight: 850;
+ line-height: 1.25;
+}
+
+.reader-meta p,
+.page-panel p {
+ margin: 6px 0 0;
+ color: hsl(var(--muted-foreground));
+ font-size: 13px;
+}
+
+.message-card {
+ max-width: 680px;
+ margin: 0 auto;
+}
+
+.message-frame {
+ width: 100%;
+ min-height: 420px;
+ border: 0;
+ border-radius: 8px;
+ background: #fff;
+}
+
+.message-text {
+ white-space: pre-wrap;
+ word-break: break-word;
+ margin: 0;
+ color: hsl(var(--muted-foreground));
+ font: inherit;
+ line-height: 1.65;
+}
+
+.code-box {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ flex-wrap: wrap;
+ margin: 22px 0 14px;
+ border: 1px dashed hsl(var(--border));
+ border-radius: 8px;
+ background: hsl(var(--background));
+ padding: 18px 22px;
+}
+
+.code-box span {
+ font-family: var(--font-mono);
+ font-size: 32px;
+ font-weight: 700;
+ letter-spacing: 9px;
+}
+
+.page-panel {
+ height: 100%;
+ overflow-y: auto;
+ padding: 36px 32px 60px;
+}
+
+.compose-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ border-bottom: 1px solid hsl(var(--border));
+ padding: 10px 18px;
+}
+
+.compose-row span:first-child {
+ width: 54px;
+ color: hsl(var(--muted-foreground));
+ font-size: 13px;
+ font-weight: 700;
+}
+
+.compose-body {
+ min-height: 240px;
+ width: 100%;
+ resize: vertical;
+ border: 0;
+ background: transparent;
+ color: inherit;
+ outline: none;
+ padding: 18px;
+ line-height: 1.6;
+}
+
+.compose-html {
+ padding: 12px 18px 0;
+}
+
+.compose-actions {
+ display: flex;
+ justify-content: space-between;
+ border-top: 1px solid hsl(var(--border));
+ background: hsl(var(--background));
+ padding: 14px 18px;
+}
+
+.address-card {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ border: 1px solid hsl(var(--border));
+ border-radius: 12px;
+ background: hsl(var(--card));
+ color: inherit;
+ cursor: pointer;
+ padding: 16px;
+}
+
+.address-card.active {
+ border-color: hsl(var(--primary));
+}
+
+.address-section-title {
+ color: hsl(var(--muted-foreground));
+ font-size: 12px;
+ font-weight: 800;
+ text-transform: uppercase;
+}
+
+.address-icon {
+ width: 42px;
+ height: 42px;
+ background: hsl(var(--accent));
+ color: hsl(var(--accent-foreground));
+}
+
+.settings-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 14px;
+}
+
+.settings-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+@media (max-width: 1080px) {
+ .auth-stage {
+ grid-template-columns: 1fr;
+ max-width: 620px;
+ }
+
+ .auth-context {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .auth-note {
+ grid-column: 1 / -1;
+ }
+
+ .auth-card-head h1 {
+ font-size: 42px;
+ }
+}
+
+@media (max-width: 700px) {
+ .notice-toast {
+ top: auto;
+ right: 12px;
+ bottom: 12px;
+ width: calc(100vw - 24px);
+ }
+
+ .mail-shell {
+ display: block;
+ height: auto;
+ overflow: visible;
+ }
+
+ .mail-sidebar {
+ display: none;
+ }
+
+ .mail-main {
+ min-height: 100vh;
+ }
+
+ .mail-topbar {
+ height: auto;
+ flex-wrap: wrap;
+ padding: 12px;
+ }
+
+ .address-pill {
+ max-width: none;
+ flex-basis: 100%;
+ }
+
+ .mail-search {
+ width: 100%;
+ }
+
+ .inbox-grid {
+ display: block;
+ overflow: visible;
+ }
+
+ .mail-list {
+ width: auto;
+ border-right: 0;
+ }
+
+ .reader {
+ display: none;
+ }
+
+ .page-panel {
+ height: auto;
+ padding: 24px 16px 48px;
+ }
+
+ .auth-shell {
+ display: block;
+ height: auto;
+ min-height: 100vh;
+ overflow: visible;
+ }
+
+ .auth-main {
+ align-items: flex-start;
+ min-height: 100vh;
+ padding: 18px 12px;
+ }
+
+ .auth-stage,
+ .auth-context {
+ grid-template-columns: 1fr;
+ }
+
+ .auth-card {
+ padding: 20px;
+ }
+
+ .auth-card-head h1 {
+ font-size: 34px;
+ }
+
+ .address-builder {
+ align-items: stretch;
+ flex-wrap: wrap;
+ }
+
+ .address-builder input {
+ flex: 1 1 160px;
+ }
+
+ .auth-tabs {
+ display: flex;
+ }
+
+ .compose-row {
+ align-items: stretch;
+ flex-direction: column;
+ }
+}
diff --git a/frontend-next/src/lib/utils.ts b/frontend-next/src/lib/utils.ts
new file mode 100644
index 0000000..bd0c391
--- /dev/null
+++ b/frontend-next/src/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/frontend-next/src/main.tsx b/frontend-next/src/main.tsx
new file mode 100644
index 0000000..5f5c5f3
--- /dev/null
+++ b/frontend-next/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from "react"
+import { createRoot } from "react-dom/client"
+import App from "./App"
+import "./index.css"
+
+createRoot(document.getElementById("root")!).render(
+
+
+ ,
+)
diff --git a/frontend-next/src/vite-env.d.ts b/frontend-next/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/frontend-next/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/frontend-next/tsconfig.app.json b/frontend-next/tsconfig.app.json
new file mode 100644
index 0000000..9edfec6
--- /dev/null
+++ b/frontend-next/tsconfig.app.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["src"]
+}
diff --git a/frontend-next/tsconfig.json b/frontend-next/tsconfig.json
new file mode 100644
index 0000000..1ffef60
--- /dev/null
+++ b/frontend-next/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/frontend-next/tsconfig.node.json b/frontend-next/tsconfig.node.json
new file mode 100644
index 0000000..be51a69
--- /dev/null
+++ b/frontend-next/tsconfig.node.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "Bundler",
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "noEmit": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/frontend-next/vite.config.ts b/frontend-next/vite.config.ts
new file mode 100644
index 0000000..3ccad23
--- /dev/null
+++ b/frontend-next/vite.config.ts
@@ -0,0 +1,21 @@
+import { defineConfig } from "vite"
+import react from "@vitejs/plugin-react"
+import tailwindcss from "@tailwindcss/vite"
+import path from "node:path"
+
+export default defineConfig({
+ plugins: [react(), tailwindcss()],
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ },
+ },
+ server: {
+ proxy: {
+ "/api": "http://127.0.0.1:8787",
+ "/open_api": "http://127.0.0.1:8787",
+ "/user_api": "http://127.0.0.1:8787",
+ "/telegram": "http://127.0.0.1:8787",
+ },
+ },
+})
diff --git a/frontend/src/i18n/locales/source/de.ts b/frontend/src/i18n/locales/source/de.ts
index b87be8f..c555ce5 100644
--- a/frontend/src/i18n/locales/source/de.ts
+++ b/frontend/src/i18n/locales/source/de.ts
@@ -5,10 +5,10 @@ export const deMessages = {
"views.Index.about": "Über",
"views.Admin.about": "Über",
"views.Header.accessHeader": "Zugangspasswort",
- "views.Admin.account": "Konto",
- "views.Index.accountSettings": "Kontoeinstellungen",
- "views.Admin.account_settings": "Kontoeinstellungen",
- "views.index.SimpleIndex.accountSettings": "Kontoeinstellungen",
+ "views.Admin.account": "E-Mail-Adressen",
+ "views.Index.accountSettings": "E-Mail-Adress-Einstellungen",
+ "views.Admin.account_settings": "E-Mail-Adress-Einstellungen",
+ "views.index.SimpleIndex.accountSettings": "E-Mail-Adress-Einstellungen",
"views.index.Attachment.action": "Aktion",
"views.admin.SenderAccess.action": "Aktion",
"views.user.UserSettings.actions": "Aktionen",
@@ -60,13 +60,13 @@ export const deMessages = {
"views.index.Attachment.deleteConfirm": "Möchtest du wirklich diesen Anhang löschen?",
"views.admin.Account.deleteTip": "Möchtest du wirklich diese E-Mail löschen?",
"views.admin.SenderAccess.deleteTip": "Möchtest du dies wirklich löschen?",
- "views.index.AccountSettings.deleteAccountConfirm": "Möchtest du wirklich dein Konto und alle zugehörigen E-Mails löschen?",
+ "views.index.AccountSettings.deleteAccountConfirm": "Möchtest du diese E-Mail-Adresse und alle zugehörigen E-Mails wirklich löschen?",
"views.index.AccountSettings.logoutConfirm": "Möchtest du dich wirklich abmelden?",
"components.MailBox.deleteMailTip": "Möchtest du die E-Mail wirklich löschen?",
"components.MailContentRenderer.deleteMailTip": "Möchtest du die E-Mail wirklich löschen?",
"components.SendBox.deleteMailTip": "Möchtest du die E-Mail wirklich löschen?",
"views.admin.AccountSettings.delete_rule_confirm": "Möchtest du diese Regel wirklich löschen?",
- "views.admin.UserManagement.deleteUserTip": "Möchtest du diesen Benutzer wirklich löschen?",
+ "views.admin.UserManagement.deleteUserTip": "Möchtest du dieses Benutzerkonto wirklich löschen?",
"views.Admin.logoutConfirmContent": "Möchtest du dich wirklich aus dem Admin-Bereich abmelden?",
"views.user.UserSettings.logoutConfirm": "Möchtest du dich wirklich abmelden?",
"views.admin.IpBlacklistSettings.asn_blacklist": "ASN-Organisationssperrliste",
@@ -110,7 +110,7 @@ export const deMessages = {
"views.admin.Maintenance.inactiveAddressLabel": "Inaktive Adressen löschen, die älter als n Tage sind",
"views.admin.Maintenance.mailBoxLabel": "Posteingänge löschen, die älter als n Tage sind",
"views.admin.Maintenance.sendBoxLabel": "Postausgänge löschen, die älter als n Tage sind",
- "views.admin.Maintenance.unboundAddressLabel": "Nicht verknüpfte Adressen löschen, die älter als n Tage sind",
+ "views.admin.Maintenance.unboundAddressLabel": "Seit n Tagen nicht verknüpfte E-Mail-Adressen löschen",
"views.admin.Maintenance.mailUnknowLabel": "E-Mails mit unbekanntem Empfänger löschen, die älter als n Tage sind",
"views.index.AccountSettings.clearInbox": "Posteingang leeren",
"views.admin.Account.clearInbox": "Posteingang leeren",
@@ -134,20 +134,20 @@ export const deMessages = {
"views.index.SimpleIndex.copyAddress": "Kopieren",
"components.AiExtractInfo.copyFailed": "Kopieren fehlgeschlagen",
"views.Footer.copyright": "Urheberrecht",
- "views.Admin.account_create": "Konto erstellen",
+ "views.Admin.account_create": "E-Mail-Adresse erstellen",
"views.admin.CreateAccount.creatNewEmail": "Neue E-Mail erstellen",
"views.common.Login.getNewEmail": "Neue E-Mail erstellen",
"views.user.AddressManagement.create_or_bind": "Erstellen oder verknüpfen",
"views.index.LocalAddress.create_or_bind": "Erstellen oder verknüpfen",
"views.user.UserSettings.createPasskey": "Passkey erstellen",
- "views.admin.UserManagement.createUser": "Benutzer erstellen",
+ "views.admin.UserManagement.createUser": "Benutzerkonto erstellen",
"views.user.UserSettings.created_at": "Erstellt am",
"views.admin.Account.created_at": "Erstellt am",
"views.admin.SenderAccess.created_at": "Erstellt am",
"views.admin.UserManagement.created_at": "Erstellt am",
"views.common.Login.credentialLogin": "Mit Zugangsdaten anmelden",
"views.admin.DatabaseManager.current_db_version": "Aktuelle DB-Version",
- "views.user.UserBar.currentUser": "Aktuell angemeldeter Benutzer",
+ "views.user.UserBar.currentUser": "Aktuelles Benutzerkonto",
"views.admin.UserManagement.roleDonotExist": "Die aktuelle Rolle existiert nicht",
"views.admin.Maintenance.customSqlCleanup": "Benutzerdefinierte SQL-Bereinigung",
"views.admin.AccountSettings.send_mail_daily_limit": "Tageslimit",
@@ -170,11 +170,11 @@ export const deMessages = {
"views.admin.UserManagement.delete": "Löschen",
"views.admin.AccountSettings.delete_rule": "Löschen",
"views.admin.Maintenance.deleteCustomSql": "Löschen",
- "views.index.AccountSettings.deleteAccount": "Konto löschen",
- "views.admin.Account.deleteAccount": "Konto löschen",
+ "views.index.AccountSettings.deleteAccount": "E-Mail-Adresse löschen",
+ "views.admin.Account.deleteAccount": "E-Mail-Adresse löschen",
"views.user.UserSettings.deletePasskey": "Passkey löschen",
"views.admin.AccountSettings.delete_success": "Erfolgreich gelöscht",
- "views.admin.UserManagement.deleteUser": "Benutzer löschen",
+ "views.admin.UserManagement.deleteUser": "Benutzerkonto löschen",
"views.index.Attachment.deleteSuccess": "Erfolgreich gelöscht",
"views.admin.SenderAccess.disable": "Deaktivieren",
"views.Admin.loginViaDisabledCheck": "Passwortprüfung deaktiviert",
@@ -207,7 +207,7 @@ export const deMessages = {
"views.admin.Telegram.enable": "Aktivieren",
"views.admin.UserSettings.enable": "Aktivieren",
"views.admin.AiExtractSettings.enableAllowList": "Aktivieren Adressfreigabeliste",
- "views.admin.Webhook.enableAllowList": "Freigabeliste aktivieren (Webhook-Zugriff auf bestimmte Benutzer beschränken)",
+ "views.admin.Webhook.enableAllowList": "Freigabeliste aktivieren (Webhook-Zugriff auf bestimmte E-Mail-Adressen beschränken)",
"views.index.AutoReply.enableAutoReply": "Automatische Antwort aktivieren",
"views.admin.Maintenance.cronTip": "Um die Cron-Bereinigung zu aktivieren, konfiguriere [crons] im Worker. Siehe Dokumentation; 0 Tage bedeutet alles löschen.",
"views.admin.IpBlacklistSettings.enable_daily_limit": "Aktivieren Tägliches Anfrage-Limit",
@@ -334,7 +334,7 @@ export const deMessages = {
"views.admin.AccountSettings.noLimitSendAddressList": "Adressliste ohne Guthabenlimit",
"views.index.SimpleIndex.noMails": "Keine E-Mails gefunden",
"views.admin.RoleAddressConfig.noRolesAvailable": "In der Systemkonfiguration sind keine Rollen verfügbar",
- "views.index.SendMail.requestAccessTip": "Noch kein Sendeguthaben vorhanden. Wenn der Administrator ein Standardguthaben aktiviert hat, wird es automatisch zugewiesen; andernfalls Zugriff anfordern oder den Administrator kontaktieren.",
+ "views.index.SendMail.requestAccessTip": "Sendezugang und Guthaben gehören zur aktuellen E-Mail-Adresse, nicht zum Benutzerkonto. Für diese Adresse Zugriff anfordern oder den Administrator kontaktieren.",
"components.SendBox.emptySent": "Keine gesendeten E-Mails",
"views.admin.RoleAddressConfig.notConfigured": "Nicht konfiguriert (globale Einstellungen verwenden)",
"views.Admin.userOauth2Settings": "OAuth2-Einstellungen",
@@ -420,7 +420,7 @@ export const deMessages = {
"views.admin.UserOauth2Settings.userEmailReplace": "Ersetzungsvorlage",
"components.MailBox.reply": "Antworten",
"components.MailContentRenderer.reply": "Antworten",
- "views.index.SendMail.requestAccess": "Zugriff anfordern",
+ "views.index.SendMail.requestAccess": "Zugriff für diese Adresse anfordern",
"views.user.UserLogin.resetPassword": "Zurücksetzen Passwort",
"views.admin.Account.resetPassword": "Zurücksetzen Passwort",
"views.admin.UserManagement.resetPassword": "Zurücksetzen Passwort",
@@ -556,15 +556,15 @@ export const deMessages = {
"views.common.Appearance.useSimpleIndex": "Einfachen Index verwenden",
"views.common.Appearance.useUTCDate": "UTC-Datum verwenden",
"views.Header.user": "Benutzer",
- "views.Admin.user": "Benutzer",
- "components.AddressSelect.userAddresses": "Benutzeradressen",
+ "views.Admin.user": "Benutzerkonten",
+ "components.AddressSelect.userAddresses": "Mit Benutzerkonto verknüpfte Adressen",
"views.Admin.loginViaUserAdmin": "Benutzer-Admin-Berechtigung",
"views.admin.Statistics.userCount": "Benutzeranzahl",
- "views.admin.UserManagement.user_email": "Benutzer-E-Mail",
- "views.index.AddressBar.userLogin": "Benutzeranmeldung",
- "views.Admin.user_management": "Benutzerverwaltung",
- "views.Admin.user_settings": "Benutzereinstellungen",
- "views.User.user_settings": "Benutzereinstellungen",
+ "views.admin.UserManagement.user_email": "E-Mail des Benutzerkontos",
+ "views.index.AddressBar.userLogin": "Benutzerkonto-Anmeldung",
+ "views.Admin.user_management": "Benutzerkonten verwalten",
+ "views.Admin.user_settings": "Benutzerkonto-Einstellungen",
+ "views.User.user_settings": "Benutzerkonto-Einstellungen",
"components.AiExtractInfo.authCode": "Bestätigungscode",
"views.user.UserLogin.verifyCode": "Bestätigungscode",
"views.user.UserLogin.verifyCodeSent": "Bestätigungscode gesendet, läuft ab in {timeout} Sekunden",
diff --git a/frontend/src/i18n/locales/source/es.ts b/frontend/src/i18n/locales/source/es.ts
index a6fe231..cd973c5 100644
--- a/frontend/src/i18n/locales/source/es.ts
+++ b/frontend/src/i18n/locales/source/es.ts
@@ -5,10 +5,10 @@ export const esMessages = {
"views.Index.about": "Acerca de",
"views.Admin.about": "Acerca de",
"views.Header.accessHeader": "Contraseña de acceso",
- "views.Admin.account": "Cuenta",
- "views.Index.accountSettings": "Configuración de la cuenta",
- "views.Admin.account_settings": "Configuración de la cuenta",
- "views.index.SimpleIndex.accountSettings": "Configuración de la cuenta",
+ "views.Admin.account": "Direcciones de correo",
+ "views.Index.accountSettings": "Configuración de la dirección",
+ "views.Admin.account_settings": "Configuración de direcciones",
+ "views.index.SimpleIndex.accountSettings": "Configuración de la dirección",
"views.index.Attachment.action": "Acción",
"views.admin.SenderAccess.action": "Acción",
"views.user.UserSettings.actions": "Acciones",
@@ -60,13 +60,13 @@ export const esMessages = {
"views.index.Attachment.deleteConfirm": "¿Seguro que quieres eliminar este adjunto?",
"views.admin.Account.deleteTip": "¿Seguro que quieres eliminar este correo?",
"views.admin.SenderAccess.deleteTip": "¿Seguro que quieres eliminar esto?",
- "views.index.AccountSettings.deleteAccountConfirm": "¿Seguro que quieres eliminar tu cuenta y todos sus correos?",
+ "views.index.AccountSettings.deleteAccountConfirm": "¿Seguro que quieres eliminar esta dirección y todos sus correos?",
"views.index.AccountSettings.logoutConfirm": "¿Seguro que quieres cerrar sesión?",
"components.MailBox.deleteMailTip": "¿Seguro que quieres eliminar el correo?",
"components.MailContentRenderer.deleteMailTip": "¿Seguro que quieres eliminar el correo?",
"components.SendBox.deleteMailTip": "¿Seguro que quieres eliminar el correo?",
"views.admin.AccountSettings.delete_rule_confirm": "¿Seguro que quieres eliminar esta regla?",
- "views.admin.UserManagement.deleteUserTip": "¿Seguro que quieres eliminar este usuario?",
+ "views.admin.UserManagement.deleteUserTip": "¿Seguro que quieres eliminar esta cuenta de usuario?",
"views.Admin.logoutConfirmContent": "¿Seguro que quieres salir del panel de administración?",
"views.user.UserSettings.logoutConfirm": "¿Seguro que quieres cerrar sesión?",
"views.admin.IpBlacklistSettings.asn_blacklist": "Lista negra de organizaciones ASN",
@@ -110,7 +110,7 @@ export const esMessages = {
"views.admin.Maintenance.inactiveAddressLabel": "Limpiar las direcciones inactivas de hace más de n días",
"views.admin.Maintenance.mailBoxLabel": "Limpiar la bandeja de entrada de hace más de n días",
"views.admin.Maintenance.sendBoxLabel": "Limpiar la bandeja de salida de hace más de n días",
- "views.admin.Maintenance.unboundAddressLabel": "Limpiar las direcciones no vinculadas de hace más de n días",
+ "views.admin.Maintenance.unboundAddressLabel": "Limpiar direcciones desvinculadas desde hace n días",
"views.admin.Maintenance.mailUnknowLabel": "Limpiar los correos con destinatario desconocido de hace más de n días",
"views.index.AccountSettings.clearInbox": "Vaciar bandeja de entrada",
"views.admin.Account.clearInbox": "Vaciar bandeja de entrada",
@@ -134,20 +134,20 @@ export const esMessages = {
"views.index.SimpleIndex.copyAddress": "Copiar",
"components.AiExtractInfo.copyFailed": "Error al copiar",
"views.Footer.copyright": "Derechos de autor",
- "views.Admin.account_create": "Crear cuenta",
+ "views.Admin.account_create": "Crear dirección de correo",
"views.admin.CreateAccount.creatNewEmail": "Crear nuevo correo",
"views.common.Login.getNewEmail": "Crear nuevo correo",
"views.user.AddressManagement.create_or_bind": "Crear o vincular",
"views.index.LocalAddress.create_or_bind": "Crear o vincular",
"views.user.UserSettings.createPasskey": "Crear passkey",
- "views.admin.UserManagement.createUser": "Crear usuario",
+ "views.admin.UserManagement.createUser": "Crear cuenta de usuario",
"views.user.UserSettings.created_at": "Creado el",
"views.admin.Account.created_at": "Creado el",
"views.admin.SenderAccess.created_at": "Creado el",
"views.admin.UserManagement.created_at": "Creado el",
"views.common.Login.credentialLogin": "Inicio de sesión con credencial",
"views.admin.DatabaseManager.current_db_version": "Versión actual de la BD",
- "views.user.UserBar.currentUser": "Usuario actual",
+ "views.user.UserBar.currentUser": "Cuenta de usuario actual",
"views.admin.UserManagement.roleDonotExist": "El rol actual no existe",
"views.admin.Maintenance.customSqlCleanup": "Limpieza SQL personalizada",
"views.admin.AccountSettings.send_mail_daily_limit": "Límite diario",
@@ -170,11 +170,11 @@ export const esMessages = {
"views.admin.UserManagement.delete": "Eliminar",
"views.admin.AccountSettings.delete_rule": "Eliminar",
"views.admin.Maintenance.deleteCustomSql": "Eliminar",
- "views.index.AccountSettings.deleteAccount": "Eliminar cuenta",
- "views.admin.Account.deleteAccount": "Eliminar cuenta",
+ "views.index.AccountSettings.deleteAccount": "Eliminar dirección de correo",
+ "views.admin.Account.deleteAccount": "Eliminar dirección de correo",
"views.user.UserSettings.deletePasskey": "Eliminar passkey",
"views.admin.AccountSettings.delete_success": "Eliminado correctamente",
- "views.admin.UserManagement.deleteUser": "Eliminar usuario",
+ "views.admin.UserManagement.deleteUser": "Eliminar cuenta de usuario",
"views.index.Attachment.deleteSuccess": "Eliminado correctamente",
"views.admin.SenderAccess.disable": "Deshabilitar",
"views.Admin.loginViaDisabledCheck": "Comprobación de contraseña deshabilitada",
@@ -207,7 +207,7 @@ export const esMessages = {
"views.admin.Telegram.enable": "Habilitar",
"views.admin.UserSettings.enable": "Habilitar",
"views.admin.AiExtractSettings.enableAllowList": "Habilitar Lista blanca de direcciones",
- "views.admin.Webhook.enableAllowList": "Habilitar lista de permitidos (restringe el acceso del webhook a usuarios específicos)",
+ "views.admin.Webhook.enableAllowList": "Habilitar lista de permitidos (restringe el webhook a direcciones específicas)",
"views.index.AutoReply.enableAutoReply": "Habilitar respuesta automática",
"views.admin.Maintenance.cronTip": "Para activar la limpieza por cron, configura [crons] en el worker. Consulta la documentación; 0 días significa limpiar todo.",
"views.admin.IpBlacklistSettings.enable_daily_limit": "Habilitar Límite diario de solicitudes",
@@ -334,7 +334,7 @@ export const esMessages = {
"views.admin.AccountSettings.noLimitSendAddressList": "Lista de direcciones sin límite de saldo",
"views.index.SimpleIndex.noMails": "No se encontraron correos",
"views.admin.RoleAddressConfig.noRolesAvailable": "No hay roles disponibles en la configuración del sistema",
- "views.index.SendMail.requestAccessTip": "Todavía no hay saldo de envío. Si el administrador activó un saldo por defecto, se asignará automáticamente; si no, solicita acceso o contacta con el administrador.",
+ "views.index.SendMail.requestAccessTip": "El acceso y el saldo de envío pertenecen a la dirección actual, no a la cuenta de usuario. Solicita acceso para esta dirección o contacta con el administrador.",
"components.SendBox.emptySent": "No hay correos enviados",
"views.admin.RoleAddressConfig.notConfigured": "No configurado (usar configuración global)",
"views.Admin.userOauth2Settings": "Configuración de OAuth2",
@@ -420,7 +420,7 @@ export const esMessages = {
"views.admin.UserOauth2Settings.userEmailReplace": "Plantilla de reemplazo",
"components.MailBox.reply": "Responder",
"components.MailContentRenderer.reply": "Responder",
- "views.index.SendMail.requestAccess": "Solicitar acceso",
+ "views.index.SendMail.requestAccess": "Solicitar acceso para esta dirección",
"views.user.UserLogin.resetPassword": "Restablecer Contraseña",
"views.admin.Account.resetPassword": "Restablecer Contraseña",
"views.admin.UserManagement.resetPassword": "Restablecer Contraseña",
@@ -556,15 +556,15 @@ export const esMessages = {
"views.common.Appearance.useSimpleIndex": "Usar índice simple",
"views.common.Appearance.useUTCDate": "Usar fecha UTC",
"views.Header.user": "Usuario",
- "views.Admin.user": "Usuario",
- "components.AddressSelect.userAddresses": "Direcciones del usuario",
+ "views.Admin.user": "Cuentas de usuario",
+ "components.AddressSelect.userAddresses": "Direcciones vinculadas a la cuenta",
"views.Admin.loginViaUserAdmin": "Permiso de administrador del usuario",
"views.admin.Statistics.userCount": "Cantidad de usuarios",
- "views.admin.UserManagement.user_email": "Correo del usuario",
- "views.index.AddressBar.userLogin": "Inicio de sesión de usuario",
- "views.Admin.user_management": "Gestión de usuarios",
- "views.Admin.user_settings": "Configuración de usuario",
- "views.User.user_settings": "Configuración de usuario",
+ "views.admin.UserManagement.user_email": "Correo de la cuenta de usuario",
+ "views.index.AddressBar.userLogin": "Inicio de sesión de la cuenta",
+ "views.Admin.user_management": "Gestión de cuentas de usuario",
+ "views.Admin.user_settings": "Configuración de cuentas de usuario",
+ "views.User.user_settings": "Configuración de la cuenta de usuario",
"components.AiExtractInfo.authCode": "Código de verificación",
"views.user.UserLogin.verifyCode": "Código de verificación",
"views.user.UserLogin.verifyCodeSent": "Código de verificación enviado, expira en {timeout} segundos",
diff --git a/frontend/src/i18n/locales/source/ja.ts b/frontend/src/i18n/locales/source/ja.ts
index 0fd5bd6..50c553d 100644
--- a/frontend/src/i18n/locales/source/ja.ts
+++ b/frontend/src/i18n/locales/source/ja.ts
@@ -5,10 +5,10 @@ export const jaMessages = {
"views.Index.about": "概要",
"views.Admin.about": "概要",
"views.Header.accessHeader": "アクセス用パスワード",
- "views.Admin.account": "アカウント",
- "views.Index.accountSettings": "アカウント設定",
- "views.Admin.account_settings": "アカウント設定",
- "views.index.SimpleIndex.accountSettings": "アカウント設定",
+ "views.Admin.account": "メールアドレス",
+ "views.Index.accountSettings": "メールアドレス設定",
+ "views.Admin.account_settings": "メールアドレス設定",
+ "views.index.SimpleIndex.accountSettings": "メールアドレス設定",
"views.index.Attachment.action": "操作",
"views.admin.SenderAccess.action": "操作",
"views.user.UserSettings.actions": "操作",
@@ -60,13 +60,13 @@ export const jaMessages = {
"views.index.Attachment.deleteConfirm": "この添付ファイルを削除してもよろしいですか?",
"views.admin.Account.deleteTip": "このメールを削除してもよろしいですか?",
"views.admin.SenderAccess.deleteTip": "これを削除してもよろしいですか?",
- "views.index.AccountSettings.deleteAccountConfirm": "このアカウントと関連メールをすべて削除してもよろしいですか?",
+ "views.index.AccountSettings.deleteAccountConfirm": "このメールアドレスと関連メールをすべて削除してもよろしいですか?",
"views.index.AccountSettings.logoutConfirm": "ログアウトしてもよろしいですか?",
"components.MailBox.deleteMailTip": "メールを削除してもよろしいですか?",
"components.MailContentRenderer.deleteMailTip": "メールを削除してもよろしいですか?",
"components.SendBox.deleteMailTip": "メールを削除してもよろしいですか?",
"views.admin.AccountSettings.delete_rule_confirm": "このルールを削除してもよろしいですか?",
- "views.admin.UserManagement.deleteUserTip": "このユーザーを削除してもよろしいですか?",
+ "views.admin.UserManagement.deleteUserTip": "このユーザーアカウントを削除してもよろしいですか?",
"views.Admin.logoutConfirmContent": "管理画面からログアウトしてもよろしいですか?",
"views.user.UserSettings.logoutConfirm": "ログアウトしてもよろしいですか?",
"views.admin.IpBlacklistSettings.asn_blacklist": "ASN組織ブラックリスト",
@@ -110,7 +110,7 @@ export const jaMessages = {
"views.admin.Maintenance.inactiveAddressLabel": "n 日より前の非アクティブなアドレスを削除",
"views.admin.Maintenance.mailBoxLabel": "n 日より前の受信箱を削除",
"views.admin.Maintenance.sendBoxLabel": "n 日より前の送信箱を削除",
- "views.admin.Maintenance.unboundAddressLabel": "n 日より前の未紐付けアドレスを削除",
+ "views.admin.Maintenance.unboundAddressLabel": "紐付け解除から n 日経過したメールアドレスを削除",
"views.admin.Maintenance.mailUnknowLabel": "n 日より前の受信者不明メールを削除",
"views.index.AccountSettings.clearInbox": "受信箱を削除",
"views.admin.Account.clearInbox": "受信箱を削除",
@@ -134,20 +134,20 @@ export const jaMessages = {
"views.index.SimpleIndex.copyAddress": "コピー",
"components.AiExtractInfo.copyFailed": "コピーに失敗しました",
"views.Footer.copyright": "著作権",
- "views.Admin.account_create": "アカウントを作成",
+ "views.Admin.account_create": "メールアドレスを作成",
"views.admin.CreateAccount.creatNewEmail": "新しいメールを作成",
"views.common.Login.getNewEmail": "新しいメールを作成",
"views.user.AddressManagement.create_or_bind": "作成または紐付け",
"views.index.LocalAddress.create_or_bind": "作成または紐付け",
"views.user.UserSettings.createPasskey": "パスキーを作成",
- "views.admin.UserManagement.createUser": "ユーザーを作成",
+ "views.admin.UserManagement.createUser": "ユーザーアカウントを作成",
"views.user.UserSettings.created_at": "作成日時",
"views.admin.Account.created_at": "作成日時",
"views.admin.SenderAccess.created_at": "作成日時",
"views.admin.UserManagement.created_at": "作成日時",
"views.common.Login.credentialLogin": "資格情報でログイン",
"views.admin.DatabaseManager.current_db_version": "現在のDBバージョン",
- "views.user.UserBar.currentUser": "現在のログインユーザー",
+ "views.user.UserBar.currentUser": "現在のユーザーアカウント",
"views.admin.UserManagement.roleDonotExist": "現在のロールは存在しません",
"views.admin.Maintenance.customSqlCleanup": "カスタムSQLクリーンアップ",
"views.admin.AccountSettings.send_mail_daily_limit": "日次上限",
@@ -170,11 +170,11 @@ export const jaMessages = {
"views.admin.UserManagement.delete": "削除",
"views.admin.AccountSettings.delete_rule": "削除",
"views.admin.Maintenance.deleteCustomSql": "削除",
- "views.index.AccountSettings.deleteAccount": "アカウントを削除",
- "views.admin.Account.deleteAccount": "アカウントを削除",
+ "views.index.AccountSettings.deleteAccount": "メールアドレスを削除",
+ "views.admin.Account.deleteAccount": "メールアドレスを削除",
"views.user.UserSettings.deletePasskey": "Passkeyを削除",
"views.admin.AccountSettings.delete_success": "削除しました",
- "views.admin.UserManagement.deleteUser": "ユーザーを削除",
+ "views.admin.UserManagement.deleteUser": "ユーザーアカウントを削除",
"views.index.Attachment.deleteSuccess": "正常に削除しました",
"views.admin.SenderAccess.disable": "無効化",
"views.Admin.loginViaDisabledCheck": "パスワードチェックを無効化",
@@ -207,7 +207,7 @@ export const jaMessages = {
"views.admin.Telegram.enable": "有効化",
"views.admin.UserSettings.enable": "有効化",
"views.admin.AiExtractSettings.enableAllowList": "アドレス許可リストを有効化",
- "views.admin.Webhook.enableAllowList": "許可リストを有効化 (Webhook へのアクセスを特定ユーザーに制限)",
+ "views.admin.Webhook.enableAllowList": "許可リストを有効化(Webhook を特定のメールアドレスに制限)",
"views.index.AutoReply.enableAutoReply": "自動返信を有効化",
"views.admin.Maintenance.cronTip": "cron クリーンアップを有効にするには worker の [crons] を設定してください。詳細はドキュメントを参照してください。0 日はすべて削除を意味します。",
"views.admin.IpBlacklistSettings.enable_daily_limit": "1日のリクエスト上限を有効化",
@@ -334,7 +334,7 @@ export const jaMessages = {
"views.admin.AccountSettings.noLimitSendAddressList": "残高無制限の送信アドレス一覧",
"views.index.SimpleIndex.noMails": "メールが見つかりません",
"views.admin.RoleAddressConfig.noRolesAvailable": "システム設定に利用可能なロールがありません",
- "views.index.SendMail.requestAccessTip": "まだ送信残高がありません。管理者がデフォルト残高を有効にしていれば自動付与されます。そうでない場合は権限申請または管理者へ連絡してください。",
+ "views.index.SendMail.requestAccessTip": "送信権限と残高はユーザーアカウントではなく現在のメールアドレスに属します。このアドレスの権限を申請するか、管理者に連絡してください。",
"components.SendBox.emptySent": "送信済みメールはありません",
"views.admin.RoleAddressConfig.notConfigured": "未設定 (全体設定を使用)",
"views.Admin.userOauth2Settings": "OAuth2設定",
@@ -420,7 +420,7 @@ export const jaMessages = {
"views.admin.UserOauth2Settings.userEmailReplace": "置換テンプレート",
"components.MailBox.reply": "返信",
"components.MailContentRenderer.reply": "返信",
- "views.index.SendMail.requestAccess": "アクセスを申請",
+ "views.index.SendMail.requestAccess": "このアドレスの送信権限を申請",
"views.user.UserLogin.resetPassword": "パスワードをリセット",
"views.admin.Account.resetPassword": "パスワードをリセット",
"views.admin.UserManagement.resetPassword": "パスワードをリセット",
@@ -556,15 +556,15 @@ export const jaMessages = {
"views.common.Appearance.useSimpleIndex": "シンプルインデックスを使う",
"views.common.Appearance.useUTCDate": "UTC 日時を使う",
"views.Header.user": "ユーザー",
- "views.Admin.user": "ユーザー",
- "components.AddressSelect.userAddresses": "ユーザーのアドレス",
+ "views.Admin.user": "ユーザーアカウント",
+ "components.AddressSelect.userAddresses": "ユーザーアカウントに紐付くアドレス",
"views.Admin.loginViaUserAdmin": "ユーザー管理者権限",
"views.admin.Statistics.userCount": "ユーザー数",
- "views.admin.UserManagement.user_email": "ユーザーメール",
- "views.index.AddressBar.userLogin": "ユーザーログイン",
- "views.Admin.user_management": "ユーザー管理",
- "views.Admin.user_settings": "ユーザー設定",
- "views.User.user_settings": "ユーザー設定",
+ "views.admin.UserManagement.user_email": "ユーザーアカウントのメール",
+ "views.index.AddressBar.userLogin": "ユーザーアカウントログイン",
+ "views.Admin.user_management": "ユーザーアカウント管理",
+ "views.Admin.user_settings": "ユーザーアカウント設定",
+ "views.User.user_settings": "ユーザーアカウント設定",
"components.AiExtractInfo.authCode": "認証コード",
"views.user.UserLogin.verifyCode": "認証コード",
"views.user.UserLogin.verifyCodeSent": "認証コードを送信しました, 有効期限 {timeout} 秒",
diff --git a/frontend/src/i18n/locales/source/ptBR.ts b/frontend/src/i18n/locales/source/ptBR.ts
index d50b861..b81dfa8 100644
--- a/frontend/src/i18n/locales/source/ptBR.ts
+++ b/frontend/src/i18n/locales/source/ptBR.ts
@@ -5,10 +5,10 @@ export const ptBRMessages = {
"views.Index.about": "Sobre",
"views.Admin.about": "Sobre",
"views.Header.accessHeader": "Senha de acesso",
- "views.Admin.account": "Conta",
- "views.Index.accountSettings": "Configurações da conta",
- "views.Admin.account_settings": "Configurações da conta",
- "views.index.SimpleIndex.accountSettings": "Configurações da conta",
+ "views.Admin.account": "Endereços de e-mail",
+ "views.Index.accountSettings": "Configurações do endereço",
+ "views.Admin.account_settings": "Configurações de endereços",
+ "views.index.SimpleIndex.accountSettings": "Configurações do endereço",
"views.index.Attachment.action": "Ação",
"views.admin.SenderAccess.action": "Ação",
"views.user.UserSettings.actions": "Ações",
@@ -60,13 +60,13 @@ export const ptBRMessages = {
"views.index.Attachment.deleteConfirm": "Tem certeza de que deseja excluir este anexo?",
"views.admin.Account.deleteTip": "Tem certeza de que deseja excluir este e-mail?",
"views.admin.SenderAccess.deleteTip": "Tem certeza de que deseja excluir isto?",
- "views.index.AccountSettings.deleteAccountConfirm": "Tem certeza de que deseja excluir sua conta e todos os e-mails dela?",
+ "views.index.AccountSettings.deleteAccountConfirm": "Tem certeza de que deseja excluir este endereço e todos os e-mails dele?",
"views.index.AccountSettings.logoutConfirm": "Tem certeza de que deseja sair?",
"components.MailBox.deleteMailTip": "Tem certeza de que deseja excluir o e-mail?",
"components.MailContentRenderer.deleteMailTip": "Tem certeza de que deseja excluir o e-mail?",
"components.SendBox.deleteMailTip": "Tem certeza de que deseja excluir o e-mail?",
"views.admin.AccountSettings.delete_rule_confirm": "Tem certeza de que deseja excluir esta regra?",
- "views.admin.UserManagement.deleteUserTip": "Tem certeza de que deseja excluir este usuário?",
+ "views.admin.UserManagement.deleteUserTip": "Tem certeza de que deseja excluir esta conta de usuário?",
"views.Admin.logoutConfirmContent": "Tem certeza de que deseja sair do painel de administração?",
"views.user.UserSettings.logoutConfirm": "Tem certeza de que deseja sair?",
"views.admin.IpBlacklistSettings.asn_blacklist": "Lista negra de organizações ASN",
@@ -110,7 +110,7 @@ export const ptBRMessages = {
"views.admin.Maintenance.inactiveAddressLabel": "Limpar os endereços inativos de mais de n dias",
"views.admin.Maintenance.mailBoxLabel": "Limpar a caixa de entrada de mais de n dias",
"views.admin.Maintenance.sendBoxLabel": "Limpar a caixa de saída de mais de n dias",
- "views.admin.Maintenance.unboundAddressLabel": "Limpar os endereços desvinculados de mais de n dias",
+ "views.admin.Maintenance.unboundAddressLabel": "Limpar endereços desvinculados há n dias",
"views.admin.Maintenance.mailUnknowLabel": "Limpar os e-mails com destinatário desconhecido de mais de n dias",
"views.index.AccountSettings.clearInbox": "Limpar caixa de entrada",
"views.admin.Account.clearInbox": "Limpar caixa de entrada",
@@ -134,20 +134,20 @@ export const ptBRMessages = {
"views.index.SimpleIndex.copyAddress": "Copiar",
"components.AiExtractInfo.copyFailed": "Falha ao copiar",
"views.Footer.copyright": "Direitos autorais",
- "views.Admin.account_create": "Criar conta",
+ "views.Admin.account_create": "Criar endereço de e-mail",
"views.admin.CreateAccount.creatNewEmail": "Criar novo e-mail",
"views.common.Login.getNewEmail": "Criar novo e-mail",
"views.user.AddressManagement.create_or_bind": "Criar ou vincular",
"views.index.LocalAddress.create_or_bind": "Criar ou vincular",
"views.user.UserSettings.createPasskey": "Criar passkey",
- "views.admin.UserManagement.createUser": "Criar usuário",
+ "views.admin.UserManagement.createUser": "Criar conta de usuário",
"views.user.UserSettings.created_at": "Criado em",
"views.admin.Account.created_at": "Criado em",
"views.admin.SenderAccess.created_at": "Criado em",
"views.admin.UserManagement.created_at": "Criado em",
"views.common.Login.credentialLogin": "Login com credencial",
"views.admin.DatabaseManager.current_db_version": "Versão atual do banco",
- "views.user.UserBar.currentUser": "Usuário atual",
+ "views.user.UserBar.currentUser": "Conta de usuário atual",
"views.admin.UserManagement.roleDonotExist": "A função atual não existe",
"views.admin.Maintenance.customSqlCleanup": "Limpeza SQL personalizada",
"views.admin.AccountSettings.send_mail_daily_limit": "Limite diário",
@@ -170,11 +170,11 @@ export const ptBRMessages = {
"views.admin.UserManagement.delete": "Excluir",
"views.admin.AccountSettings.delete_rule": "Excluir",
"views.admin.Maintenance.deleteCustomSql": "Excluir",
- "views.index.AccountSettings.deleteAccount": "Excluir conta",
- "views.admin.Account.deleteAccount": "Excluir conta",
+ "views.index.AccountSettings.deleteAccount": "Excluir endereço de e-mail",
+ "views.admin.Account.deleteAccount": "Excluir endereço de e-mail",
"views.user.UserSettings.deletePasskey": "Excluir passkey",
"views.admin.AccountSettings.delete_success": "Excluído com sucesso",
- "views.admin.UserManagement.deleteUser": "Excluir usuário",
+ "views.admin.UserManagement.deleteUser": "Excluir conta de usuário",
"views.index.Attachment.deleteSuccess": "Excluído com sucesso",
"views.admin.SenderAccess.disable": "Desativar",
"views.Admin.loginViaDisabledCheck": "Verificação de senha desativada",
@@ -207,7 +207,7 @@ export const ptBRMessages = {
"views.admin.Telegram.enable": "Ativar",
"views.admin.UserSettings.enable": "Ativar",
"views.admin.AiExtractSettings.enableAllowList": "Ativar Lista branca de endereços",
- "views.admin.Webhook.enableAllowList": "Ativar lista de permissão (restringir o acesso ao webhook a usuários específicos)",
+ "views.admin.Webhook.enableAllowList": "Ativar lista de permissão (restringir o webhook a endereços específicos)",
"views.index.AutoReply.enableAutoReply": "Ativar resposta automática",
"views.admin.Maintenance.cronTip": "Para ativar a limpeza por cron, configure [crons] no worker. Consulte a documentação; 0 dias significa limpar tudo.",
"views.admin.IpBlacklistSettings.enable_daily_limit": "Ativar limite diário de solicitações",
@@ -334,7 +334,7 @@ export const ptBRMessages = {
"views.admin.AccountSettings.noLimitSendAddressList": "Lista de endereços sem limite de saldo",
"views.index.SimpleIndex.noMails": "Nenhum e-mail encontrado",
"views.admin.RoleAddressConfig.noRolesAvailable": "Nenhuma função disponível na configuração do sistema",
- "views.index.SendMail.requestAccessTip": "Ainda não há saldo de envio. Se o administrador ativou um saldo padrão, ele será atribuído automaticamente; caso contrário, solicite acesso ou fale com o administrador.",
+ "views.index.SendMail.requestAccessTip": "O acesso e o saldo de envio pertencem ao endereço atual, não à conta de usuário. Solicite acesso para este endereço ou fale com o administrador.",
"components.SendBox.emptySent": "Nenhum e-mail enviado",
"views.admin.RoleAddressConfig.notConfigured": "Não configurado (usar configurações globais)",
"views.Admin.userOauth2Settings": "Configurações de OAuth2",
@@ -420,7 +420,7 @@ export const ptBRMessages = {
"views.admin.UserOauth2Settings.userEmailReplace": "Modelo de substituição",
"components.MailBox.reply": "Responder",
"components.MailContentRenderer.reply": "Responder",
- "views.index.SendMail.requestAccess": "Solicitar acesso",
+ "views.index.SendMail.requestAccess": "Solicitar acesso para este endereço",
"views.user.UserLogin.resetPassword": "Redefinir Senha",
"views.admin.Account.resetPassword": "Redefinir Senha",
"views.admin.UserManagement.resetPassword": "Redefinir Senha",
@@ -556,15 +556,15 @@ export const ptBRMessages = {
"views.common.Appearance.useSimpleIndex": "Usar índice simples",
"views.common.Appearance.useUTCDate": "Usar data UTC",
"views.Header.user": "Usuário",
- "views.Admin.user": "Usuário",
- "components.AddressSelect.userAddresses": "Endereços do usuário",
+ "views.Admin.user": "Contas de usuário",
+ "components.AddressSelect.userAddresses": "Endereços vinculados à conta",
"views.Admin.loginViaUserAdmin": "Permissão de administrador do usuário",
"views.admin.Statistics.userCount": "Quantidade de usuários",
- "views.admin.UserManagement.user_email": "E-mail do usuário",
- "views.index.AddressBar.userLogin": "Login do usuário",
- "views.Admin.user_management": "Gerenciamento de usuários",
- "views.Admin.user_settings": "Configurações do usuário",
- "views.User.user_settings": "Configurações do usuário",
+ "views.admin.UserManagement.user_email": "E-mail da conta de usuário",
+ "views.index.AddressBar.userLogin": "Login da conta de usuário",
+ "views.Admin.user_management": "Gerenciamento de contas de usuário",
+ "views.Admin.user_settings": "Configurações de contas de usuário",
+ "views.User.user_settings": "Configurações da conta de usuário",
"components.AiExtractInfo.authCode": "Código de verificação",
"views.user.UserLogin.verifyCode": "Código de verificação",
"views.user.UserLogin.verifyCodeSent": "Código de verificação enviado, expira em {timeout} segundos",
diff --git a/frontend/src/i18n/message-registry.ts b/frontend/src/i18n/message-registry.ts
index 2f52dfb..15f4bd8 100644
--- a/frontend/src/i18n/message-registry.ts
+++ b/frontend/src/i18n/message-registry.ts
@@ -289,8 +289,8 @@ export const MESSAGE_REGISTRY = {
"zh": "本地地址"
},
"userAddresses": {
- "en": "User Addresses",
- "zh": "用户地址"
+ "en": "Addresses Bound to User Account",
+ "zh": "用户账号绑定地址"
}
},
"components.AddressCredentialModal": {
@@ -419,8 +419,8 @@ export const MESSAGE_REGISTRY = {
"zh": "关于"
},
"accountSettings": {
- "en": "Account Settings",
- "zh": "账户"
+ "en": "Mailbox Address Settings",
+ "zh": "邮箱地址设置"
},
"appearance": {
"en": "Appearance",
@@ -565,8 +565,8 @@ export const MESSAGE_REGISTRY = {
"zh": "Cloudflare 临时邮件"
},
"user": {
- "en": "User",
- "zh": "用户"
+ "en": "User Account",
+ "zh": "用户账号"
}
},
"views.user.BindAddress": {
@@ -589,16 +589,16 @@ export const MESSAGE_REGISTRY = {
"zh": "请输入 Admin 密码"
},
"account": {
- "en": "Account",
- "zh": "账号"
+ "en": "Mailbox Addresses",
+ "zh": "邮箱地址"
},
"account_create": {
- "en": "Create Account",
- "zh": "创建账号"
+ "en": "Create Mailbox Address",
+ "zh": "创建邮箱地址"
},
"account_settings": {
- "en": "Account Settings",
- "zh": "账号设置"
+ "en": "Mailbox Address Settings",
+ "zh": "邮箱地址设置"
},
"adminAccount": {
"en": "Admin",
@@ -705,20 +705,20 @@ export const MESSAGE_REGISTRY = {
"zh": "无收件人邮件"
},
"user": {
- "en": "User",
- "zh": "用户"
+ "en": "User Accounts",
+ "zh": "用户账号"
},
"userOauth2Settings": {
"en": "Oauth2 Settings",
"zh": "Oauth2 设置"
},
"user_management": {
- "en": "User Management",
- "zh": "用户管理"
+ "en": "User Account Management",
+ "zh": "用户账号管理"
},
"user_settings": {
- "en": "User Settings",
- "zh": "用户设置"
+ "en": "User Account Settings",
+ "zh": "用户账号设置"
},
"webhookSettings": {
"en": "Webhook Settings",
@@ -743,8 +743,8 @@ export const MESSAGE_REGISTRY = {
"zh": "收件箱"
},
"user_settings": {
- "en": "User Settings",
- "zh": "用户设置"
+ "en": "User Account Settings",
+ "zh": "用户账号设置"
}
},
"views.user.UserLogin": {
@@ -823,8 +823,8 @@ export const MESSAGE_REGISTRY = {
},
"views.user.UserBar": {
"currentUser": {
- "en": "Current Login User",
- "zh": "当前登录用户"
+ "en": "Current User Account",
+ "zh": "当前用户账号"
},
"fetchUserSettingsError": {
"en": "Login password is invalid or account not exist, it may be network connection issue, please try again later.",
@@ -877,8 +877,8 @@ export const MESSAGE_REGISTRY = {
"zh": "转移地址"
},
"transferAddressTip": {
- "en": "Transfer address to another user will remove the address from your account and transfer it to another user. Are you sure to transfer the address?",
- "zh": "转移地址到其他用户将会从你的账户中移除此地址并转移给其他用户。确定要转移地址吗?"
+ "en": "Transferring this address removes it from your user account and binds it to another user account. Are you sure?",
+ "zh": "转移后,此邮箱地址将从你的用户账号解绑,并绑定到另一个用户账号。确定要转移吗?"
},
"unbindAddress": {
"en": "Unbind Address",
@@ -915,12 +915,12 @@ export const MESSAGE_REGISTRY = {
"zh": "确认密码"
},
"deleteAccount": {
- "en": "Delete Account",
- "zh": "删除账户"
+ "en": "Delete Mailbox Address",
+ "zh": "删除邮箱地址"
},
"deleteAccountConfirm": {
- "en": "Are you sure to delete your account and all emails for this account?",
- "zh": "确定要删除你的账户和其中的所有邮件吗?"
+ "en": "Are you sure you want to delete this mailbox address and all of its emails?",
+ "zh": "确定要删除当前邮箱地址及其全部邮件吗?"
},
"logout": {
"en": "Logout",
@@ -1055,12 +1055,12 @@ export const MESSAGE_REGISTRY = {
"zh": "预览"
},
"requestAccess": {
- "en": "Request Access",
- "zh": "申请权限"
+ "en": "Request Access for This Address",
+ "zh": "为当前地址申请发信权限"
},
"requestAccessTip": {
- "en": "No send balance yet. If your admin enabled a default balance it should be assigned automatically; otherwise request access or contact the admin.",
- "zh": "当前还没有可用的发信额度。如果管理员启用了默认额度,会自动发放;否则请申请权限或联系管理员处理。"
+ "en": "Send access and balance belong to the current mailbox address, not the user account. This address has no send balance yet. Request access for it or contact the admin.",
+ "zh": "发信权限和额度属于当前邮箱地址,不属于用户账号。当前地址还没有可用额度,请为该地址申请发信权限或联系管理员。"
},
"rich text": {
"en": "Rich Text",
@@ -1105,8 +1105,8 @@ export const MESSAGE_REGISTRY = {
},
"views.index.SimpleIndex": {
"accountSettings": {
- "en": "Account Settings",
- "zh": "账户设置"
+ "en": "Mailbox Address Settings",
+ "zh": "邮箱地址设置"
},
"addressCopied": {
"en": "Address copied successfully",
@@ -1197,7 +1197,7 @@ export const MESSAGE_REGISTRY = {
"zh": "邮箱地址凭证"
},
"addressCredentialTip": {
- "en": "Please copy the Mail Address Credential and you can use it to login to your email account.",
+ "en": "Copy this mailbox address credential to log in to this address.",
"zh": "请复制邮箱地址凭证,你可以使用它登录你的邮箱。"
},
"addressManage": {
@@ -1209,7 +1209,7 @@ export const MESSAGE_REGISTRY = {
"zh": "地址密码"
},
"fetchAddressError": {
- "en": "Mail address credential is invalid or account not exist, it may be network connection issue, please try again later.",
+ "en": "The mailbox address credential is invalid or the address does not exist. This may also be a network issue; please try again later.",
"zh": "邮箱地址凭证无效或邮箱地址不存在,也可能是网络连接异常,请稍后再尝试。"
},
"linkWithAddressCredential": {
@@ -1221,8 +1221,8 @@ export const MESSAGE_REGISTRY = {
"zh": "确定"
},
"userLogin": {
- "en": "User Login",
- "zh": "用户登录"
+ "en": "User Account Login",
+ "zh": "用户账号登录"
}
},
"views.admin.SendBox": {
@@ -1353,7 +1353,7 @@ export const MESSAGE_REGISTRY = {
"zh": "邮箱地址凭证"
},
"addressCredentialTip": {
- "en": "Please copy the Mail Address Credential and you can use it to login to your email account.",
+ "en": "Copy this mailbox address credential to log in to this address.",
"zh": "请复制邮箱地址凭证,你可以使用它登录你的邮箱。"
},
"addressQueryTip": {
@@ -1385,8 +1385,8 @@ export const MESSAGE_REGISTRY = {
"zh": "删除"
},
"deleteAccount": {
- "en": "Delete Account",
- "zh": "删除邮箱"
+ "en": "Delete Mailbox Address",
+ "zh": "删除邮箱地址"
},
"deleteTip": {
"en": "Are you sure to delete this email?",
@@ -1857,8 +1857,8 @@ export const MESSAGE_REGISTRY = {
"zh": "更改角色"
},
"createUser": {
- "en": "Create User",
- "zh": "创建用户"
+ "en": "Create User Account",
+ "zh": "创建用户账号"
},
"created_at": {
"en": "Created At",
@@ -1869,12 +1869,12 @@ export const MESSAGE_REGISTRY = {
"zh": "删除"
},
"deleteUser": {
- "en": "Delete User",
- "zh": "删除用户"
+ "en": "Delete User Account",
+ "zh": "删除用户账号"
},
"deleteUserTip": {
- "en": "Are you sure you want to delete this user?",
- "zh": "确定要删除此用户吗?"
+ "en": "Are you sure you want to delete this user account?",
+ "zh": "确定要删除此用户账号吗?"
},
"domains": {
"en": "Domains",
@@ -1921,12 +1921,12 @@ export const MESSAGE_REGISTRY = {
"zh": "成功"
},
"userAddressManagement": {
- "en": "Address Management",
- "zh": "地址管理"
+ "en": "Bound Address Management",
+ "zh": "绑定地址管理"
},
"user_email": {
- "en": "User Email",
- "zh": "用户邮箱"
+ "en": "User Account Email",
+ "zh": "用户账号邮箱"
}
},
"views.admin.Telegram": {
@@ -1989,7 +1989,7 @@ export const MESSAGE_REGISTRY = {
"zh": "邮箱地址凭证"
},
"addressCredentialTip": {
- "en": "Please copy the Mail Address Credential and you can use it to login to your email account.",
+ "en": "Copy this mailbox address credential to log in to this address.",
"zh": "请复制邮箱地址凭证,你可以使用它登录你的邮箱。"
},
"addressPassword": {
@@ -2465,8 +2465,8 @@ export const MESSAGE_REGISTRY = {
"zh": "请输入天数"
},
"unboundAddressLabel": {
- "en": "Cleanup the unbound address before n days",
- "zh": "清理 n 天前的未绑定用户地址"
+ "en": "Clean up mailbox addresses unbound for n days",
+ "zh": "清理已解绑 n 天的邮箱地址"
}
},
"views.common.Login": {
@@ -2561,8 +2561,8 @@ export const MESSAGE_REGISTRY = {
},
"views.admin.Webhook": {
"enableAllowList": {
- "en": "Enable Allow List (Restrict webhook access to specific users)",
- "zh": "启用白名单 (限制 webhook 访问权限,只有白名单中的用户可以使用)"
+ "en": "Enable Allow List (Restrict webhook access to specific mailbox addresses)",
+ "zh": "启用白名单(仅允许白名单中的邮箱地址使用 Webhook)"
},
"manualInputPrompt": {
"en": "Type and press Enter to add",
diff --git a/vitepress-docs/docs/en/guide/config-send-mail.md b/vitepress-docs/docs/en/guide/config-send-mail.md
index d173116..5db3840 100644
--- a/vitepress-docs/docs/en/guide/config-send-mail.md
+++ b/vitepress-docs/docs/en/guide/config-send-mail.md
@@ -152,10 +152,12 @@ wrangler secret put SMTP_CONFIG
## Send Balance Mechanism
-Users need a send balance to send emails. The balance mechanism works as follows:
+Send access and balance belong to a **mailbox address**, not to a user account. A user account only binds, syncs, and manages multiple mailbox addresses. Even after signing in to a user account, select a specific mailbox address and request send access for that address. Each address has its own enabled state and balance.
+
+A mailbox address needs a send balance to send emails. The balance mechanism works as follows:
1. **Auto-initialize Default Quota**: When `DEFAULT_SEND_BALANCE > 0`, the system automatically initializes the default quota when the user opens the send page or calls the send-mail API for the first time
-2. **Manual Request**: If `DEFAULT_SEND_BALANCE = 0`, users can still click "Request Send Permission" in the frontend to create a pending send-access record for admins to review
+2. **Manual Request**: If `DEFAULT_SEND_BALANCE = 0`, users can click "Request Access for This Address" on the current mailbox address's send page to create an address-specific request for admins to review
3. **Unlimited Sending**: The following methods can bypass balance checks:
- Add the address to the "No Limit Send Address List" in the admin console
- Configure the `NO_LIMIT_SEND_ROLE` environment variable to specify roles that can send without limits
diff --git a/vitepress-docs/docs/en/guide/feature/admin-user-management.md b/vitepress-docs/docs/en/guide/feature/admin-user-management.md
index b11f0ff..d8bd29d 100644
--- a/vitepress-docs/docs/en/guide/feature/admin-user-management.md
+++ b/vitepress-docs/docs/en/guide/feature/admin-user-management.md
@@ -1,11 +1,11 @@
-# Admin User Management
+# Admin User Account Management
-## User Management Page
+## User Account Management Page

-## User Settings
+## User Account Settings
-Configure user login and authentication settings here
+Configure site user account login, registration, and authentication here. A user account binds and manages mailbox addresses; it is not itself a mailbox address.

diff --git a/vitepress-docs/docs/en/guide/feature/admin.md b/vitepress-docs/docs/en/guide/feature/admin.md
index c0a82c2..5c965ef 100644
--- a/vitepress-docs/docs/en/guide/feature/admin.md
+++ b/vitepress-docs/docs/en/guide/feature/admin.md
@@ -8,24 +8,30 @@ After deploying the frontend application, click the upper-left logo 5 times or v
You need to configure `ADMIN_PASSWORDS` in the backend or ensure the current user role is `ADMIN_USER_ROLE`, otherwise access to the console will be denied.
-## Admin Passwords vs User Accounts
+## Admin Passwords, User Accounts, and Mailbox Addresses
`ADMIN_PASSWORDS` is the management password for the Admin console. It is not a site user account
and does not correspond to any mailbox address. Logging in with an admin password grants access to
the console, but that login itself cannot receive mail.
-Site user accounts are stored in the `users` table and use the user login flow. Whether a user can
-receive mail depends on whether they created or bound a mailbox address. Creating a normal user
-whose email looks like `admin@example.com` does not automatically grant admin permissions.
+Site user accounts are stored in the `users` table and use the user login flow. They bind, sync, and
+manage multiple mailbox addresses, but do not have an inbox, address credential, or send balance by
+themselves.
+
+Mailbox addresses are stored in the `address` table. Each address has its own inbox, address
+credential, and send access. A user can receive mail only after creating or binding an address, and
+send access must be requested for a specific mailbox address rather than for the user account.
+Creating a normal user whose email looks like `admin@example.com` does not automatically grant admin
+permissions.
If you want a user account to access the Admin console, configure `ADMIN_USER_ROLE` and assign the
same role to that user in user management.

-## Account List Sorting
+## Mailbox Address List Sorting
-The Accounts tab in the admin console supports column sorting. Click the column header to toggle ascending/descending order for:
+The Mailbox Addresses tab in the admin console supports column sorting. Click the column header to toggle ascending/descending order for:
- ID
- Name
diff --git a/vitepress-docs/docs/en/guide/feature/agent-email.md b/vitepress-docs/docs/en/guide/feature/agent-email.md
index 4a88d76..333f7bd 100644
--- a/vitepress-docs/docs/en/guide/feature/agent-email.md
+++ b/vitepress-docs/docs/en/guide/feature/agent-email.md
@@ -93,6 +93,10 @@ curl -s "$BASE/api/parsed_mails?limit=20&offset=0" \
Requires `send_balance > 0` (check via `/api/settings`). The deployment must have a send method configured (Resend / SMTP / Cloudflare Email Routing binding).
+::: tip Send access belongs to a mailbox address
+`/api/request_send_mail_access` requests access for the mailbox address identified by the Address JWT in `Authorization`, not for a user account. Request access separately for each mailbox address; a User JWT cannot replace the Address JWT on this endpoint.
+:::
+
| Task | Method | Path | Body / Returns |
| ----------------------- | ------ | ------------------------------- | ------------------------------------------- |
| Request send access | POST | `/api/request_send_mail_access` | `{}` → `{ status: "ok" }` |
diff --git a/vitepress-docs/docs/zh/guide/config-send-mail.md b/vitepress-docs/docs/zh/guide/config-send-mail.md
index 7fe40ac..2a9e660 100644
--- a/vitepress-docs/docs/zh/guide/config-send-mail.md
+++ b/vitepress-docs/docs/zh/guide/config-send-mail.md
@@ -152,10 +152,12 @@ wrangler secret put SMTP_CONFIG
## 发信余额机制
-用户发送邮件需要有发信余额。余额机制如下:
+发信权限和额度属于**邮箱地址**,不属于用户账号。用户账号只用于绑定、同步和管理多个邮箱地址;即使已登录用户账号,也需要先切换到具体邮箱地址,再为该地址申请发信权限。每个地址的启用状态和余额互相独立。
+
+邮箱地址发送邮件需要有发信余额。余额机制如下:
1. **自动初始化默认额度**:当 `DEFAULT_SEND_BALANCE > 0` 时,用户打开前端发信页或第一次调用发信接口时,系统会自动为该地址初始化默认额度
-2. **手动申请**:如果 `DEFAULT_SEND_BALANCE = 0`,用户仍可以在前端界面点击「申请发信权限」按钮,创建待管理员处理的发信权限记录
+2. **手动申请**:如果 `DEFAULT_SEND_BALANCE = 0`,用户仍可以在当前邮箱地址的发信页面点击「为当前地址申请发信权限」,创建待管理员处理的地址发信权限记录
3. **无限制发送**:以下方式可以跳过余额检查:
- 在 admin 后台将地址加入「无限制发送地址列表」
- 配置 `NO_LIMIT_SEND_ROLE` 环境变量,指定可以无限发送的用户角色
diff --git a/vitepress-docs/docs/zh/guide/feature/admin-user-management.md b/vitepress-docs/docs/zh/guide/feature/admin-user-management.md
index e85c568..d4b3f24 100644
--- a/vitepress-docs/docs/zh/guide/feature/admin-user-management.md
+++ b/vitepress-docs/docs/zh/guide/feature/admin-user-management.md
@@ -1,11 +1,11 @@
-# Admin 用户相关
+# Admin 用户账号管理
-## 用户管理页面
+## 用户账号管理页面

-## 用户设置
+## 用户账号设置
-此处开启用户登录,以及验证等配置
+此处配置站点用户账号的登录、注册和验证。用户账号用于绑定和管理邮箱地址,本身不是邮箱地址。

diff --git a/vitepress-docs/docs/zh/guide/feature/admin.md b/vitepress-docs/docs/zh/guide/feature/admin.md
index 78285e4..034cacd 100644
--- a/vitepress-docs/docs/zh/guide/feature/admin.md
+++ b/vitepress-docs/docs/zh/guide/feature/admin.md
@@ -8,19 +8,21 @@
需要在后端配置 `ADMIN_PASSWORDS` 或者当前用户角色为 `ADMIN_USER_ROLE`,否则不允许访问控制台。
-## 管理口令和用户账号的区别
+## 管理口令、用户账号和邮箱地址的区别
`ADMIN_PASSWORDS` 是 Admin 控制台的管理口令,不是站点用户账号,也不对应某个邮箱地址。使用管理口令登录后可以进入后台,但它本身不能收信。
-站点用户账号存储在 `users` 表中,需要通过用户登录体系进入;用户是否能收信取决于是否创建或绑定了邮箱地址。即使你创建了一个邮箱为 `admin@example.com` 或用户名看起来像 `admin` 的普通用户,它也不会自动获得后台权限。
+站点用户账号存储在 `users` 表中,需要通过用户登录体系进入。它用于绑定、同步和管理多个邮箱地址,本身没有收件箱、地址凭证或发信额度。
+
+邮箱地址存储在 `address` 表中,每个地址有独立的收件箱、地址凭证和发信权限。用户是否能收信取决于是否创建或绑定了邮箱地址;发信权限也必须为具体邮箱地址申请,而不是为用户账号申请。即使你创建了一个邮箱为 `admin@example.com` 或用户名看起来像 `admin` 的普通用户,它也不会自动获得后台权限。
如果希望某个用户也能进入 Admin 控制台,请配置 `ADMIN_USER_ROLE`,并在用户管理中给该用户设置相同的角色。

-## 账号列表排序
+## 邮箱地址列表排序
-管理后台的账号标签页支持按列排序,可点击表头对以下列进行升序/降序排列:
+管理后台的邮箱地址标签页支持按列排序,可点击表头对以下列进行升序/降序排列:
- ID
- 名称
diff --git a/vitepress-docs/docs/zh/guide/feature/agent-email.md b/vitepress-docs/docs/zh/guide/feature/agent-email.md
index bf1d335..7b1304c 100644
--- a/vitepress-docs/docs/zh/guide/feature/agent-email.md
+++ b/vitepress-docs/docs/zh/guide/feature/agent-email.md
@@ -93,6 +93,10 @@ curl -s "$BASE/api/parsed_mails?limit=20&offset=0" \
需要 `send_balance > 0`(通过 `/api/settings` 查看),且部署方已配置发送方式(Resend / SMTP / Cloudflare Email Routing binding)。
+::: tip 发信权限属于邮箱地址
+`/api/request_send_mail_access` 申请的是 `Authorization` 中 Address JWT 对应邮箱地址的发信权限,不是用户账号的权限。不同邮箱地址需要分别申请;User JWT 不能代替 Address JWT 调用该接口。
+:::
+
| 任务 | 方法 | 路径 | 请求体 / 返回 |
| ---------------- | ------ | ------------------------------- | ------------------------------------------ |
| 申请发信权限 | POST | `/api/request_send_mail_access` | `{}` → `{ status: "ok" }` |