mirror of
https://git.unlock-music.dev/um/um-react.git
synced 2025-05-23 16:27:41 +08:00
refactor: batch 1
This commit is contained in:
parent
089d66cbf4
commit
13c669b4ea
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -1,7 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"editorconfig.editorconfig",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"esbenp.prettier-vscode",
|
||||
"foxundermoon.shell-format"
|
||||
]
|
||||
|
@ -24,6 +24,7 @@
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@unlock-music/crypto": "0.1.10",
|
||||
"classnames": "^2.5.1",
|
||||
"framer-motion": "^12.12.1",
|
||||
"nanoid": "^5.1.5",
|
||||
"next-themes": "^0.4.6",
|
||||
@ -32,8 +33,8 @@
|
||||
"react-dom": "^19.1.0",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-promise-suspense": "^0.3.4",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router": "^7.6.0",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"sass": "^1.89.0",
|
||||
"sql.js": "^1.13.0",
|
||||
@ -42,6 +43,7 @@
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@rollup/plugin-replace": "^6.0.2",
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
@ -67,6 +69,7 @@
|
||||
"prettier": "^3.5.3",
|
||||
"rollup": "^4.40.2",
|
||||
"simple-git-hooks": "^2.13.0",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"terser": "^5.39.2",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
|
535
pnpm-lock.yaml
generated
535
pnpm-lock.yaml
generated
@ -41,6 +41,9 @@ importers:
|
||||
'@unlock-music/crypto':
|
||||
specifier: 0.1.10
|
||||
version: 0.1.10
|
||||
classnames:
|
||||
specifier: ^2.5.1
|
||||
version: 2.5.1
|
||||
framer-motion:
|
||||
specifier: ^12.12.1
|
||||
version: 12.12.1(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
@ -65,12 +68,12 @@ importers:
|
||||
react-icons:
|
||||
specifier: ^5.5.0
|
||||
version: 5.5.0(react@19.1.0)
|
||||
react-promise-suspense:
|
||||
specifier: ^0.3.4
|
||||
version: 0.3.4
|
||||
react-redux:
|
||||
specifier: ^9.2.0
|
||||
version: 9.2.0(@types/react@19.1.4)(react@19.1.0)(redux@5.0.1)
|
||||
react-router:
|
||||
specifier: ^7.6.0
|
||||
version: 7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
react-syntax-highlighter:
|
||||
specifier: ^15.6.1
|
||||
version: 15.6.1(react@19.1.0)
|
||||
@ -90,6 +93,9 @@ importers:
|
||||
'@rollup/plugin-replace':
|
||||
specifier: ^6.0.2
|
||||
version: 6.0.2(rollup@4.40.2)
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.7(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
'@testing-library/jest-dom':
|
||||
specifier: ^6.6.3
|
||||
version: 6.6.3
|
||||
@ -116,13 +122,13 @@ importers:
|
||||
version: 1.4.9
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^8.32.1
|
||||
version: 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0)(typescript@5.8.3))(eslint@9.26.0)(typescript@5.8.3)
|
||||
version: 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.32.1
|
||||
version: 8.32.1(eslint@9.26.0)(typescript@5.8.3)
|
||||
version: 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.4.1
|
||||
version: 4.4.1(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
version: 4.4.1(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3(vitest@3.1.3)
|
||||
@ -134,16 +140,16 @@ importers:
|
||||
version: 5.0.35
|
||||
eslint:
|
||||
specifier: ^9.26.0
|
||||
version: 9.26.0
|
||||
version: 9.26.0(jiti@2.4.2)
|
||||
eslint-config-prettier:
|
||||
specifier: ^10.1.5
|
||||
version: 10.1.5(eslint@9.26.0)
|
||||
version: 10.1.5(eslint@9.26.0(jiti@2.4.2))
|
||||
eslint-plugin-react-hooks:
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0(eslint@9.26.0)
|
||||
version: 5.2.0(eslint@9.26.0(jiti@2.4.2))
|
||||
eslint-plugin-react-refresh:
|
||||
specifier: ^0.4.20
|
||||
version: 0.4.20(eslint@9.26.0)
|
||||
version: 0.4.20(eslint@9.26.0(jiti@2.4.2))
|
||||
globals:
|
||||
specifier: ^16.1.0
|
||||
version: 16.1.0
|
||||
@ -165,6 +171,9 @@ importers:
|
||||
simple-git-hooks:
|
||||
specifier: ^2.13.0
|
||||
version: 2.13.0
|
||||
tailwindcss:
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.7
|
||||
terser:
|
||||
specifier: ^5.39.2
|
||||
version: 5.39.2
|
||||
@ -173,22 +182,22 @@ importers:
|
||||
version: 5.8.3
|
||||
typescript-eslint:
|
||||
specifier: ^8.32.1
|
||||
version: 8.32.1(eslint@9.26.0)(typescript@5.8.3)
|
||||
version: 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
vite:
|
||||
specifier: ^6.3.5
|
||||
version: 6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
version: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite-plugin-pwa:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0)
|
||||
version: 1.0.0(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0)
|
||||
vite-plugin-top-level-await:
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0(@swc/helpers@0.5.17)(rollup@4.40.2)(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
version: 1.5.0(@swc/helpers@0.5.17)(rollup@4.40.2)(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
vite-plugin-wasm:
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.1(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
version: 3.4.1(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
vitest:
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3(@types/node@22.15.18)(@vitest/ui@3.1.3)(jsdom@26.1.0)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
version: 3.1.3(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
workbox-window:
|
||||
specifier: ^7.3.0
|
||||
version: 7.3.0
|
||||
@ -1050,6 +1059,10 @@ packages:
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@isaacs/fs-minipass@4.0.1':
|
||||
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@istanbuljs/schema@0.1.3':
|
||||
resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
|
||||
engines: {node: '>=8'}
|
||||
@ -1451,6 +1464,96 @@ packages:
|
||||
'@swc/types@0.1.21':
|
||||
resolution: {integrity: sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==}
|
||||
|
||||
'@tailwindcss/node@4.1.7':
|
||||
resolution: {integrity: sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==}
|
||||
|
||||
'@tailwindcss/oxide-android-arm64@4.1.7':
|
||||
resolution: {integrity: sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@tailwindcss/oxide-darwin-arm64@4.1.7':
|
||||
resolution: {integrity: sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@tailwindcss/oxide-darwin-x64@4.1.7':
|
||||
resolution: {integrity: sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@tailwindcss/oxide-freebsd-x64@4.1.7':
|
||||
resolution: {integrity: sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7':
|
||||
resolution: {integrity: sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-gnu@4.1.7':
|
||||
resolution: {integrity: sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-musl@4.1.7':
|
||||
resolution: {integrity: sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-gnu@4.1.7':
|
||||
resolution: {integrity: sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-musl@4.1.7':
|
||||
resolution: {integrity: sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@tailwindcss/oxide-wasm32-wasi@4.1.7':
|
||||
resolution: {integrity: sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [wasm32]
|
||||
bundledDependencies:
|
||||
- '@napi-rs/wasm-runtime'
|
||||
- '@emnapi/core'
|
||||
- '@emnapi/runtime'
|
||||
- '@tybys/wasm-util'
|
||||
- '@emnapi/wasi-threads'
|
||||
- tslib
|
||||
|
||||
'@tailwindcss/oxide-win32-arm64-msvc@4.1.7':
|
||||
resolution: {integrity: sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@tailwindcss/oxide-win32-x64-msvc@4.1.7':
|
||||
resolution: {integrity: sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@tailwindcss/oxide@4.1.7':
|
||||
resolution: {integrity: sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@tailwindcss/vite@4.1.7':
|
||||
resolution: {integrity: sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==}
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6
|
||||
|
||||
'@testing-library/dom@10.4.0':
|
||||
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
|
||||
engines: {node: '>=18'}
|
||||
@ -1853,6 +1956,13 @@ packages:
|
||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
|
||||
chownr@3.0.0:
|
||||
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
classnames@2.5.1:
|
||||
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
|
||||
engines: {node: '>=18'}
|
||||
@ -1913,6 +2023,10 @@ packages:
|
||||
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
cookie@1.0.2:
|
||||
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
copy-to-clipboard@3.3.3:
|
||||
resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
|
||||
|
||||
@ -2008,6 +2122,10 @@ packages:
|
||||
engines: {node: '>=0.10'}
|
||||
hasBin: true
|
||||
|
||||
detect-libc@2.0.4:
|
||||
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
detect-node-es@1.1.0:
|
||||
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
||||
|
||||
@ -2048,6 +2166,10 @@ packages:
|
||||
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
enhanced-resolve@5.18.1:
|
||||
resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
entities@6.0.0:
|
||||
resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==}
|
||||
engines: {node: '>=0.12'}
|
||||
@ -2199,9 +2321,6 @@ packages:
|
||||
resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
fast-deep-equal@2.0.1:
|
||||
resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
@ -2688,6 +2807,10 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
jiti@2.4.2:
|
||||
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
|
||||
hasBin: true
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
@ -2755,6 +2878,70 @@ packages:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
lightningcss-darwin-arm64@1.30.1:
|
||||
resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-darwin-x64@1.30.1:
|
||||
resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-freebsd-x64@1.30.1:
|
||||
resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.30.1:
|
||||
resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.30.1:
|
||||
resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.1:
|
||||
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.1:
|
||||
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.1:
|
||||
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.1:
|
||||
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss-win32-x64-msvc@1.30.1:
|
||||
resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss@1.30.1:
|
||||
resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
lilconfig@3.1.3:
|
||||
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
|
||||
engines: {node: '>=14'}
|
||||
@ -2878,6 +3065,15 @@ packages:
|
||||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
minizlib@3.0.2:
|
||||
resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
mkdirp@3.0.1:
|
||||
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
motion-dom@12.12.1:
|
||||
resolution: {integrity: sha512-GXq/uUbZBEiFFE+K1Z/sxdPdadMdfJ/jmBALDfIuHGi0NmtealLOfH9FqT+6aNPgVx8ilq0DtYmyQlo6Uj9LKQ==}
|
||||
|
||||
@ -3157,9 +3353,6 @@ packages:
|
||||
react-is@17.0.2:
|
||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||
|
||||
react-promise-suspense@0.3.4:
|
||||
resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==}
|
||||
|
||||
react-redux@9.2.0:
|
||||
resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==}
|
||||
peerDependencies:
|
||||
@ -3196,6 +3389,16 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
react-router@7.6.0:
|
||||
resolution: {integrity: sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
peerDependencies:
|
||||
react: '>=18'
|
||||
react-dom: '>=18'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
react-style-singleton@2.2.3:
|
||||
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
|
||||
engines: {node: '>=10'}
|
||||
@ -3357,6 +3560,9 @@ packages:
|
||||
resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
set-cookie-parser@2.7.1:
|
||||
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -3527,6 +3733,17 @@ packages:
|
||||
symbol-tree@3.2.4:
|
||||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||
|
||||
tailwindcss@4.1.7:
|
||||
resolution: {integrity: sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==}
|
||||
|
||||
tapable@2.2.1:
|
||||
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tar@7.4.3:
|
||||
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
temp-dir@2.0.0:
|
||||
resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
|
||||
engines: {node: '>=8'}
|
||||
@ -3973,6 +4190,10 @@ packages:
|
||||
yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
|
||||
yallist@5.0.0:
|
||||
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
yaml@1.10.2:
|
||||
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
|
||||
engines: {node: '>= 6'}
|
||||
@ -4913,9 +5134,9 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@eslint-community/eslint-utils@4.7.0(eslint@9.26.0)':
|
||||
'@eslint-community/eslint-utils@4.7.0(eslint@9.26.0(jiti@2.4.2))':
|
||||
dependencies:
|
||||
eslint: 9.26.0
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.1': {}
|
||||
@ -4979,6 +5200,10 @@ snapshots:
|
||||
wrap-ansi: 8.1.0
|
||||
wrap-ansi-cjs: wrap-ansi@7.0.0
|
||||
|
||||
'@isaacs/fs-minipass@4.0.1':
|
||||
dependencies:
|
||||
minipass: 7.1.2
|
||||
|
||||
'@istanbuljs/schema@0.1.3': {}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.8':
|
||||
@ -5310,6 +5535,77 @@ snapshots:
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
|
||||
'@tailwindcss/node@4.1.7':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
enhanced-resolve: 5.18.1
|
||||
jiti: 2.4.2
|
||||
lightningcss: 1.30.1
|
||||
magic-string: 0.30.17
|
||||
source-map-js: 1.2.1
|
||||
tailwindcss: 4.1.7
|
||||
|
||||
'@tailwindcss/oxide-android-arm64@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-darwin-arm64@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-darwin-x64@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-freebsd-x64@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-gnu@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-musl@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-gnu@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-musl@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-wasm32-wasi@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-win32-arm64-msvc@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-win32-x64-msvc@4.1.7':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide@4.1.7':
|
||||
dependencies:
|
||||
detect-libc: 2.0.4
|
||||
tar: 7.4.3
|
||||
optionalDependencies:
|
||||
'@tailwindcss/oxide-android-arm64': 4.1.7
|
||||
'@tailwindcss/oxide-darwin-arm64': 4.1.7
|
||||
'@tailwindcss/oxide-darwin-x64': 4.1.7
|
||||
'@tailwindcss/oxide-freebsd-x64': 4.1.7
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.7
|
||||
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.7
|
||||
'@tailwindcss/oxide-linux-arm64-musl': 4.1.7
|
||||
'@tailwindcss/oxide-linux-x64-gnu': 4.1.7
|
||||
'@tailwindcss/oxide-linux-x64-musl': 4.1.7
|
||||
'@tailwindcss/oxide-wasm32-wasi': 4.1.7
|
||||
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.7
|
||||
'@tailwindcss/oxide-win32-x64-msvc': 4.1.7
|
||||
|
||||
'@tailwindcss/vite@4.1.7(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@tailwindcss/node': 4.1.7
|
||||
'@tailwindcss/oxide': 4.1.7
|
||||
tailwindcss: 4.1.7
|
||||
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
|
||||
'@testing-library/dom@10.4.0':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
@ -5417,15 +5713,15 @@ snapshots:
|
||||
|
||||
'@types/use-sync-external-store@0.0.6': {}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0)(typescript@5.8.3))(eslint@9.26.0)(typescript@5.8.3)':
|
||||
'@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@typescript-eslint/parser': 8.32.1(eslint@9.26.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
'@typescript-eslint/scope-manager': 8.32.1
|
||||
'@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.32.1(eslint@9.26.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
'@typescript-eslint/visitor-keys': 8.32.1
|
||||
eslint: 9.26.0
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
graphemer: 1.4.0
|
||||
ignore: 7.0.4
|
||||
natural-compare: 1.4.0
|
||||
@ -5434,14 +5730,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.32.1(eslint@9.26.0)(typescript@5.8.3)':
|
||||
'@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.32.1
|
||||
'@typescript-eslint/types': 8.32.1
|
||||
'@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3)
|
||||
'@typescript-eslint/visitor-keys': 8.32.1
|
||||
debug: 4.4.1
|
||||
eslint: 9.26.0
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@ -5451,12 +5747,12 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.32.1
|
||||
'@typescript-eslint/visitor-keys': 8.32.1
|
||||
|
||||
'@typescript-eslint/type-utils@8.32.1(eslint@9.26.0)(typescript@5.8.3)':
|
||||
'@typescript-eslint/type-utils@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.32.1(eslint@9.26.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
debug: 4.4.1
|
||||
eslint: 9.26.0
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
ts-api-utils: 2.1.0(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
@ -5478,13 +5774,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.32.1(eslint@9.26.0)(typescript@5.8.3)':
|
||||
'@typescript-eslint/utils@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0)
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2))
|
||||
'@typescript-eslint/scope-manager': 8.32.1
|
||||
'@typescript-eslint/types': 8.32.1
|
||||
'@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3)
|
||||
eslint: 9.26.0
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@ -5496,14 +5792,14 @@ snapshots:
|
||||
|
||||
'@unlock-music/crypto@0.1.10': {}
|
||||
|
||||
'@vitejs/plugin-react@4.4.1(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))':
|
||||
'@vitejs/plugin-react@4.4.1(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.1
|
||||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.1)
|
||||
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.1)
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.17.0
|
||||
vite: 6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -5521,7 +5817,7 @@ snapshots:
|
||||
std-env: 3.9.0
|
||||
test-exclude: 7.0.1
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.1.3(@types/node@22.15.18)(@vitest/ui@3.1.3)(jsdom@26.1.0)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vitest: 3.1.3(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -5532,13 +5828,13 @@ snapshots:
|
||||
chai: 5.2.0
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.1.3(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))':
|
||||
'@vitest/mocker@3.1.3(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.1.3
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
optionalDependencies:
|
||||
vite: 6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
|
||||
'@vitest/pretty-format@3.1.3':
|
||||
dependencies:
|
||||
@ -5568,7 +5864,7 @@ snapshots:
|
||||
sirv: 3.0.1
|
||||
tinyglobby: 0.2.13
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.1.3(@types/node@22.15.18)(@vitest/ui@3.1.3)(jsdom@26.1.0)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vitest: 3.1.3(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
|
||||
'@vitest/utils@3.1.3':
|
||||
dependencies:
|
||||
@ -5793,6 +6089,10 @@ snapshots:
|
||||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
|
||||
chownr@3.0.0: {}
|
||||
|
||||
classnames@2.5.1: {}
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
dependencies:
|
||||
restore-cursor: 5.1.0
|
||||
@ -5836,6 +6136,8 @@ snapshots:
|
||||
|
||||
cookie@0.7.2: {}
|
||||
|
||||
cookie@1.0.2: {}
|
||||
|
||||
copy-to-clipboard@3.3.3:
|
||||
dependencies:
|
||||
toggle-selection: 1.0.6
|
||||
@ -5930,6 +6232,8 @@ snapshots:
|
||||
detect-libc@1.0.3:
|
||||
optional: true
|
||||
|
||||
detect-libc@2.0.4: {}
|
||||
|
||||
detect-node-es@1.1.0: {}
|
||||
|
||||
dom-accessibility-api@0.5.16: {}
|
||||
@ -5960,6 +6264,11 @@ snapshots:
|
||||
|
||||
encodeurl@2.0.0: {}
|
||||
|
||||
enhanced-resolve@5.18.1:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.2.1
|
||||
|
||||
entities@6.0.0: {}
|
||||
|
||||
environment@1.1.0: {}
|
||||
@ -6079,17 +6388,17 @@ snapshots:
|
||||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
eslint-config-prettier@10.1.5(eslint@9.26.0):
|
||||
eslint-config-prettier@10.1.5(eslint@9.26.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
eslint: 9.26.0
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
|
||||
eslint-plugin-react-hooks@5.2.0(eslint@9.26.0):
|
||||
eslint-plugin-react-hooks@5.2.0(eslint@9.26.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
eslint: 9.26.0
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
|
||||
eslint-plugin-react-refresh@0.4.20(eslint@9.26.0):
|
||||
eslint-plugin-react-refresh@0.4.20(eslint@9.26.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
eslint: 9.26.0
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
|
||||
eslint-scope@8.3.0:
|
||||
dependencies:
|
||||
@ -6100,9 +6409,9 @@ snapshots:
|
||||
|
||||
eslint-visitor-keys@4.2.0: {}
|
||||
|
||||
eslint@9.26.0:
|
||||
eslint@9.26.0(jiti@2.4.2):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0)
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2))
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@eslint/config-array': 0.20.0
|
||||
'@eslint/config-helpers': 0.2.2
|
||||
@ -6139,6 +6448,8 @@ snapshots:
|
||||
natural-compare: 1.4.0
|
||||
optionator: 0.9.4
|
||||
zod: 3.24.4
|
||||
optionalDependencies:
|
||||
jiti: 2.4.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -6216,8 +6527,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
fast-deep-equal@2.0.1: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-glob@3.3.3:
|
||||
@ -6706,6 +7015,8 @@ snapshots:
|
||||
filelist: 1.0.4
|
||||
minimatch: 3.1.2
|
||||
|
||||
jiti@2.4.2: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
@ -6776,6 +7087,51 @@ snapshots:
|
||||
prelude-ls: 1.2.1
|
||||
type-check: 0.4.0
|
||||
|
||||
lightningcss-darwin-arm64@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-x64@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-freebsd-x64@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-x64-msvc@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss@1.30.1:
|
||||
dependencies:
|
||||
detect-libc: 2.0.4
|
||||
optionalDependencies:
|
||||
lightningcss-darwin-arm64: 1.30.1
|
||||
lightningcss-darwin-x64: 1.30.1
|
||||
lightningcss-freebsd-x64: 1.30.1
|
||||
lightningcss-linux-arm-gnueabihf: 1.30.1
|
||||
lightningcss-linux-arm64-gnu: 1.30.1
|
||||
lightningcss-linux-arm64-musl: 1.30.1
|
||||
lightningcss-linux-x64-gnu: 1.30.1
|
||||
lightningcss-linux-x64-musl: 1.30.1
|
||||
lightningcss-win32-arm64-msvc: 1.30.1
|
||||
lightningcss-win32-x64-msvc: 1.30.1
|
||||
|
||||
lilconfig@3.1.3: {}
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
@ -6900,6 +7256,12 @@ snapshots:
|
||||
|
||||
minipass@7.1.2: {}
|
||||
|
||||
minizlib@3.0.2:
|
||||
dependencies:
|
||||
minipass: 7.1.2
|
||||
|
||||
mkdirp@3.0.1: {}
|
||||
|
||||
motion-dom@12.12.1:
|
||||
dependencies:
|
||||
motion-utils: 12.12.1
|
||||
@ -7144,10 +7506,6 @@ snapshots:
|
||||
|
||||
react-is@17.0.2: {}
|
||||
|
||||
react-promise-suspense@0.3.4:
|
||||
dependencies:
|
||||
fast-deep-equal: 2.0.1
|
||||
|
||||
react-redux@9.2.0(@types/react@19.1.4)(react@19.1.0)(redux@5.0.1):
|
||||
dependencies:
|
||||
'@types/use-sync-external-store': 0.0.6
|
||||
@ -7178,6 +7536,14 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.4
|
||||
|
||||
react-router@7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
cookie: 1.0.2
|
||||
react: 19.1.0
|
||||
set-cookie-parser: 2.7.1
|
||||
optionalDependencies:
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
react-style-singleton@2.2.3(@types/react@19.1.4)(react@19.1.0):
|
||||
dependencies:
|
||||
get-nonce: 1.0.1
|
||||
@ -7395,6 +7761,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
set-cookie-parser@2.7.1: {}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
dependencies:
|
||||
define-data-property: 1.1.4
|
||||
@ -7593,6 +7961,19 @@ snapshots:
|
||||
|
||||
symbol-tree@3.2.4: {}
|
||||
|
||||
tailwindcss@4.1.7: {}
|
||||
|
||||
tapable@2.2.1: {}
|
||||
|
||||
tar@7.4.3:
|
||||
dependencies:
|
||||
'@isaacs/fs-minipass': 4.0.1
|
||||
chownr: 3.0.0
|
||||
minipass: 7.1.2
|
||||
minizlib: 3.0.2
|
||||
mkdirp: 3.0.1
|
||||
yallist: 5.0.0
|
||||
|
||||
temp-dir@2.0.0: {}
|
||||
|
||||
tempy@0.6.0:
|
||||
@ -7711,12 +8092,12 @@ snapshots:
|
||||
possible-typed-array-names: 1.1.0
|
||||
reflect.getprototypeof: 1.0.10
|
||||
|
||||
typescript-eslint@8.32.1(eslint@9.26.0)(typescript@5.8.3):
|
||||
typescript-eslint@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0)(typescript@5.8.3))(eslint@9.26.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/parser': 8.32.1(eslint@9.26.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.32.1(eslint@9.26.0)(typescript@5.8.3)
|
||||
eslint: 9.26.0
|
||||
'@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
'@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
eslint: 9.26.0(jiti@2.4.2)
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@ -7786,13 +8167,13 @@ snapshots:
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
vite-node@3.1.3(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0):
|
||||
vite-node@3.1.3(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.1
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
@ -7807,32 +8188,32 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite-plugin-pwa@1.0.0(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0):
|
||||
vite-plugin-pwa@1.0.0(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0):
|
||||
dependencies:
|
||||
debug: 4.4.1
|
||||
pretty-bytes: 6.1.1
|
||||
tinyglobby: 0.2.13
|
||||
vite: 6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
workbox-build: 7.3.0(@types/babel__core@7.20.5)
|
||||
workbox-window: 7.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-top-level-await@1.5.0(@swc/helpers@0.5.17)(rollup@4.40.2)(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)):
|
||||
vite-plugin-top-level-await@1.5.0(@swc/helpers@0.5.17)(rollup@4.40.2)(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)):
|
||||
dependencies:
|
||||
'@rollup/plugin-virtual': 3.0.2(rollup@4.40.2)
|
||||
'@swc/core': 1.11.24(@swc/helpers@0.5.17)
|
||||
uuid: 10.0.0
|
||||
vite: 6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- '@swc/helpers'
|
||||
- rollup
|
||||
|
||||
vite-plugin-wasm@3.4.1(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)):
|
||||
vite-plugin-wasm@3.4.1(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)):
|
||||
dependencies:
|
||||
vite: 6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
|
||||
vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0):
|
||||
vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0):
|
||||
dependencies:
|
||||
esbuild: 0.25.4
|
||||
fdir: 6.4.4(picomatch@4.0.2)
|
||||
@ -7843,14 +8224,16 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.18
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.4.2
|
||||
lightningcss: 1.30.1
|
||||
sass: 1.89.0
|
||||
terser: 5.39.2
|
||||
yaml: 2.8.0
|
||||
|
||||
vitest@3.1.3(@types/node@22.15.18)(@vitest/ui@3.1.3)(jsdom@26.1.0)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0):
|
||||
vitest@3.1.3(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0):
|
||||
dependencies:
|
||||
'@vitest/expect': 3.1.3
|
||||
'@vitest/mocker': 3.1.3(vite@6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
'@vitest/mocker': 3.1.3(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0))
|
||||
'@vitest/pretty-format': 3.1.3
|
||||
'@vitest/runner': 3.1.3
|
||||
'@vitest/snapshot': 3.1.3
|
||||
@ -7867,8 +8250,8 @@ snapshots:
|
||||
tinyglobby: 0.2.13
|
||||
tinypool: 1.0.2
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 6.3.5(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite-node: 3.1.3(@types/node@22.15.18)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
vite-node: 3.1.3(@types/node@22.15.18)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.0)(terser@5.39.2)(yaml@2.8.0)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.18
|
||||
@ -8108,6 +8491,8 @@ snapshots:
|
||||
|
||||
yallist@3.1.1: {}
|
||||
|
||||
yallist@5.0.0: {}
|
||||
|
||||
yaml@1.10.2: {}
|
||||
|
||||
yaml@2.8.0: {}
|
||||
|
17
src/App.css
Normal file
17
src/App.css
Normal file
@ -0,0 +1,17 @@
|
||||
@import 'tailwindcss';
|
||||
@plugin "daisyui";
|
||||
@theme {
|
||||
--font-display:
|
||||
system-ui, Sarasa UI SC, Source Han Sans CN, Noto Sans CJK SC, sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
||||
--font-mono:
|
||||
ui-monospace, Consolas, Sarasa Mono CJK SC, Sarasa UI SC, Source Han Sans CN, Noto Sans CJK SC, Microsoft YaHei UI,
|
||||
monospace, Apple Color Emoji, Segoe UI Emoji;
|
||||
}
|
||||
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
@ -1,16 +1,4 @@
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
HStack,
|
||||
Icon,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
} from '@chakra-ui/react';
|
||||
import { MdAdd, MdDeleteForever, MdExpandMore, MdFileUpload } from 'react-icons/md';
|
||||
import { MdAdd, MdDeleteForever, MdFileUpload } from 'react-icons/md';
|
||||
|
||||
export interface AddKeyProps {
|
||||
addKey: () => void;
|
||||
@ -20,28 +8,20 @@ export interface AddKeyProps {
|
||||
|
||||
export function AddKey({ addKey, importKeyFromFile, clearKeys }: AddKeyProps) {
|
||||
return (
|
||||
<HStack pb={2} pt={2}>
|
||||
<ButtonGroup isAttached colorScheme="purple" variant="outline">
|
||||
<Button onClick={addKey} leftIcon={<Icon as={MdAdd} />}>
|
||||
添加一条密钥
|
||||
</Button>
|
||||
<Menu>
|
||||
<MenuButton as={IconButton} icon={<MdExpandMore />}></MenuButton>
|
||||
<MenuList>
|
||||
{importKeyFromFile && (
|
||||
<MenuItem onClick={importKeyFromFile} icon={<Icon as={MdFileUpload} boxSize={5} />}>
|
||||
从文件导入密钥…
|
||||
</MenuItem>
|
||||
)}
|
||||
{importKeyFromFile && clearKeys && <MenuDivider />}
|
||||
{clearKeys && (
|
||||
<MenuItem color="red" onClick={clearKeys} icon={<Icon as={MdDeleteForever} boxSize={5} />}>
|
||||
清空密钥
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</ButtonGroup>
|
||||
</HStack>
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
<div className="join">
|
||||
<button className="btn join-item" onClick={addKey}>
|
||||
<MdAdd /> 添加一条
|
||||
</button>
|
||||
<button className="btn join-item" onClick={importKeyFromFile}>
|
||||
<MdFileUpload />
|
||||
导入数据库…
|
||||
</button>
|
||||
<button className="btn btn-error join-item" onClick={clearKeys}>
|
||||
<MdDeleteForever />
|
||||
清空密钥
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { useEffect } from 'react';
|
||||
import { BrowserRouter, NavLink, Route, Routes } from 'react-router';
|
||||
import { MdSettings, MdHome, MdQuestionAnswer } from 'react-icons/md';
|
||||
import { ChakraProvider, Tabs, TabList, TabPanels, Tab, TabPanel, Icon, chakra } from '@chakra-ui/react';
|
||||
|
||||
import { MainTab } from '~/tabs/MainTab';
|
||||
import { SettingsTab } from '~/tabs/SettingsTab';
|
||||
|
||||
import { Provider } from 'react-redux';
|
||||
import { theme } from '~/theme';
|
||||
import { persistSettings } from '~/features/settings/persistSettings';
|
||||
import { setupStore } from '~/store';
|
||||
import { Footer } from '~/components/Footer';
|
||||
@ -15,43 +14,39 @@ import { FaqTab } from '~/tabs/FaqTab';
|
||||
// Private to this file only.
|
||||
const store = setupStore();
|
||||
|
||||
const tabClassNames = ({ isActive }: { isActive: boolean }) => `tab ${isActive ? 'tab-active' : ''}`;
|
||||
|
||||
export function AppRoot() {
|
||||
useEffect(() => persistSettings(store), []);
|
||||
|
||||
return (
|
||||
<ChakraProvider theme={theme}>
|
||||
<BrowserRouter>
|
||||
<Provider store={store}>
|
||||
<Tabs flex={1} minH={0} display="flex" flexDir="column">
|
||||
<TabList justifyContent="center">
|
||||
<Tab>
|
||||
<Icon as={MdHome} mr="1" />
|
||||
<chakra.span>应用</chakra.span>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<Icon as={MdSettings} mr="1" />
|
||||
<chakra.span>设置</chakra.span>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<Icon as={MdQuestionAnswer} mr="1" />
|
||||
<chakra.span>答疑</chakra.span>
|
||||
</Tab>
|
||||
</TabList>
|
||||
<div role="tablist" className="tabs tabs-border w-full justify-center">
|
||||
<NavLink to="/" role="tab" className={tabClassNames}>
|
||||
<MdHome />
|
||||
应用
|
||||
</NavLink>
|
||||
<NavLink to="/settings" role="tab" className={tabClassNames}>
|
||||
<MdSettings />
|
||||
设置
|
||||
</NavLink>
|
||||
<NavLink to="/questions" role="tab" className={tabClassNames}>
|
||||
<MdQuestionAnswer />
|
||||
答疑
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<TabPanels overflow="auto" minW={0} flexDir="column" flex={1} display="flex">
|
||||
<TabPanel>
|
||||
<MainTab />
|
||||
</TabPanel>
|
||||
<TabPanel flex={1} display="flex">
|
||||
<SettingsTab />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<FaqTab />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
<main className="flex-1 flex justify-center">
|
||||
<Routes>
|
||||
<Route path="/" Component={MainTab} />
|
||||
<Route path="/settings" Component={SettingsTab} />
|
||||
<Route path="/questions" Component={FaqTab} />
|
||||
</Routes>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</Provider>
|
||||
</ChakraProvider>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
40
src/components/Dialog.tsx
Normal file
40
src/components/Dialog.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export interface DialogProps {
|
||||
closeButton?: boolean;
|
||||
backdropClose?: boolean;
|
||||
title?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function Dialog({ closeButton, backdropClose, title, children, show, onClose }: DialogProps) {
|
||||
const refModel = useRef<HTMLDialogElement>(null);
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
refModel.current?.showModal();
|
||||
} else {
|
||||
refModel.current?.close();
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<dialog ref={refModel} className="modal">
|
||||
<div className="modal-box">
|
||||
{closeButton && (
|
||||
<form method="dialog" onSubmit={onClose}>
|
||||
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||
</form>
|
||||
)}
|
||||
<h3 className="font-bold text-lg">{title}</h3>
|
||||
{children}
|
||||
</div>
|
||||
{backdropClose && (
|
||||
<form method="dialog" className="modal-backdrop" onSubmit={onClose}>
|
||||
<button>close</button>
|
||||
</form>
|
||||
)}
|
||||
</dialog>
|
||||
);
|
||||
}
|
@ -1,12 +1,15 @@
|
||||
import type { AnchorHTMLAttributes } from 'react';
|
||||
import { ExternalLinkIcon } from '@chakra-ui/icons';
|
||||
import { Link } from '@chakra-ui/react';
|
||||
import { FiExternalLink } from 'react-icons/fi';
|
||||
|
||||
export function ExtLink({ children, ...props }: AnchorHTMLAttributes<HTMLAnchorElement>) {
|
||||
export type ExtLinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
|
||||
icon?: boolean;
|
||||
};
|
||||
|
||||
export function ExtLink({ className, icon = true, children, ...props }: ExtLinkProps) {
|
||||
return (
|
||||
<Link isExternal {...props} rel="noreferrer noopener nofollow">
|
||||
<a rel="noreferrer noopener nofollow" className={`link ${className}`} {...props}>
|
||||
{children}
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
{icon && <FiExternalLink />}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import classnames from 'classnames';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
|
||||
export interface FileInputProps {
|
||||
onReceiveFiles: (files: File[]) => void;
|
||||
@ -14,30 +14,19 @@ export function FileInput({ children, onReceiveFiles }: FileInputProps) {
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
<div
|
||||
{...getRootProps()}
|
||||
w="100%"
|
||||
maxW={480}
|
||||
borderWidth="1px"
|
||||
borderRadius="lg"
|
||||
transitionDuration="0.5s"
|
||||
p="6"
|
||||
cursor="pointer"
|
||||
display="flex"
|
||||
flexDir="column"
|
||||
alignItems="center"
|
||||
_hover={{
|
||||
borderColor: 'gray.400',
|
||||
bg: 'gray.50',
|
||||
}}
|
||||
{...(isDragActive && {
|
||||
bg: 'blue.50',
|
||||
borderColor: 'blue.700',
|
||||
})}
|
||||
className={classnames(
|
||||
'w-full max-w-xl border rounded-lg transition duration-500 p-6 border-base-300 mx-auto',
|
||||
'cursor-pointer flex flex-col items-center hover:border-gray-400 hover:bg-gray-50',
|
||||
{
|
||||
'bg-blue-50 border-blue-700': isDragActive,
|
||||
},
|
||||
)}
|
||||
tabIndex={0}
|
||||
>
|
||||
<input {...getInputProps()} />
|
||||
|
||||
{children}
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { Code, Text } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
export function FilePathBlock({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Text as="pre" whiteSpace="pre-wrap" wordBreak="break-all">
|
||||
<Code>{children}</Code>
|
||||
</Text>
|
||||
<pre className="whitespace-pre-wrap break-all">
|
||||
<code>{children}</code>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
@ -1,45 +1,28 @@
|
||||
import { Center, Flex, Link, Text } from '@chakra-ui/react';
|
||||
import { Suspense } from 'react';
|
||||
import { SDKVersion } from './SDKVersion';
|
||||
import { CurrentYear } from './CurrentYear';
|
||||
|
||||
export function Footer() {
|
||||
const appVersionShort = '__APP_VERSION_SHORT__';
|
||||
return (
|
||||
<Center
|
||||
fontSize="sm"
|
||||
textAlign="center"
|
||||
bottom="0"
|
||||
w="full"
|
||||
pt="3"
|
||||
pb="3"
|
||||
borderTop="1px solid"
|
||||
borderColor="gray.300"
|
||||
bg="gray.100"
|
||||
color="gray.800"
|
||||
flexDir="column"
|
||||
flexShrink={0}
|
||||
>
|
||||
<Flex as={Text}>
|
||||
<Link href="https://git.unlock-music.dev/um/um-react" isExternal>
|
||||
<footer className="flex flex-col text-center p-4 bg-base-200">
|
||||
<p className="flex flex-row justify-center items-center h-[1em]">
|
||||
<a className="link link-info mr-1" href="https://git.unlock-music.dev/um/um-react">
|
||||
音乐解锁
|
||||
</Link>
|
||||
{' (__APP_VERSION_SHORT__'}
|
||||
<Suspense>
|
||||
<SDKVersion />
|
||||
</Suspense>
|
||||
{') - 移除已购音乐的加密保护。'}
|
||||
</Flex>
|
||||
<Text>
|
||||
</a>
|
||||
(v{appVersionShort}
|
||||
<SDKVersion />) - 移除已购音乐的加密保护。
|
||||
</p>
|
||||
<p>
|
||||
{'© 2019 - '}
|
||||
<CurrentYear />{' '}
|
||||
<Link href="https://git.unlock-music.dev/um" isExternal>
|
||||
<a className="link link-info" href="https://git.unlock-music.dev/um">
|
||||
UnlockMusic 团队
|
||||
</Link>
|
||||
</a>
|
||||
{' | '}
|
||||
<Link href="https://git.unlock-music.dev/um/um-react/src/branch/main/LICENSE" isExternal>
|
||||
<a className="link link-info" href="https://git.unlock-music.dev/um/um-react/src/branch/main/LICENSE">
|
||||
使用 MIT 授权协议
|
||||
</Link>
|
||||
</Text>
|
||||
</Center>
|
||||
</a>
|
||||
</p>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,4 @@
|
||||
import {
|
||||
Center,
|
||||
Flex,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Tabs,
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { FileInput } from '~/components/FileInput';
|
||||
|
||||
@ -18,39 +7,43 @@ export interface ImportSecretModalProps {
|
||||
children: React.ReactNode;
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
onImport: (file: File) => void|Promise<void>;
|
||||
onImport: (file: File) => void | Promise<void>;
|
||||
}
|
||||
|
||||
export function ImportSecretModal({ clientName, children, show, onClose, onImport }: ImportSecretModalProps) {
|
||||
const handleFileReceived = (files: File[]) => {
|
||||
const promise = onImport(files[0]);
|
||||
if (promise instanceof Promise) {
|
||||
promise.catch(err => {
|
||||
promise.catch((err) => {
|
||||
console.error('could not import: ', err);
|
||||
});
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={show} onClose={onClose} closeOnOverlayClick={false} scrollBehavior="inside" size="xl">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>从文件导入密钥</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Flex as={ModalBody} gap={2} flexDir="column" flex={1}>
|
||||
<Center>
|
||||
<FileInput onReceiveFiles={handleFileReceived}>拖放或点我选择含有密钥的数据库文件</FileInput>
|
||||
</Center>
|
||||
const refModel = useRef<HTMLDialogElement>(null);
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
refModel.current?.showModal();
|
||||
} else {
|
||||
refModel.current?.close();
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
<Text as="div" mt={2}>
|
||||
选择你的{clientName && <>「{clientName}」</>}客户端平台以查看对应说明:
|
||||
</Text>
|
||||
<Flex as={Tabs} variant="enclosed" flexDir="column" flex={1} minH={0}>
|
||||
{children}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
return (
|
||||
<dialog ref={refModel} className="modal">
|
||||
<div className="modal-box">
|
||||
<form method="dialog" onSubmit={() => onClose()}>
|
||||
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||
</form>
|
||||
<h3 className="font-bold text-lg">从文件导入密钥</h3>
|
||||
<div className="py-4 flex flex-col gap-2 flex-1">
|
||||
<FileInput onReceiveFiles={handleFileReceived}>拖放或点我选择含有密钥的数据库文件</FileInput>
|
||||
|
||||
<div className="mt-2">选择你的{clientName && <>「{clientName}」</>}客户端平台以查看对应说明:</div>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
);
|
||||
}
|
||||
|
@ -1,33 +1,39 @@
|
||||
import { InfoOutlineIcon } from '@chakra-ui/icons';
|
||||
import { Tooltip, VStack, Text, Flex } from '@chakra-ui/react';
|
||||
import { MdInfoOutline } from 'react-icons/md';
|
||||
import { workerClientBus } from '~/decrypt-worker/client';
|
||||
import { DECRYPTION_WORKER_ACTION_NAME } from '~/decrypt-worker/constants';
|
||||
|
||||
import usePromise from 'react-promise-suspense';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
const getSDKVersion = async (): Promise<string> => {
|
||||
return workerClientBus.request(DECRYPTION_WORKER_ACTION_NAME.VERSION, null);
|
||||
};
|
||||
|
||||
export function SDKVersion() {
|
||||
const sdkVersion = usePromise(getSDKVersion, []);
|
||||
const refDialog = useRef<HTMLDialogElement>(null);
|
||||
const [sdkVersion, setSdkVersion] = useState('...');
|
||||
useEffect(() => {
|
||||
getSDKVersion().then(setSdkVersion);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex as="span" pl="1" alignItems="center" data-testid="sdk-version">
|
||||
<Tooltip
|
||||
hasArrow
|
||||
placement="top"
|
||||
label={
|
||||
<VStack>
|
||||
<Text>App: __APP_VERSION__</Text>
|
||||
<Text>SDK: {sdkVersion}</Text>
|
||||
</VStack>
|
||||
}
|
||||
bg="gray.300"
|
||||
color="black"
|
||||
>
|
||||
<InfoOutlineIcon />
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
<>
|
||||
<span className="btn btn-ghost inline-flex p-0 h-[1em]" onClick={() => refDialog.current?.showModal()}>
|
||||
<MdInfoOutline />
|
||||
</span>
|
||||
|
||||
<dialog ref={refDialog} className="modal text-left">
|
||||
<div className="modal-box">
|
||||
<form method="dialog">
|
||||
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||
</form>
|
||||
<h3 className="font-bold text-lg">详细信息</h3>
|
||||
|
||||
<p>App: __APP_VERSION__</p>
|
||||
<p>SDK: {sdkVersion}</p>
|
||||
</div>
|
||||
<form method="dialog" className="modal-backdrop">
|
||||
<button>关闭</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Box, Text } from '@chakra-ui/react';
|
||||
import { UnlockIcon } from '@chakra-ui/icons';
|
||||
import { FiUnlock } from 'react-icons/fi';
|
||||
|
||||
import { useAppDispatch } from '~/hooks';
|
||||
import { addNewFile, processFile } from '~/features/file-listing/fileListingSlice';
|
||||
@ -12,7 +11,7 @@ export function SelectFile() {
|
||||
console.debug(
|
||||
'react-dropzone/onDropAccepted(%o, %o)',
|
||||
files.length,
|
||||
files.map((x) => x.name)
|
||||
files.map((x) => x.name),
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
@ -26,7 +25,7 @@ export function SelectFile() {
|
||||
id: fileId,
|
||||
blobURI,
|
||||
fileName,
|
||||
})
|
||||
}),
|
||||
);
|
||||
dispatch(processFile({ fileId }));
|
||||
}
|
||||
@ -34,19 +33,13 @@ export function SelectFile() {
|
||||
|
||||
return (
|
||||
<FileInput multiple onReceiveFiles={handleFileReceived}>
|
||||
<Box pb={3}>
|
||||
<UnlockIcon boxSize={8} />
|
||||
</Box>
|
||||
<Text as="div" textAlign="center">
|
||||
<FiUnlock className="size-8 mb-4" />
|
||||
<p className="text-center">
|
||||
拖放或
|
||||
<Text as="span" color="teal.400">
|
||||
点我选择
|
||||
</Text>
|
||||
<span className="text-teal-700 font-semibold">点我选择</span>
|
||||
需要解密的文件
|
||||
<Text fontSize="sm" opacity="50%">
|
||||
在浏览器内对文件进行解锁,零上传
|
||||
</Text>
|
||||
</Text>
|
||||
</p>
|
||||
<p className="text-sm opacity-50 m-0">在浏览器内对文件进行解锁,零上传</p>
|
||||
</FileInput>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { VStack } from '@chakra-ui/react';
|
||||
|
||||
import { selectFiles } from './fileListingSlice';
|
||||
import { useAppSelector } from '~/hooks';
|
||||
import { FileRow } from './FileRow';
|
||||
@ -8,10 +6,10 @@ export function FileListing() {
|
||||
const files = useAppSelector(selectFiles);
|
||||
|
||||
return (
|
||||
<VStack>
|
||||
<div className="flex flex-row flex-wrap gap-8">
|
||||
{Object.entries(files).map(([id, file]) => (
|
||||
<FileRow key={id} id={id} file={file} />
|
||||
))}
|
||||
</VStack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,24 +1,9 @@
|
||||
import { useRef } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
Collapse,
|
||||
GridItem,
|
||||
Link,
|
||||
VStack,
|
||||
Wrap,
|
||||
WrapItem,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { FileRowResponsiveGrid } from './FileRowResponsiveGrid';
|
||||
import { DecryptedAudioFile, deleteFile, ProcessState } from './fileListingSlice';
|
||||
import { useAppDispatch } from '~/hooks';
|
||||
import { AnimationDefinition } from 'framer-motion';
|
||||
import { AlbumImage } from './AlbumImage';
|
||||
import { SongMetadata } from './SongMetadata';
|
||||
import { FileError } from './FileError';
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface FileRowProps {
|
||||
id: string;
|
||||
@ -26,7 +11,7 @@ interface FileRowProps {
|
||||
}
|
||||
|
||||
export function FileRow({ id, file }: FileRowProps) {
|
||||
const { isOpen, onClose } = useDisclosure({ defaultIsOpen: true });
|
||||
// const { isOpen, onClose } = useDisclosure({ defaultIsOpen: true });
|
||||
const dispatch = useAppDispatch();
|
||||
const isDecrypted = file.state === ProcessState.COMPLETE;
|
||||
const metadata = file.metadata;
|
||||
@ -35,81 +20,54 @@ export function FileRow({ id, file }: FileRowProps) {
|
||||
const decryptedName = nameWithoutExt + '.' + file.ext;
|
||||
|
||||
const audioPlayerRef = useRef<HTMLAudioElement>(null);
|
||||
const togglePlay = () => {
|
||||
const player = audioPlayerRef.current;
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.paused) {
|
||||
player.play();
|
||||
} else {
|
||||
player.pause();
|
||||
}
|
||||
};
|
||||
|
||||
const onCollapseAnimationComplete = (definition: AnimationDefinition) => {
|
||||
const _onCollapseAnimationComplete = (definition: AnimationDefinition) => {
|
||||
if (definition === 'exit') {
|
||||
dispatch(deleteFile({ id }));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
in={isOpen}
|
||||
animateOpacity
|
||||
unmountOnExit
|
||||
startingHeight={0}
|
||||
onAnimationComplete={onCollapseAnimationComplete}
|
||||
style={{ width: '100%', padding: '0.25rem' }}
|
||||
>
|
||||
<Card w="full" data-testid="file-row">
|
||||
<CardBody>
|
||||
<FileRowResponsiveGrid>
|
||||
<GridItem area="cover">
|
||||
<AlbumImage name={metadata?.album} url={metadata?.cover} />
|
||||
</GridItem>
|
||||
<GridItem area="title">
|
||||
<Box w="full" as="h4" fontWeight="semibold" mt="1" textAlign={{ base: 'center', md: 'left' }}>
|
||||
<span data-testid="audio-meta-song-name">{metadata?.name ?? nameWithoutExt}</span>
|
||||
</Box>
|
||||
</GridItem>
|
||||
<GridItem area="meta">
|
||||
{isDecrypted && metadata && <SongMetadata metadata={metadata} />}
|
||||
{file.state === ProcessState.ERROR && <FileError error={file.errorMessage} code={file.errorCode} />}
|
||||
</GridItem>
|
||||
<GridItem area="action" alignSelf="center">
|
||||
<VStack>
|
||||
{file.decrypted && <audio controls autoPlay={false} src={file.decrypted} ref={audioPlayerRef} />}
|
||||
<div className="card bg-base-100 shadow-sm w-full md:w-[30%] " data-testid="file-row">
|
||||
<div className="card-body items-center text-center px-2">
|
||||
<h2
|
||||
className="card-title overflow-hidden text-ellipsis block max-w-full whitespace-nowrap"
|
||||
data-testid="audio-meta-song-name"
|
||||
>
|
||||
{metadata?.name ?? nameWithoutExt}
|
||||
</h2>
|
||||
|
||||
<Wrap>
|
||||
{isDecrypted && (
|
||||
<>
|
||||
<WrapItem>
|
||||
<Button type="button" onClick={togglePlay}>
|
||||
播放/暂停
|
||||
</Button>
|
||||
</WrapItem>
|
||||
<WrapItem>
|
||||
{file.decrypted && (
|
||||
<Link href={file.decrypted} download={decryptedName}>
|
||||
<Button as="span">下载</Button>
|
||||
</Link>
|
||||
)}
|
||||
</WrapItem>
|
||||
</>
|
||||
)}
|
||||
<WrapItem>
|
||||
<Button type="button" onClick={onClose}>
|
||||
删除
|
||||
</Button>
|
||||
</WrapItem>
|
||||
</Wrap>
|
||||
</VStack>
|
||||
</GridItem>
|
||||
</FileRowResponsiveGrid>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Collapse>
|
||||
<div>
|
||||
{file.state === ProcessState.ERROR && <FileError error={file.errorMessage} code={file.errorCode} />}
|
||||
{isDecrypted && (
|
||||
<audio
|
||||
className="max-w-[100%]"
|
||||
aria-disabled={!file.decrypted}
|
||||
controls
|
||||
autoPlay={false}
|
||||
src={file.decrypted}
|
||||
ref={audioPlayerRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="card-actions justify-end">
|
||||
<a
|
||||
href={file.decrypted}
|
||||
download={decryptedName}
|
||||
title={`下载: ${decryptedName}`}
|
||||
className={classNames('btn', {
|
||||
'btn-primary': file.decrypted,
|
||||
'cursor-not-allowed pointer-events-none': !file.decrypted,
|
||||
})}
|
||||
data-testid="audio-download"
|
||||
>
|
||||
下载
|
||||
</a>
|
||||
<button type="button" className="btn btn-error" onClick={() => dispatch(deleteFile({ id }))}>
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import {
|
||||
TabPanels,
|
||||
Tabs,
|
||||
Text,
|
||||
useBreakpointValue,
|
||||
useToast,
|
||||
VStack,
|
||||
} from '@chakra-ui/react';
|
||||
@ -47,11 +46,7 @@ const TABS: { name: string; Tab: FC }[] = [
|
||||
export function Settings() {
|
||||
const toast = useToast();
|
||||
const dispatch = useAppDispatch();
|
||||
const isLargeWidthDevice =
|
||||
useBreakpointValue({
|
||||
base: false,
|
||||
lg: true,
|
||||
}) ?? false;
|
||||
const isLargeWidthDevice = false;
|
||||
|
||||
const [tabIndex, setTabIndex] = useState(0);
|
||||
const handleTabChange = (idx: number) => {
|
||||
|
@ -1,31 +1,9 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Heading,
|
||||
HStack,
|
||||
Icon,
|
||||
IconButton,
|
||||
List,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Select,
|
||||
Text,
|
||||
Tooltip,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import { Select, useToast } from '@chakra-ui/react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { qmc2AddKey, qmc2AllowFuzzyNameSearch, qmc2ClearKeys, qmc2ImportKeys } from '../settingsSlice';
|
||||
import { selectStagingQMCv2Settings } from '../settingsSelector';
|
||||
import React, { useState } from 'react';
|
||||
import { MdAdd, MdDeleteForever, MdExpandMore, MdFileUpload } from 'react-icons/md';
|
||||
import { QMCv2EKeyItem } from './QMCv2/QMCv2EKeyItem';
|
||||
import { InfoOutlineIcon } from '@chakra-ui/icons';
|
||||
import { ImportSecretModal } from '~/components/ImportSecretModal';
|
||||
import { StagingQMCv2Key } from '../keyFormats';
|
||||
import { DatabaseKeyExtractor } from '~/util/DatabaseKeyExtractor';
|
||||
@ -33,8 +11,11 @@ import { parseAndroidQmEKey } from '~/util/mmkv/qm';
|
||||
import { getFileName } from '~/util/pathHelper';
|
||||
import { QMCv2QQMusicAllInstructions } from './QMCv2/QMCv2QQMusicAllInstructions';
|
||||
import { QMCv2DoubanAllInstructions } from './QMCv2/QMCv2DoubanAllInstructions';
|
||||
import { AddKey } from '~/components/AddKey';
|
||||
import { Dialog } from '~/components/Dialog';
|
||||
|
||||
export function PanelQMCv2Key() {
|
||||
const [showFuzzyNameSearchInfo, setShowFuzzyNameSearchInfo] = useState(false);
|
||||
const toast = useToast();
|
||||
const dispatch = useDispatch();
|
||||
const { keys: qmc2Keys, allowFuzzyNameSearch } = useSelector(selectStagingQMCv2Settings);
|
||||
@ -93,73 +74,64 @@ export function PanelQMCv2Key() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex minH={0} flexDir="column" flex={1}>
|
||||
<Heading as="h2" size="lg">
|
||||
QMCv2 解密密钥
|
||||
</Heading>
|
||||
<div className="flex min-h-0 flex-col flex-1">
|
||||
<h2 className="text-2xl font-bold">QMCv2 解密密钥</h2>
|
||||
|
||||
<Text>
|
||||
QQ 音乐、豆瓣 FM 目前采用的加密方案(QMCv2)。在使用「QQ 音乐」安卓、Mac 或 iOS 客户端,以及在使用「豆瓣
|
||||
FM」安卓客户端的情况下,其「离线加密文件」对应的「密钥」储存在独立的数据库文件内。
|
||||
</Text>
|
||||
<p>
|
||||
<span>QQ 音乐、豆瓣 FM 目前采用的加密方案(QMCv2)。</span>
|
||||
<span>
|
||||
在使用「QQ 音乐」安卓、Mac 或 iOS 客户端,以及在使用「豆瓣 FM」安卓客户端的情况下,
|
||||
其「离线加密文件」对应的「密钥」储存在独立的数据库文件内。
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<HStack pb={2} pt={2}>
|
||||
<ButtonGroup isAttached colorScheme="purple" variant="outline">
|
||||
<Button onClick={addKey} leftIcon={<Icon as={MdAdd} />}>
|
||||
添加一条密钥
|
||||
</Button>
|
||||
<Menu>
|
||||
<MenuButton as={IconButton} icon={<MdExpandMore />}></MenuButton>
|
||||
<MenuList>
|
||||
<MenuItem onClick={() => setShowImportModal(true)} icon={<Icon as={MdFileUpload} boxSize={5} />}>
|
||||
从文件导入密钥…
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem color="red" onClick={clearAll} icon={<Icon as={MdDeleteForever} boxSize={5} />}>
|
||||
清空密钥
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</ButtonGroup>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<label className="label">
|
||||
<input
|
||||
className="checkbox"
|
||||
type="checkbox"
|
||||
checked={allowFuzzyNameSearch}
|
||||
onChange={handleAllowFuzzyNameSearchCheckbox}
|
||||
/>
|
||||
允许匹配相似文件名
|
||||
</label>
|
||||
<button className="btn btn-info btn-sm" type="button" onClick={() => setShowFuzzyNameSearchInfo(true)}>
|
||||
这是什么?
|
||||
</button>
|
||||
<Dialog
|
||||
closeButton
|
||||
backdropClose
|
||||
show={showFuzzyNameSearchInfo}
|
||||
onClose={() => setShowFuzzyNameSearchInfo(false)}
|
||||
title="莱文斯坦距离"
|
||||
>
|
||||
<p>若文件名匹配失败,则使用相似文件名的密钥。</p>
|
||||
<p>
|
||||
该匹配使用「
|
||||
<ruby>
|
||||
莱文斯坦距离
|
||||
<rp> (</rp>
|
||||
<rt>Levenshtein distance</rt>
|
||||
<rp>)</rp>
|
||||
</ruby>
|
||||
」算法来计算文件名的相似程度。
|
||||
</p>
|
||||
<p>若密钥数量过多,匹配时可能会造成浏览器卡顿或无响应一段时间。</p>
|
||||
<p>若不确定,请勾选该项。</p>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
<HStack>
|
||||
<Checkbox isChecked={allowFuzzyNameSearch} onChange={handleAllowFuzzyNameSearchCheckbox}>
|
||||
<Text>匹配相似文件名</Text>
|
||||
</Checkbox>
|
||||
<Tooltip
|
||||
hasArrow
|
||||
closeOnClick={false}
|
||||
label={
|
||||
<Box>
|
||||
<Text>若文件名匹配失败,则使用相似文件名的密钥。</Text>
|
||||
<Text>
|
||||
使用「
|
||||
<ruby>
|
||||
莱文斯坦距离
|
||||
<rp> (</rp>
|
||||
<rt>Levenshtein distance</rt>
|
||||
<rp>)</rp>
|
||||
</ruby>
|
||||
」算法计算相似程度。
|
||||
</Text>
|
||||
<Text>若密钥数量过多,匹配时可能会造成浏览器卡顿或无响应一段时间。</Text>
|
||||
<Text>若不确定,请勾选该项。</Text>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<InfoOutlineIcon />
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
</HStack>
|
||||
<h3 className="mt-2 text-1xl font-bold">密钥管理</h3>
|
||||
<AddKey addKey={addKey} importKeyFromFile={() => setShowImportModal(true)} clearKeys={clearAll} />
|
||||
|
||||
<Box flex={1} minH={0} overflow="auto" pr="4">
|
||||
<List spacing={3}>
|
||||
<div className="flex-1 min-h-0 overflow-auto pr-4">
|
||||
<ul className="list bg-base-100 rounded-box shadow-md">
|
||||
{qmc2Keys.map(({ id, ekey, name }, i) => (
|
||||
<QMCv2EKeyItem key={id} id={id} ekey={ekey} name={name} i={i} />
|
||||
))}
|
||||
</List>
|
||||
{qmc2Keys.length === 0 && <Text>还没有密钥。</Text>}
|
||||
</Box>
|
||||
</ul>
|
||||
{qmc2Keys.length === 0 && <p className="p-4 pb-2 text-xs tracking-wide">还没有密钥。</p>}
|
||||
</div>
|
||||
|
||||
<ImportSecretModal
|
||||
clientName={
|
||||
@ -181,6 +153,6 @@ export function PanelQMCv2Key() {
|
||||
{secretType === 'qm' && <QMCv2QQMusicAllInstructions />}
|
||||
{secretType === 'douban' && <QMCv2DoubanAllInstructions />}
|
||||
</ImportSecretModal>
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,3 @@
|
||||
import {
|
||||
HStack,
|
||||
Icon,
|
||||
IconButton,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputLeftElement,
|
||||
InputRightElement,
|
||||
ListItem,
|
||||
Text,
|
||||
VStack,
|
||||
} from '@chakra-ui/react';
|
||||
import { MdDelete, MdVpnKey } from 'react-icons/md';
|
||||
import { qmc2DeleteKey, qmc2UpdateKey } from '../../settingsSlice';
|
||||
import { useAppDispatch } from '~/hooks';
|
||||
@ -22,48 +10,45 @@ export const QMCv2EKeyItem = memo(({ id, name, ekey, i }: { id: string; name: st
|
||||
dispatch(qmc2UpdateKey({ id, field: prop, value: e.target.value }));
|
||||
const deleteKey = () => dispatch(qmc2DeleteKey({ id }));
|
||||
|
||||
return (
|
||||
<ListItem mt={0} pt={2} pb={2} _even={{ bg: 'gray.50' }}>
|
||||
<HStack>
|
||||
<Text w="2em" textAlign="center">
|
||||
{i + 1}
|
||||
</Text>
|
||||
const isValidEKey = [364, 704].includes(ekey.length);
|
||||
|
||||
<VStack flex={1}>
|
||||
<Input
|
||||
variant="flushed"
|
||||
return (
|
||||
<li className="list-row items-center">
|
||||
<div className="flex items-center justify-center w-8 h-8 text-sm font-bold text-gray-500 bg-gray-200 rounded-full">
|
||||
{i + 1}
|
||||
</div>
|
||||
|
||||
<div className="join join-vertical flex-1">
|
||||
<label className="input w-full rounded-tl-md rounded-tr-md">
|
||||
<span className="cursor-default select-none">文件名</span>
|
||||
<input
|
||||
type="text"
|
||||
className="font-mono"
|
||||
placeholder="文件名,包括后缀名。如 “AAA - BBB.mflac”"
|
||||
value={name}
|
||||
onChange={(e) => updateKey('name', e)}
|
||||
/>
|
||||
</label>
|
||||
<label className="input w-full rounded-bl-md rounded-br-md mt-[-1px]">
|
||||
<span className="cursor-default inline-flex items-center gap-1 select-none">
|
||||
密钥 <MdVpnKey />
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
className="font-mono"
|
||||
placeholder="密钥,通常包含 364 或 704 位字符,没有空格。"
|
||||
value={ekey}
|
||||
onChange={(e) => updateKey('ekey', e)}
|
||||
/>
|
||||
<span className={isValidEKey ? 'text-green-600' : 'text-red-600'}>
|
||||
<code>{ekey.length || '?'}</code>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<InputGroup size="xs">
|
||||
<InputLeftElement pr="2">
|
||||
<Icon as={MdVpnKey} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
variant="flushed"
|
||||
placeholder="密钥,通常包含 364 或 704 位字符,没有空格。"
|
||||
value={ekey}
|
||||
onChange={(e) => updateKey('ekey', e)}
|
||||
/>
|
||||
<InputRightElement>
|
||||
<Text pl="2" color={ekey.length ? 'green.500' : 'red.500'}>
|
||||
<code>{ekey.length || '?'}</code>
|
||||
</Text>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</VStack>
|
||||
|
||||
<IconButton
|
||||
aria-label="删除该密钥"
|
||||
icon={<Icon as={MdDelete} boxSize={6} />}
|
||||
variant="ghost"
|
||||
colorScheme="red"
|
||||
type="button"
|
||||
onClick={deleteKey}
|
||||
/>
|
||||
</HStack>
|
||||
</ListItem>
|
||||
<button type="button" className="btn btn-error btn-sm px-1 btn-outline" onClick={deleteKey}>
|
||||
<MdDelete className="size-6" />
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
import './pwa';
|
||||
import './App.css';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Alert, AlertIcon, Box, Button, Flex, Text, VStack } from '@chakra-ui/react';
|
||||
import { RiErrorWarningLine } from 'react-icons/ri';
|
||||
import { SelectFile } from '../components/SelectFile';
|
||||
|
||||
import { FileListing } from '~/features/file-listing/FileListing';
|
||||
@ -14,29 +14,32 @@ export function MainTab() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box h="full" w="full" pt="4">
|
||||
<VStack gap="3">
|
||||
<div className="size-full max-w-[80%] self-center pt-4">
|
||||
<div className="gap-3 flex flex-col">
|
||||
{isSettingsNotSaved && (
|
||||
<Alert borderRadius={7} maxW={400} status="warning">
|
||||
<AlertIcon />
|
||||
<Flex flexDir="row" alignItems="center" flexGrow={1} justifyContent="space-between">
|
||||
<Text m={0}>
|
||||
有尚未储存的设置,
|
||||
<br />
|
||||
设定将在保存后生效
|
||||
</Text>
|
||||
<Button type="button" ml={3} size="md" onClick={onClickSaveSettings}>
|
||||
<div role="alert" className="alert alert-warning gap-2">
|
||||
<span className="md:flex flex-row items-center gap-1">
|
||||
<RiErrorWarningLine className="size-6" />
|
||||
<span className="font-bold hidden md:inline">警告</span>
|
||||
</span>
|
||||
<div>
|
||||
<span className="block font-bold md:hidden">警告</span>
|
||||
<span>有尚未储存的设置,</span>
|
||||
<span className="inline-block">设定将在保存后生效。</span>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" className="btn btn-primary btn-sm" onClick={onClickSaveSettings}>
|
||||
立即储存
|
||||
</Button>
|
||||
</Flex>
|
||||
</Alert>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<SelectFile />
|
||||
|
||||
<Box w="full">
|
||||
<div className="w-full mt-4">
|
||||
<FileListing />
|
||||
</Box>
|
||||
</VStack>
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,9 @@
|
||||
import { Container, Flex, useBreakpointValue } from '@chakra-ui/react';
|
||||
import { Settings } from '~/features/settings/Settings';
|
||||
|
||||
export function SettingsTab() {
|
||||
const containerProps = useBreakpointValue({
|
||||
base: { p: '0' },
|
||||
lg: { p: undefined },
|
||||
});
|
||||
|
||||
return (
|
||||
<Container as={Flex} maxW="container.lg" {...containerProps}>
|
||||
<div className="flex p-0">
|
||||
<Settings />
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import wasm from 'vite-plugin-wasm';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
import topLevelAwait from 'vite-plugin-top-level-await';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
import { tryCommand } from './support/command';
|
||||
|
||||
@ -43,6 +44,7 @@ export default defineConfig({
|
||||
exclude: ['@unlock-music/crypto', 'sql.js'],
|
||||
},
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
values: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user