diff --git a/.env b/.env
index 58a558e..9ee0589 100644
--- a/.env
+++ b/.env
@@ -12,6 +12,7 @@ NEXT_PUBLIC_CHAT_API_URL=http://54.223.196.180
# 三方登录
NEXT_PUBLIC_DISCORD_CLIENT_ID=1448143535609217076
+NEXT_PUBLIC_GOOGLE_CLIENT_ID=754103853450-vn4af37tmguerut5a7abo54jr87mv09k.apps.googleusercontent.com
# 前端回调地址
NEXT_PUBLIC_APP_URL=http://54.223.196.180:3000
diff --git a/package.json b/package.json
index 0312dbf..2486f70 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
"lamejs": "^1.2.1",
"lucide-react": "^0.525.0",
"next": "16.0.8",
+ "next-intl": "^4.6.1",
"next-themes": "^0.4.6",
"numeral": "^2.0.6",
"qs": "^6.14.0",
@@ -63,7 +64,6 @@
"react-virtuoso": "^4.17.0",
"sonner": "^2.0.6",
"stream-chat": "^9.27.0",
- "swiper": "^12.0.3",
"tailwind-merge": "^3.3.1",
"vaul": "^1.1.2",
"zod": "^4.0.5",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0d5c6ca..7d70eed 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -122,6 +122,9 @@ importers:
next:
specifier: 16.0.8
version: 16.0.8(@babel/core@7.28.5)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ next-intl:
+ specifier: ^4.6.1
+ version: 4.6.1(next@16.0.8(@babel/core@7.28.5)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(typescript@5.8.3)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
@@ -158,9 +161,6 @@ importers:
stream-chat:
specifier: ^9.27.0
version: 9.27.0
- swiper:
- specifier: ^12.0.3
- version: 12.0.3
tailwind-merge:
specifier: ^3.3.1
version: 3.3.1
@@ -242,7 +242,7 @@ importers:
version: 27.0.2
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@20.19.8)(typescript@5.8.3)
+ version: 10.9.2(@swc/core@1.15.7)(@types/node@20.19.8)(typescript@5.8.3)
tsx:
specifier: ^4.20.6
version: 4.20.6
@@ -793,6 +793,24 @@ packages:
'@floating-ui/utils@0.2.10':
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
+ '@formatjs/ecma402-abstract@2.3.6':
+ resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==}
+
+ '@formatjs/fast-memoize@2.2.7':
+ resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==}
+
+ '@formatjs/icu-messageformat-parser@2.11.4':
+ resolution: {integrity: sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==}
+
+ '@formatjs/icu-skeleton-parser@1.8.16':
+ resolution: {integrity: sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==}
+
+ '@formatjs/intl-localematcher@0.5.10':
+ resolution: {integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==}
+
+ '@formatjs/intl-localematcher@0.6.2':
+ resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==}
+
'@gulpjs/to-absolute-glob@4.0.0':
resolution: {integrity: sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==}
engines: {node: '>=10.13.0'}
@@ -1107,6 +1125,88 @@ packages:
'@open-draft/until@2.1.0':
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
+ '@parcel/watcher-android-arm64@2.5.1':
+ resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.1':
+ resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+ engines: {node: '>= 10.0.0'}
+
'@pkgr/core@0.2.9':
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@@ -1570,6 +1670,9 @@ packages:
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
+ '@schummar/icu-type-parser@1.21.5':
+ resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==}
+
'@sindresorhus/merge-streams@4.0.0':
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
engines: {node: '>=18'}
@@ -1789,9 +1892,84 @@ packages:
'@standard-schema/utils@0.3.0':
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
+ '@swc/core-darwin-arm64@1.15.7':
+ resolution: {integrity: sha512-+hNVUfezUid7LeSHqnhoC6Gh3BROABxjlDNInuZ/fie1RUxaEX4qzDwdTgozJELgHhvYxyPIg1ro8ibnKtgO4g==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@swc/core-darwin-x64@1.15.7':
+ resolution: {integrity: sha512-ZAFuvtSYZTuXPcrhanaD5eyp27H8LlDzx2NAeVyH0FchYcuXf0h5/k3GL9ZU6Jw9eQ63R1E8KBgpXEJlgRwZUQ==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@swc/core-linux-arm-gnueabihf@1.15.7':
+ resolution: {integrity: sha512-K3HTYocpqnOw8KcD8SBFxiDHjIma7G/X+bLdfWqf+qzETNBrzOub/IEkq9UaeupaJiZJkPptr/2EhEXXWryS/A==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@swc/core-linux-arm64-gnu@1.15.7':
+ resolution: {integrity: sha512-HCnVIlsLnCtQ3uXcXgWrvQ6SAraskLA9QJo9ykTnqTH6TvUYqEta+TdTdGjzngD6TOE7XjlAiUs/RBtU8Z0t+Q==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@swc/core-linux-arm64-musl@1.15.7':
+ resolution: {integrity: sha512-/OOp9UZBg4v2q9+x/U21Jtld0Wb8ghzBScwhscI7YvoSh4E8RALaJ1msV8V8AKkBkZH7FUAFB7Vbv0oVzZsezA==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@swc/core-linux-x64-gnu@1.15.7':
+ resolution: {integrity: sha512-VBbs4gtD4XQxrHuQ2/2+TDZpPQQgrOHYRnS6SyJW+dw0Nj/OomRqH+n5Z4e/TgKRRbieufipeIGvADYC/90PYQ==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@swc/core-linux-x64-musl@1.15.7':
+ resolution: {integrity: sha512-kVuy2unodso6p0rMauS2zby8/bhzoGRYxBDyD6i2tls/fEYAE74oP0VPFzxIyHaIjK1SN6u5TgvV9MpyJ5xVug==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@swc/core-win32-arm64-msvc@1.15.7':
+ resolution: {integrity: sha512-uddYoo5Xmo1XKLhAnh4NBIyy5d0xk33x1sX3nIJboFySLNz878ksCFCZ3IBqrt1Za0gaoIWoOSSSk0eNhAc/sw==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@swc/core-win32-ia32-msvc@1.15.7':
+ resolution: {integrity: sha512-rqq8JjNMLx3QNlh0aPTtN/4+BGLEHC94rj9mkH1stoNRf3ra6IksNHMHy+V1HUqElEgcZyx+0yeXx3eLOTcoFw==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@swc/core-win32-x64-msvc@1.15.7':
+ resolution: {integrity: sha512-4BK06EGdPnuplgcNhmSbOIiLdRgHYX3v1nl4HXo5uo4GZMfllXaCyBUes+0ePRfwbn9OFgVhCWPcYYjMT6hycQ==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@swc/core@1.15.7':
+ resolution: {integrity: sha512-kTGB8XI7P+pTKW83tnUEDVP4zduF951u3UAOn5eTi0vyW6MvL56A3+ggMdfuVFtDI0/DsbSzf5z34HVBbuScWw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@swc/helpers': '>=0.5.17'
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+ '@swc/types@0.1.25':
+ resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==}
+
'@tailwindcss/node@4.1.11':
resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==}
@@ -2500,6 +2678,11 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
+ detect-libc@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
@@ -3003,6 +3186,9 @@ packages:
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019.
+ intl-messageformat@10.7.18:
+ resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==}
+
is-array-buffer@3.0.5:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
@@ -3435,6 +3621,23 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+ negotiator@1.0.0:
+ resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+ engines: {node: '>= 0.6'}
+
+ next-intl-swc-plugin-extractor@4.6.1:
+ resolution: {integrity: sha512-+HHNeVERfSvuPDF7LYVn3pxst5Rf7EYdUTw7C7WIrYhcLaKiZ1b9oSRkTQddAN3mifDMCfHqO4kAQ/pcKiBl3A==}
+
+ next-intl@4.6.1:
+ resolution: {integrity: sha512-KlWgWtKLBPUsTPgxqwyjws1wCMD2QKxLlVjeeGj53DC1JWfKmBShKOrhIP0NznZrRQ0GleeoDUeHSETmyyIFeA==}
+ peerDependencies:
+ next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
+ typescript: ^5.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
next-themes@0.4.6:
resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
peerDependencies:
@@ -3462,6 +3665,9 @@ packages:
sass:
optional: true
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
@@ -3575,6 +3781,9 @@ packages:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
+ po-parser@2.0.0:
+ resolution: {integrity: sha512-SZvoKi3PoI/hHa2V9je9CW7Xgxl4dvO74cvaa6tWShIHT51FkPxje6pt0gTJznJrU67ix91nDaQp2hUxkOYhKA==}
+
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -4029,10 +4238,6 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
- swiper@12.0.3:
- resolution: {integrity: sha512-BHd6U1VPEIksrXlyXjMmRWO0onmdNPaTAFduzqR3pgjvi7KfmUCAm/0cj49u2D7B0zNjMw02TSeXfinC1hDCXg==}
- engines: {node: '>= 4.7.0'}
-
synckit@0.11.11:
resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -4196,6 +4401,11 @@ packages:
'@types/react':
optional: true
+ use-intl@4.6.1:
+ resolution: {integrity: sha512-mUIj6QvJZ7Rk33mLDxRziz1YiBBAnIji8YW4TXXMdYHtaPEbVucrXD3iKQGAqJhbVn0VnjrEtIKYO1B18mfSJw==}
+ peerDependencies:
+ react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
+
use-sidecar@1.1.3:
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
engines: {node: '>=10'}
@@ -5253,6 +5463,36 @@ snapshots:
'@floating-ui/utils@0.2.10': {}
+ '@formatjs/ecma402-abstract@2.3.6':
+ dependencies:
+ '@formatjs/fast-memoize': 2.2.7
+ '@formatjs/intl-localematcher': 0.6.2
+ decimal.js: 10.6.0
+ tslib: 2.8.1
+
+ '@formatjs/fast-memoize@2.2.7':
+ dependencies:
+ tslib: 2.8.1
+
+ '@formatjs/icu-messageformat-parser@2.11.4':
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.3.6
+ '@formatjs/icu-skeleton-parser': 1.8.16
+ tslib: 2.8.1
+
+ '@formatjs/icu-skeleton-parser@1.8.16':
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.3.6
+ tslib: 2.8.1
+
+ '@formatjs/intl-localematcher@0.5.10':
+ dependencies:
+ tslib: 2.8.1
+
+ '@formatjs/intl-localematcher@0.6.2':
+ dependencies:
+ tslib: 2.8.1
+
'@gulpjs/to-absolute-glob@4.0.0':
dependencies:
is-negated-glob: 1.0.0
@@ -5501,6 +5741,66 @@ snapshots:
'@open-draft/until@2.1.0': {}
+ '@parcel/watcher-android-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher@2.5.1':
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.1
+ '@parcel/watcher-darwin-arm64': 2.5.1
+ '@parcel/watcher-darwin-x64': 2.5.1
+ '@parcel/watcher-freebsd-x64': 2.5.1
+ '@parcel/watcher-linux-arm-glibc': 2.5.1
+ '@parcel/watcher-linux-arm-musl': 2.5.1
+ '@parcel/watcher-linux-arm64-glibc': 2.5.1
+ '@parcel/watcher-linux-arm64-musl': 2.5.1
+ '@parcel/watcher-linux-x64-glibc': 2.5.1
+ '@parcel/watcher-linux-x64-musl': 2.5.1
+ '@parcel/watcher-win32-arm64': 2.5.1
+ '@parcel/watcher-win32-ia32': 2.5.1
+ '@parcel/watcher-win32-x64': 2.5.1
+
'@pkgr/core@0.2.9': {}
'@radix-ui/number@1.1.1': {}
@@ -5959,6 +6259,8 @@ snapshots:
'@rtsao/scc@1.1.0': {}
+ '@schummar/icu-type-parser@1.21.5': {}
+
'@sindresorhus/merge-streams@4.0.0': {}
'@smithy/abort-controller@4.0.4':
@@ -6296,10 +6598,62 @@ snapshots:
'@standard-schema/utils@0.3.0': {}
+ '@swc/core-darwin-arm64@1.15.7':
+ optional: true
+
+ '@swc/core-darwin-x64@1.15.7':
+ optional: true
+
+ '@swc/core-linux-arm-gnueabihf@1.15.7':
+ optional: true
+
+ '@swc/core-linux-arm64-gnu@1.15.7':
+ optional: true
+
+ '@swc/core-linux-arm64-musl@1.15.7':
+ optional: true
+
+ '@swc/core-linux-x64-gnu@1.15.7':
+ optional: true
+
+ '@swc/core-linux-x64-musl@1.15.7':
+ optional: true
+
+ '@swc/core-win32-arm64-msvc@1.15.7':
+ optional: true
+
+ '@swc/core-win32-ia32-msvc@1.15.7':
+ optional: true
+
+ '@swc/core-win32-x64-msvc@1.15.7':
+ optional: true
+
+ '@swc/core@1.15.7':
+ dependencies:
+ '@swc/counter': 0.1.3
+ '@swc/types': 0.1.25
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.15.7
+ '@swc/core-darwin-x64': 1.15.7
+ '@swc/core-linux-arm-gnueabihf': 1.15.7
+ '@swc/core-linux-arm64-gnu': 1.15.7
+ '@swc/core-linux-arm64-musl': 1.15.7
+ '@swc/core-linux-x64-gnu': 1.15.7
+ '@swc/core-linux-x64-musl': 1.15.7
+ '@swc/core-win32-arm64-msvc': 1.15.7
+ '@swc/core-win32-ia32-msvc': 1.15.7
+ '@swc/core-win32-x64-msvc': 1.15.7
+
+ '@swc/counter@0.1.3': {}
+
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
+ '@swc/types@0.1.25':
+ dependencies:
+ '@swc/counter': 0.1.3
+
'@tailwindcss/node@4.1.11':
dependencies:
'@ampproject/remapping': 2.3.0
@@ -6984,6 +7338,8 @@ snapshots:
delayed-stream@1.0.0: {}
+ detect-libc@1.0.3: {}
+
detect-libc@2.1.2: {}
detect-node-es@1.1.0: {}
@@ -7669,6 +8025,13 @@ snapshots:
intersection-observer@0.12.2: {}
+ intl-messageformat@10.7.18:
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.3.6
+ '@formatjs/fast-memoize': 2.2.7
+ '@formatjs/icu-messageformat-parser': 2.11.4
+ tslib: 2.8.1
+
is-array-buffer@3.0.5:
dependencies:
call-bind: 1.0.8
@@ -8057,6 +8420,26 @@ snapshots:
natural-compare@1.4.0: {}
+ negotiator@1.0.0: {}
+
+ next-intl-swc-plugin-extractor@4.6.1: {}
+
+ next-intl@4.6.1(next@16.0.8(@babel/core@7.28.5)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(typescript@5.8.3):
+ dependencies:
+ '@formatjs/intl-localematcher': 0.5.10
+ '@parcel/watcher': 2.5.1
+ '@swc/core': 1.15.7
+ negotiator: 1.0.0
+ next: 16.0.8(@babel/core@7.28.5)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ next-intl-swc-plugin-extractor: 4.6.1
+ po-parser: 2.0.0
+ react: 19.2.1
+ use-intl: 4.6.1(react@19.2.1)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - '@swc/helpers'
+
next-themes@0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
dependencies:
react: 19.2.1
@@ -8085,6 +8468,8 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
+ node-addon-api@7.1.1: {}
+
node-releases@2.0.27: {}
normalize-path@3.0.0: {}
@@ -8194,6 +8579,8 @@ snapshots:
picomatch@4.0.3: {}
+ po-parser@2.0.0: {}
+
possible-typed-array-names@1.1.0: {}
postcss@8.4.31:
@@ -8669,8 +9056,6 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
- swiper@12.0.3: {}
-
synckit@0.11.11:
dependencies:
'@pkgr/core': 0.2.9
@@ -8744,7 +9129,7 @@ snapshots:
'@ts-morph/common': 0.28.1
code-block-writer: 13.0.3
- ts-node@10.9.2(@types/node@20.19.8)(typescript@5.8.3):
+ ts-node@10.9.2(@swc/core@1.15.7)(@types/node@20.19.8)(typescript@5.8.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
@@ -8761,6 +9146,8 @@ snapshots:
typescript: 5.8.3
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
+ optionalDependencies:
+ '@swc/core': 1.15.7
tsconfig-paths@3.15.0:
dependencies:
@@ -8893,6 +9280,13 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.7
+ use-intl@4.6.1(react@19.2.1):
+ dependencies:
+ '@formatjs/fast-memoize': 2.2.7
+ '@schummar/icu-type-parser': 1.21.5
+ intl-messageformat: 10.7.18
+ react: 19.2.1
+
use-sidecar@1.1.3(@types/react@19.2.7)(react@19.2.1):
dependencies:
detect-node-es: 1.1.0
diff --git a/public/font-v2/demo.css b/public/font-v2/demo.css
deleted file mode 100644
index a67054a..0000000
--- a/public/font-v2/demo.css
+++ /dev/null
@@ -1,539 +0,0 @@
-/* Logo 字体 */
-@font-face {
- font-family: "iconfont logo";
- src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
- src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
-}
-
-.logo {
- font-family: "iconfont logo";
- font-size: 160px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-/* tabs */
-.nav-tabs {
- position: relative;
-}
-
-.nav-tabs .nav-more {
- position: absolute;
- right: 0;
- bottom: 0;
- height: 42px;
- line-height: 42px;
- color: #666;
-}
-
-#tabs {
- border-bottom: 1px solid #eee;
-}
-
-#tabs li {
- cursor: pointer;
- width: 100px;
- height: 40px;
- line-height: 40px;
- text-align: center;
- font-size: 16px;
- border-bottom: 2px solid transparent;
- position: relative;
- z-index: 1;
- margin-bottom: -1px;
- color: #666;
-}
-
-
-#tabs .active {
- border-bottom-color: #f00;
- color: #222;
-}
-
-.tab-container .content {
- display: none;
-}
-
-/* 页面布局 */
-.main {
- padding: 30px 100px;
- width: 960px;
- margin: 0 auto;
-}
-
-.main .logo {
- color: #333;
- text-align: left;
- margin-bottom: 30px;
- line-height: 1;
- height: 110px;
- margin-top: -50px;
- overflow: hidden;
- *zoom: 1;
-}
-
-.main .logo a {
- font-size: 160px;
- color: #333;
-}
-
-.helps {
- margin-top: 40px;
-}
-
-.helps pre {
- padding: 20px;
- margin: 10px 0;
- border: solid 1px #e7e1cd;
- background-color: #fffdef;
- overflow: auto;
-}
-
-.icon_lists {
- width: 100% !important;
- overflow: hidden;
- *zoom: 1;
-}
-
-.icon_lists li {
- width: 100px;
- margin-bottom: 10px;
- margin-right: 20px;
- text-align: center;
- list-style: none !important;
- cursor: default;
-}
-
-.icon_lists li .code-name {
- line-height: 1.2;
-}
-
-.icon_lists .icon {
- display: block;
- height: 100px;
- line-height: 100px;
- font-size: 42px;
- margin: 10px auto;
- color: #333;
- -webkit-transition: font-size 0.25s linear, width 0.25s linear;
- -moz-transition: font-size 0.25s linear, width 0.25s linear;
- transition: font-size 0.25s linear, width 0.25s linear;
-}
-
-.icon_lists .icon:hover {
- font-size: 100px;
-}
-
-.icon_lists .svg-icon {
- /* 通过设置 font-size 来改变图标大小 */
- width: 1em;
- /* 图标和文字相邻时,垂直对齐 */
- vertical-align: -0.15em;
- /* 通过设置 color 来改变 SVG 的颜色/fill */
- fill: currentColor;
- /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
- normalize.css 中也包含这行 */
- overflow: hidden;
-}
-
-.icon_lists li .name,
-.icon_lists li .code-name {
- color: #666;
-}
-
-/* markdown 样式 */
-.markdown {
- color: #666;
- font-size: 14px;
- line-height: 1.8;
-}
-
-.highlight {
- line-height: 1.5;
-}
-
-.markdown img {
- vertical-align: middle;
- max-width: 100%;
-}
-
-.markdown h1 {
- color: #404040;
- font-weight: 500;
- line-height: 40px;
- margin-bottom: 24px;
-}
-
-.markdown h2,
-.markdown h3,
-.markdown h4,
-.markdown h5,
-.markdown h6 {
- color: #404040;
- margin: 1.6em 0 0.6em 0;
- font-weight: 500;
- clear: both;
-}
-
-.markdown h1 {
- font-size: 28px;
-}
-
-.markdown h2 {
- font-size: 22px;
-}
-
-.markdown h3 {
- font-size: 16px;
-}
-
-.markdown h4 {
- font-size: 14px;
-}
-
-.markdown h5 {
- font-size: 12px;
-}
-
-.markdown h6 {
- font-size: 12px;
-}
-
-.markdown hr {
- height: 1px;
- border: 0;
- background: #e9e9e9;
- margin: 16px 0;
- clear: both;
-}
-
-.markdown p {
- margin: 1em 0;
-}
-
-.markdown>p,
-.markdown>blockquote,
-.markdown>.highlight,
-.markdown>ol,
-.markdown>ul {
- width: 80%;
-}
-
-.markdown ul>li {
- list-style: circle;
-}
-
-.markdown>ul li,
-.markdown blockquote ul>li {
- margin-left: 20px;
- padding-left: 4px;
-}
-
-.markdown>ul li p,
-.markdown>ol li p {
- margin: 0.6em 0;
-}
-
-.markdown ol>li {
- list-style: decimal;
-}
-
-.markdown>ol li,
-.markdown blockquote ol>li {
- margin-left: 20px;
- padding-left: 4px;
-}
-
-.markdown code {
- margin: 0 3px;
- padding: 0 5px;
- background: #eee;
- border-radius: 3px;
-}
-
-.markdown strong,
-.markdown b {
- font-weight: 600;
-}
-
-.markdown>table {
- border-collapse: collapse;
- border-spacing: 0px;
- empty-cells: show;
- border: 1px solid #e9e9e9;
- width: 95%;
- margin-bottom: 24px;
-}
-
-.markdown>table th {
- white-space: nowrap;
- color: #333;
- font-weight: 600;
-}
-
-.markdown>table th,
-.markdown>table td {
- border: 1px solid #e9e9e9;
- padding: 8px 16px;
- text-align: left;
-}
-
-.markdown>table th {
- background: #F7F7F7;
-}
-
-.markdown blockquote {
- font-size: 90%;
- color: #999;
- border-left: 4px solid #e9e9e9;
- padding-left: 0.8em;
- margin: 1em 0;
-}
-
-.markdown blockquote p {
- margin: 0;
-}
-
-.markdown .anchor {
- opacity: 0;
- transition: opacity 0.3s ease;
- margin-left: 8px;
-}
-
-.markdown .waiting {
- color: #ccc;
-}
-
-.markdown h1:hover .anchor,
-.markdown h2:hover .anchor,
-.markdown h3:hover .anchor,
-.markdown h4:hover .anchor,
-.markdown h5:hover .anchor,
-.markdown h6:hover .anchor {
- opacity: 1;
- display: inline-block;
-}
-
-.markdown>br,
-.markdown>p>br {
- clear: both;
-}
-
-
-.hljs {
- display: block;
- background: white;
- padding: 0.5em;
- color: #333333;
- overflow-x: auto;
-}
-
-.hljs-comment,
-.hljs-meta {
- color: #969896;
-}
-
-.hljs-string,
-.hljs-variable,
-.hljs-template-variable,
-.hljs-strong,
-.hljs-emphasis,
-.hljs-quote {
- color: #df5000;
-}
-
-.hljs-keyword,
-.hljs-selector-tag,
-.hljs-type {
- color: #a71d5d;
-}
-
-.hljs-literal,
-.hljs-symbol,
-.hljs-bullet,
-.hljs-attribute {
- color: #0086b3;
-}
-
-.hljs-section,
-.hljs-name {
- color: #63a35c;
-}
-
-.hljs-tag {
- color: #333333;
-}
-
-.hljs-title,
-.hljs-attr,
-.hljs-selector-id,
-.hljs-selector-class,
-.hljs-selector-attr,
-.hljs-selector-pseudo {
- color: #795da3;
-}
-
-.hljs-addition {
- color: #55a532;
- background-color: #eaffea;
-}
-
-.hljs-deletion {
- color: #bd2c00;
- background-color: #ffecec;
-}
-
-.hljs-link {
- text-decoration: underline;
-}
-
-/* 代码高亮 */
-/* PrismJS 1.15.0
-https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
-/**
- * prism.js default theme for JavaScript, CSS and HTML
- * Based on dabblet (http://dabblet.com)
- * @author Lea Verou
- */
-code[class*="language-"],
-pre[class*="language-"] {
- color: black;
- background: none;
- text-shadow: 0 1px white;
- font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
- text-align: left;
- white-space: pre;
- word-spacing: normal;
- word-break: normal;
- word-wrap: normal;
- line-height: 1.5;
-
- -moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
-
- -webkit-hyphens: none;
- -moz-hyphens: none;
- -ms-hyphens: none;
- hyphens: none;
-}
-
-pre[class*="language-"]::-moz-selection,
-pre[class*="language-"] ::-moz-selection,
-code[class*="language-"]::-moz-selection,
-code[class*="language-"] ::-moz-selection {
- text-shadow: none;
- background: #b3d4fc;
-}
-
-pre[class*="language-"]::selection,
-pre[class*="language-"] ::selection,
-code[class*="language-"]::selection,
-code[class*="language-"] ::selection {
- text-shadow: none;
- background: #b3d4fc;
-}
-
-@media print {
-
- code[class*="language-"],
- pre[class*="language-"] {
- text-shadow: none;
- }
-}
-
-/* Code blocks */
-pre[class*="language-"] {
- padding: 1em;
- margin: .5em 0;
- overflow: auto;
-}
-
-:not(pre)>code[class*="language-"],
-pre[class*="language-"] {
- background: #f5f2f0;
-}
-
-/* Inline code */
-:not(pre)>code[class*="language-"] {
- padding: .1em;
- border-radius: .3em;
- white-space: normal;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
- color: slategray;
-}
-
-.token.punctuation {
- color: #999;
-}
-
-.namespace {
- opacity: .7;
-}
-
-.token.property,
-.token.tag,
-.token.boolean,
-.token.number,
-.token.constant,
-.token.symbol,
-.token.deleted {
- color: #905;
-}
-
-.token.selector,
-.token.attr-name,
-.token.string,
-.token.char,
-.token.builtin,
-.token.inserted {
- color: #690;
-}
-
-.token.operator,
-.token.entity,
-.token.url,
-.language-css .token.string,
-.style .token.string {
- color: #9a6e3a;
- background: hsla(0, 0%, 100%, .5);
-}
-
-.token.atrule,
-.token.attr-value,
-.token.keyword {
- color: #07a;
-}
-
-.token.function,
-.token.class-name {
- color: #DD4A68;
-}
-
-.token.regex,
-.token.important,
-.token.variable {
- color: #e90;
-}
-
-.token.important,
-.token.bold {
- font-weight: bold;
-}
-
-.token.italic {
- font-style: italic;
-}
-
-.token.entity {
- cursor: help;
-}
diff --git a/public/font-v2/demo_index.html b/public/font-v2/demo_index.html
deleted file mode 100644
index b423b79..0000000
--- a/public/font-v2/demo_index.html
+++ /dev/null
@@ -1,674 +0,0 @@
-
-
-
-
- iconfont Demo
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Unicode
- - Font class
- - Symbol
-
-
-
查看项目
-
-
-
-
-
-
- -
-
-
Logo
- 
-
-
- -
-
-
12
- 
-
-
- -
-
-
群聊
- 
-
-
- -
-
-
挂断电话
- 
-
-
- -
-
-
电话
- 
-
-
- -
-
-
艾特
- 
-
-
- -
-
-
性别
- 
-
-
- -
-
-
Frame 247
- 
-
-
- -
-
-
编辑
- 
-
-
- -
-
-
展开
- 
-
-
- -
-
-
16-右
- 
-
-
- -
-
-
展开-1
- 
-
-
- -
-
-
生成
- 
-
-
- -
-
-
复制
- 
-
-
- -
-
-
16-左
- 
-
-
- -
-
-
gender-female-line
- 
-
-
- -
-
-
gender-male-line
- 
-
-
- -
-
-
刷新
- 
-
-
- -
-
-
箭头
- 
-
-
- -
-
-
关闭
- 
-
-
- -
-
-
搜索
- 
-
-
-
-
-
Unicode 引用
-
-
-
Unicode 是字体在网页端最原始的应用方式,特点是:
-
- - 支持按字体的方式去动态调整图标大小,颜色等等。
- - 默认情况下不支持多色,直接添加多色图标会自动去色。
-
-
- 注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)
-
-
Unicode 使用步骤如下:
-
第一步:拷贝项目下面生成的 @font-face
-
@font-face {
- font-family: 'iconfont';
- src: url('iconfont.eot?t=1766052523135'); /* IE9 */
- src: url('iconfont.eot?t=1766052523135#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url('iconfont.woff2?t=1766052523135') format('woff2'),
- url('iconfont.woff?t=1766052523135') format('woff'),
- url('iconfont.ttf?t=1766052523135') format('truetype'),
- url('iconfont.svg?t=1766052523135#iconfont') format('svg');
-}
-
-
第二步:定义使用 iconfont 的样式
-
.iconfont {
- font-family: "iconfont" !important;
- font-size: 16px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-
第三步:挑选相应图标并获取字体编码,应用于页面
-
-<span class="iconfont">3</span>
-
-
- "iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
-
-
-
-
-
-
-
font-class 引用
-
-
-
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。
-
与 Unicode 使用方式相比,具有如下特点:
-
- - 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
- - 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
-
-
使用步骤如下:
-
第一步:引入项目下面生成的 fontclass 代码:
-
<link rel="stylesheet" href="./iconfont.css">
-
-
第二步:挑选相应图标并获取类名,应用于页面:
-
<span class="iconfont icon-xxx"></span>
-
-
- "
- iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
-
-
-
-
-
-
-
Symbol 引用
-
-
-
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章
- 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
-
- - 支持多色图标了,不再受单色限制。
- - 通过一些技巧,支持像字体那样,通过
font-size, color 来调整样式。
- - 兼容性较差,支持 IE9+,及现代浏览器。
- - 浏览器渲染 SVG 的性能一般,还不如 png。
-
-
使用步骤如下:
-
第一步:引入项目下面生成的 symbol 代码:
-
<script src="./iconfont.js"></script>
-
-
第二步:加入通用 CSS 代码(引入一次就行):
-
<style>
-.icon {
- width: 1em;
- height: 1em;
- vertical-align: -0.15em;
- fill: currentColor;
- overflow: hidden;
-}
-</style>
-
-
第三步:挑选相应图标并获取类名,应用于页面:
-
<svg class="icon" aria-hidden="true">
- <use xlink:href="#icon-xxx"></use>
-</svg>
-
-
-
-
-
-
-
-
-
diff --git a/public/font-v2/iconfont.css b/public/font-v2/iconfont.css
deleted file mode 100644
index bc47704..0000000
--- a/public/font-v2/iconfont.css
+++ /dev/null
@@ -1,102 +0,0 @@
-@font-face {
- font-family: "iconfont"; /* Project id 5076160 */
- src: url('iconfont.eot?t=1766052523135'); /* IE9 */
- src: url('iconfont.eot?t=1766052523135#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url('iconfont.woff2?t=1766052523135') format('woff2'),
- url('iconfont.woff?t=1766052523135') format('woff'),
- url('iconfont.ttf?t=1766052523135') format('truetype'),
- url('iconfont.svg?t=1766052523135#iconfont') format('svg');
-}
-
-.iconfont {
- font-family: "iconfont" !important;
- font-size: 16px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-.icon-Logo:before {
- content: "\e624";
-}
-
-.icon-wodejiemianqianwang:before {
- content: "\e622";
-}
-
-.icon-qunliao:before {
- content: "\e621";
-}
-
-.icon-guaduandianhua:before {
- content: "\e620";
-}
-
-.icon-dianhua:before {
- content: "\e61f";
-}
-
-.icon-aite:before {
- content: "\e61d";
-}
-
-.icon-xingbie:before {
- content: "\e61e";
-}
-
-.icon-a-Frame247:before {
- content: "\e61c";
-}
-
-.icon-bianji:before {
- content: "\e61b";
-}
-
-.icon-zhankai1:before {
- content: "\e615";
-}
-
-.icon-a-Frame195:before {
- content: "\e616";
-}
-
-.icon-zhankai-1:before {
- content: "\e617";
-}
-
-.icon-shengcheng:before {
- content: "\e618";
-}
-
-.icon-fuzhi:before {
- content: "\e619";
-}
-
-.icon-a-Frame194:before {
- content: "\e61a";
-}
-
-.icon-gender-female-line:before {
- content: "\e614";
-}
-
-.icon-gender-male-line:before {
- content: "\e613";
-}
-
-.icon-shuaxin:before {
- content: "\e612";
-}
-
-.icon-jiantou:before {
- content: "\e610";
-}
-
-.icon-guanbi:before {
- content: "\e611";
-}
-
-.icon-sousuo:before {
- content: "\e60d";
-}
-
diff --git a/public/font-v2/iconfont.eot b/public/font-v2/iconfont.eot
deleted file mode 100644
index 7603066..0000000
Binary files a/public/font-v2/iconfont.eot and /dev/null differ
diff --git a/public/font-v2/iconfont.js b/public/font-v2/iconfont.js
deleted file mode 100644
index 06f633d..0000000
--- a/public/font-v2/iconfont.js
+++ /dev/null
@@ -1 +0,0 @@
-window._iconfont_svg_string_5076160='',(l=>{var a=(t=(t=document.getElementsByTagName("script"))[t.length-1]).getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var o,e,c,i,n,h=function(a,t){t.parentNode.insertBefore(a,t)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}o=function(){var a,t=document.createElement("div");t.innerHTML=l._iconfont_svg_string_5076160,(t=t.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",t=t,(a=document.body).firstChild?h(t,a.firstChild):a.appendChild(t))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(e=function(){document.removeEventListener("DOMContentLoaded",e,!1),o()},document.addEventListener("DOMContentLoaded",e,!1)):document.attachEvent&&(c=o,i=l.document,n=!1,s(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,d())})}function d(){n||(n=!0,c())}function s(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(s,50)}d()}})(window);
\ No newline at end of file
diff --git a/public/font-v2/iconfont.json b/public/font-v2/iconfont.json
deleted file mode 100644
index dd87d30..0000000
--- a/public/font-v2/iconfont.json
+++ /dev/null
@@ -1,156 +0,0 @@
-{
- "id": "5076160",
- "name": "spicyxx.ai",
- "font_family": "iconfont",
- "css_prefix_text": "icon-",
- "description": "",
- "glyphs": [
- {
- "icon_id": "46381406",
- "name": "Logo",
- "font_class": "Logo",
- "unicode": "e624",
- "unicode_decimal": 58916
- },
- {
- "icon_id": "46339903",
- "name": "12",
- "font_class": "wodejiemianqianwang",
- "unicode": "e622",
- "unicode_decimal": 58914
- },
- {
- "icon_id": "46337600",
- "name": "群聊",
- "font_class": "qunliao",
- "unicode": "e621",
- "unicode_decimal": 58913
- },
- {
- "icon_id": "46281959",
- "name": "挂断电话",
- "font_class": "guaduandianhua",
- "unicode": "e620",
- "unicode_decimal": 58912
- },
- {
- "icon_id": "46281954",
- "name": "电话",
- "font_class": "dianhua",
- "unicode": "e61f",
- "unicode_decimal": 58911
- },
- {
- "icon_id": "46261887",
- "name": "艾特",
- "font_class": "aite",
- "unicode": "e61d",
- "unicode_decimal": 58909
- },
- {
- "icon_id": "46261886",
- "name": "性别",
- "font_class": "xingbie",
- "unicode": "e61e",
- "unicode_decimal": 58910
- },
- {
- "icon_id": "46252831",
- "name": "Frame 247",
- "font_class": "a-Frame247",
- "unicode": "e61c",
- "unicode_decimal": 58908
- },
- {
- "icon_id": "46252115",
- "name": "编辑",
- "font_class": "bianji",
- "unicode": "e61b",
- "unicode_decimal": 58907
- },
- {
- "icon_id": "46252119",
- "name": "展开",
- "font_class": "zhankai1",
- "unicode": "e615",
- "unicode_decimal": 58901
- },
- {
- "icon_id": "46252114",
- "name": "16-右",
- "font_class": "a-Frame195",
- "unicode": "e616",
- "unicode_decimal": 58902
- },
- {
- "icon_id": "46252118",
- "name": "展开-1",
- "font_class": "zhankai-1",
- "unicode": "e617",
- "unicode_decimal": 58903
- },
- {
- "icon_id": "46252117",
- "name": "生成",
- "font_class": "shengcheng",
- "unicode": "e618",
- "unicode_decimal": 58904
- },
- {
- "icon_id": "46252116",
- "name": "复制",
- "font_class": "fuzhi",
- "unicode": "e619",
- "unicode_decimal": 58905
- },
- {
- "icon_id": "46252113",
- "name": "16-左",
- "font_class": "a-Frame194",
- "unicode": "e61a",
- "unicode_decimal": 58906
- },
- {
- "icon_id": "46234515",
- "name": "gender-female-line",
- "font_class": "gender-female-line",
- "unicode": "e614",
- "unicode_decimal": 58900
- },
- {
- "icon_id": "46234139",
- "name": "gender-male-line",
- "font_class": "gender-male-line",
- "unicode": "e613",
- "unicode_decimal": 58899
- },
- {
- "icon_id": "46211262",
- "name": "刷新",
- "font_class": "shuaxin",
- "unicode": "e612",
- "unicode_decimal": 58898
- },
- {
- "icon_id": "46211223",
- "name": "箭头",
- "font_class": "jiantou",
- "unicode": "e610",
- "unicode_decimal": 58896
- },
- {
- "icon_id": "46211222",
- "name": "关闭",
- "font_class": "guanbi",
- "unicode": "e611",
- "unicode_decimal": 58897
- },
- {
- "icon_id": "46211227",
- "name": "搜索",
- "font_class": "sousuo",
- "unicode": "e60d",
- "unicode_decimal": 58893
- }
- ]
-}
diff --git a/public/font-v2/iconfont.svg b/public/font-v2/iconfont.svg
deleted file mode 100644
index 974c1e9..0000000
--- a/public/font-v2/iconfont.svg
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
diff --git a/public/font-v2/iconfont.ttf b/public/font-v2/iconfont.ttf
deleted file mode 100644
index 8065346..0000000
Binary files a/public/font-v2/iconfont.ttf and /dev/null differ
diff --git a/public/font-v2/iconfont.woff b/public/font-v2/iconfont.woff
deleted file mode 100644
index ad56f9c..0000000
Binary files a/public/font-v2/iconfont.woff and /dev/null differ
diff --git a/public/font-v2/iconfont.woff2 b/public/font-v2/iconfont.woff2
deleted file mode 100644
index 26cab29..0000000
Binary files a/public/font-v2/iconfont.woff2 and /dev/null differ
diff --git a/src/app/(main)/chat-history/page.tsx b/src/app/(main)/chat-history/page.tsx
index df5e453..b823f44 100644
--- a/src/app/(main)/chat-history/page.tsx
+++ b/src/app/(main)/chat-history/page.tsx
@@ -1,11 +1,11 @@
'use client';
-import { useMedia } from '@/hooks/tools';
-import ChatSidebar from '@/layout/components/ChatSidebar';
+import ChatSidebar from '@/layout/BasicLayout/components/ChatSidebar';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
+import { useLayoutStore } from '@/stores';
export default function ChatPage() {
- const response = useMedia();
+ const response = useLayoutStore((s) => s.response);
const router = useRouter();
useEffect(() => {
diff --git a/src/app/(main)/chat/[id]/Background.tsx b/src/app/(main)/chat/[id]/Background.tsx
index 6864821..46bac6f 100644
--- a/src/app/(main)/chat/[id]/Background.tsx
+++ b/src/app/(main)/chat/[id]/Background.tsx
@@ -9,7 +9,7 @@ function Background({ imageUrl }: { imageUrl: string }) {
return (
-
+
{imageUrl && (

)}
{
const [isFullIntroduction, setIsFullIntroduction] = useState(false);
const [shouldShowExpandButton, setShouldShowExpandButton] = useState(false);
const textRef = useRef
(null);
@@ -50,7 +50,7 @@ function ChatMessageUserHeader() {
}, [character.description]);
return (
-
+
Content generated by AI
);
-}
+});
-export default React.memo(ChatMessageUserHeader);
+export default ChatMessageUserHeader;
diff --git a/src/app/(main)/chat/[id]/Drawer/MaskCreate.tsx b/src/app/(main)/chat/[id]/Drawer/MaskCreate.tsx
new file mode 100644
index 0000000..6c826be
--- /dev/null
+++ b/src/app/(main)/chat/[id]/Drawer/MaskCreate.tsx
@@ -0,0 +1,12 @@
+'use client';
+
+import { MaskForm } from '@/app/(main)/profile/mask/MaskForm';
+import { ActiveTabType } from '.';
+
+export default function MaskCreate({ onActiveTab }: { onActiveTab: (tab: ActiveTabType) => void }) {
+ return (
+
+ onActiveTab('mask')} />
+
+ );
+}
diff --git a/src/app/(main)/chat/[id]/Drawer/MaskList.tsx b/src/app/(main)/chat/[id]/Drawer/MaskList.tsx
new file mode 100644
index 0000000..6cece20
--- /dev/null
+++ b/src/app/(main)/chat/[id]/Drawer/MaskList.tsx
@@ -0,0 +1,13 @@
+'use client';
+
+import MaskList from '@/app/(main)/profile/mask/MaskList';
+import { ActiveTabType } from '.';
+
+export default function Personal({ onActiveTab }: { onActiveTab: (tab: ActiveTabType) => void }) {
+ // const router = useRouter();
+ return (
+
+ onActiveTab('mask_create')} />
+
+ );
+}
diff --git a/src/app/(main)/chat/[id]/Drawer/Personal.tsx b/src/app/(main)/chat/[id]/Drawer/Personal.tsx
deleted file mode 100644
index 5f4efcb..0000000
--- a/src/app/(main)/chat/[id]/Drawer/Personal.tsx
+++ /dev/null
@@ -1,308 +0,0 @@
-'use client';
-import { useEffect, useState, useCallback } from 'react';
-import { z } from 'zod';
-import dayjs from 'dayjs';
-import {
- Form,
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from '@/components/ui/form';
-import { zodResolver } from '@hookform/resolvers/zod';
-import { useForm } from 'react-hook-form';
-import { Gender } from '@/types/user';
-import { Input } from '@/components/ui/input';
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from '@/components/ui/select';
-import { Label } from '@/components/ui/label';
-import { calculateAge, getDaysInMonth } from '@/lib/utils';
-import { Textarea } from '@/components/ui/textarea';
-import { Button } from '@/components/ui/button';
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
-} from '@/components/ui/alert-dialog';
-
-const currentYear = dayjs().year();
-const years = Array.from({ length: currentYear - 1950 + 1 }, (_, i) => `${1950 + i}`);
-const months = Array.from({ length: 12 }, (_, i) => `${i + 1}`.padStart(2, '0'));
-const monthTexts = Array.from({ length: 12 }, (_, i) => dayjs().month(i).format('MMM'));
-
-const characterFormSchema = z
- .object({
- nickname: z
- .string()
- .trim()
- .min(1, 'Please Enter nickname')
- .min(2, 'Nickname must be between 2 and 20 characters')
- .max(20, 'Nickname must be less than 20 characters'),
- sex: z.enum(Gender, { message: 'Please select gender' }),
- year: z.string().min(1, 'Please select year'),
- month: z.string().min(1, 'Please select month'),
- day: z.string().min(1, 'Please select day'),
- profile: z.string().trim().optional(),
- })
- .refine(
- (data) => {
- const age = calculateAge(data.year, data.month, data.day);
- return age >= 18;
- },
- {
- message: 'Character age must be at least 18 years old',
- path: ['year'],
- }
- )
- .refine(
- (data) => {
- if (data.profile) {
- if (data.profile.trim().length > 300) {
- return false;
- }
- return data.profile.trim().length >= 10;
- }
- return true;
- },
- {
- message: 'At least 10 characters',
- path: ['profile'],
- }
- );
-
-export default function Personal() {
- // 静态数据,模拟从接口获取的数据
- const chatSettingData = {
- nickname: 'John',
- sex: Gender.MALE,
- birthday: dayjs('1995-06-15').valueOf(),
- whoAmI: 'A creative and passionate developer',
- };
-
- const birthday = chatSettingData?.birthday ? dayjs(chatSettingData.birthday) : undefined;
-
- const form = useForm>({
- resolver: zodResolver(characterFormSchema),
- defaultValues: {
- nickname: chatSettingData?.nickname || '',
- sex: chatSettingData?.sex,
- year: birthday?.year().toString() || undefined,
- month:
- birthday?.month() !== undefined
- ? (birthday.month() + 1).toString().padStart(2, '0')
- : undefined,
- day: birthday?.date().toString().padStart(2, '0') || undefined,
- profile: chatSettingData?.whoAmI || '',
- },
- });
-
- const selectedYear = form.watch('year');
- const selectedMonth = form.watch('month');
- const days = selectedYear && selectedMonth ? getDaysInMonth(selectedYear, selectedMonth) : [];
-
- const genderTexts = [
- {
- value: Gender.MALE,
- label: 'Male',
- },
- {
- value: Gender.FEMALE,
- label: 'Female',
- },
- {
- value: Gender.OTHER,
- label: 'Other',
- },
- ];
-
- const gender = form.watch('sex');
- const genderText = genderTexts.find((text) => text.value === gender)?.label;
-
- return (
- <>
-
-
-
-
- {/*
-
-
-
*/}
-
-
- {/* 确认放弃修改的对话框 */}
- {/*
-
-
- Unsaved Edits
-
- The edited content will not be saved after exiting. Please confirm whether to continue
- exiting?
-
-
-
- setShowConfirmDialog(false)}>
- Cancel
-
-
- Exit
-
-
-
- */}
- >
- );
-}
diff --git a/src/app/(main)/chat/[id]/Drawer/Profile.tsx b/src/app/(main)/chat/[id]/Drawer/Profile.tsx
index aecfbc4..47b2a59 100644
--- a/src/app/(main)/chat/[id]/Drawer/Profile.tsx
+++ b/src/app/(main)/chat/[id]/Drawer/Profile.tsx
@@ -29,10 +29,10 @@ const ChatProfilePersona = React.memo(({ onActiveTab }: ProfileProps) => {
return (
-
My Chat Persona
+
Masked Identity Mode
onActiveTab('personal')}
+ onClick={() => onActiveTab('mask')}
>
Edit
diff --git a/src/app/(main)/chat/[id]/Drawer/index.tsx b/src/app/(main)/chat/[id]/Drawer/index.tsx
index 5a7888f..a682b05 100644
--- a/src/app/(main)/chat/[id]/Drawer/index.tsx
+++ b/src/app/(main)/chat/[id]/Drawer/index.tsx
@@ -1,6 +1,6 @@
'use client';
import Profile from './Profile';
-import Personal from './Personal';
+import MaskList from './MaskList';
import VoiceActor from './VoiceActor';
import Font from './Font';
import MaxToken from './MaxToken';
@@ -19,6 +19,8 @@ import {
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { useStreamChatStore } from '../stream-chat';
+import IconFont from '@/components/ui/iconFont';
+import MaskCreate from './MaskCreate';
type SettingProps = {
open: boolean;
@@ -26,7 +28,8 @@ type SettingProps = {
};
export type ActiveTabType =
| 'profile'
- | 'personal'
+ | 'mask'
+ | 'mask_create'
| 'history'
| 'voice_actor'
| 'font'
@@ -35,7 +38,8 @@ export type ActiveTabType =
| 'model';
const titleMap = {
- personal: 'Personal',
+ mask: 'Masked Identity Mode',
+ mask_create: 'Create Mask',
history: 'History',
voice_actor: 'Voice Actor',
font: 'Font',
@@ -44,6 +48,17 @@ const titleMap = {
model: 'Chat Model',
};
+const backMap = {
+ mask: 'profile',
+ mask_create: 'mask',
+ history: 'profile',
+ voice_actor: 'profile',
+ font: 'profile',
+ max_token: 'profile',
+ background: 'profile',
+ model: 'profile',
+} as const;
+
export default function SettingDialog({ open, onOpenChange }: SettingProps) {
const [activeTab, setActiveTab] = useState
('profile');
const updateUserChatSetting = useStreamChatStore((store) => store.updateUserChatSetting);
@@ -65,14 +80,19 @@ export default function SettingDialog({ open, onOpenChange }: SettingProps) {
titleMap[activeTab]
)}
{activeTab !== 'profile' && (
- setActiveTab('profile')}>
-
+ setActiveTab(backMap[activeTab])}
+ >
+
)}
{activeTab === 'profile' &&
}
- {activeTab === 'personal' &&
}
+ {activeTab === 'mask' &&
}
+ {activeTab === 'mask_create' &&
}
{activeTab === 'voice_actor' &&
}
{activeTab === 'font' &&
}
{activeTab === 'max_token' &&
}
diff --git a/src/app/(main)/chat/[id]/Input.tsx b/src/app/(main)/chat/[id]/Input.tsx
index 8ac2efd..0c00fa8 100644
--- a/src/app/(main)/chat/[id]/Input.tsx
+++ b/src/app/(main)/chat/[id]/Input.tsx
@@ -5,6 +5,7 @@ import { useAsyncFn } from '@/hooks/tools';
import { cn } from '@/lib/utils';
import { useStreamChatStore } from '@/app/(main)/chat/[id]/stream-chat';
import { useState, useRef, useEffect } from 'react';
+import IconFont from '@/components/ui/iconFont';
const AuthHeightTextarea = (
props: React.ComponentProps<'textarea'> & {
@@ -90,7 +91,9 @@ export default function Input() {
{/* 打电话按钮 */}
-
{}} iconfont="icon-gift-border" />
+ {}}>
+
+
{/* 语音录制按钮 */}
();
@@ -34,11 +35,11 @@ export default function ChatPage() {
setSettingOpen(!settingOpen)}
- className="absolute top-2 right-2"
+ className="absolute z-10 top-2 right-2"
variant="ghost"
size="small"
>
-
+
diff --git a/src/app/(main)/home/components/Character/index.tsx b/src/app/(main)/home/components/Character/index.tsx
index 3c57a18..b167fdd 100644
--- a/src/app/(main)/home/components/Character/index.tsx
+++ b/src/app/(main)/home/components/Character/index.tsx
@@ -23,6 +23,8 @@ const Character = () => {
items={dataSource}
+ enableLazyRender
+ lazyRenderMargin="500px"
columns={(width) => {
const cardWidth = width > 1200 ? 256 : width > 588 ? 200 : width > 375 ? 170 : 150;
return Math.floor(width / cardWidth);
diff --git a/src/app/(main)/home/components/Filter.tsx b/src/app/(main)/home/components/Filter.tsx
index 9d6accf..ff8555d 100644
--- a/src/app/(main)/home/components/Filter.tsx
+++ b/src/app/(main)/home/components/Filter.tsx
@@ -7,6 +7,7 @@ import { useHomeStore } from '../store';
import { useQuery } from '@tanstack/react-query';
import { fetchCharacterTags } from '@/services/editor';
import { useRef } from 'react';
+import { useTranslations } from 'next-intl';
const Filter = () => {
const tab = useHomeStore((state) => state.tab);
@@ -14,6 +15,7 @@ const Filter = () => {
const ref = useRef(null);
const selectedTags = useHomeStore((state) => state.selectedTags);
const setSelectedTags = useHomeStore((state) => state.setSelectedTags);
+ const t = useTranslations('home');
// useEffect(() => {
// const mainContent = document.getElementById('main-content');
@@ -48,13 +50,13 @@ const Filter = () => {
const tabs = [
{
- label: 'Story',
+ label: t('story'),
value: 'story',
icon: 'icon-story',
activeIcon: 'icon-story-active',
},
{
- label: 'Character',
+ label: t('character'),
value: 'character',
icon: 'icon-character',
activeIcon: 'icon-character-active',
diff --git a/src/app/(main)/home/components/Header.tsx b/src/app/(main)/home/components/Header.tsx
index 6e28607..4638aab 100644
--- a/src/app/(main)/home/components/Header.tsx
+++ b/src/app/(main)/home/components/Header.tsx
@@ -4,10 +4,10 @@ import Image from 'next/image';
import { IconButton } from '@/components/ui/button';
import Link from 'next/link';
import React from 'react';
-import { useMedia } from '@/hooks/tools';
+import { useLayoutStore } from '@/stores';
const Header = React.memo(() => {
- const response = useMedia();
+ const response = useLayoutStore((s) => s.response);
return (
//
diff --git a/src/app/(main)/home/page.tsx b/src/app/(main)/home/page.tsx
index 530c3b3..c935d73 100644
--- a/src/app/(main)/home/page.tsx
+++ b/src/app/(main)/home/page.tsx
@@ -6,11 +6,11 @@ import Story from './components/Story';
import Character from './components/Character';
import Filter from './components/Filter';
import { useHomeStore } from './store';
-import { useMedia } from '@/hooks/tools';
+import { useLayoutStore } from '@/stores';
const HomePage = () => {
const tab = useHomeStore((state) => state.tab);
- const response = useMedia();
+ const response = useLayoutStore((s) => s.response);
return (
<>
diff --git a/src/app/(main)/profile/account/account-page.tsx b/src/app/(main)/profile/account/account-page.tsx
index e1a5315..022842b 100644
--- a/src/app/(main)/profile/account/account-page.tsx
+++ b/src/app/(main)/profile/account/account-page.tsx
@@ -14,6 +14,7 @@ import {
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { IconButton, Button } from '@/components/ui/button';
import { useCurrentUser, useDeleteUser } from '@/hooks/auth';
+import ProfileLayout from '@/layout/ProfileLayout';
import { ThirdType } from '@/services/auth';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -25,10 +26,6 @@ const AccountPage = () => {
const [isDisabling, setIsDisabling] = useState(false);
const { mutateAsync: deleteUser } = useDeleteUser();
- const handleBack = () => {
- router.back();
- };
-
const handleDisableAccount = async () => {
setIsDisabling(true);
try {
@@ -56,15 +53,7 @@ const AccountPage = () => {
};
return (
-
- {/* 标题栏 */}
-
-
-
-
-
Account
-
-
+
{/* 账户信息容器 */}
@@ -114,7 +103,7 @@ const AccountPage = () => {
-
+
);
};
diff --git a/src/app/(main)/profile/components/CharacterCard.tsx b/src/app/(main)/profile/components/CharacterCard.tsx
deleted file mode 100644
index b51ad5c..0000000
--- a/src/app/(main)/profile/components/CharacterCard.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import { Tag } from '@/components/ui/tag'
-import { cn, formatNumberToKMB } from '@/lib/utils'
-import { AIPermission, AiUserBaseListOutput } from '@/services/create'
-import Image from 'next/image'
-import Link from 'next/link'
-
-const CharacterCard = ({
- character,
- isHovered,
- onHover,
-}: {
- character: AiUserBaseListOutput
- isHovered: boolean
- onHover: (hovered: boolean) => void
-}) => {
- // 根据权限判断是否私密
- const isPrivate = character.permission === AIPermission.Private
-
- return (
-
-
onHover(true)}
- onMouseLeave={() => onHover(false)}
- >
- {/* 角色图片 */}
-
-
-
- {/* 私密标识 */}
-
-
-
-
- {/* 底部遮罩层 */}
-
- {/* 描述文字 - hover时显示,带过渡动效 */}
-
- {character.introduction || ''}
-
-
- {/* 点赞数 - 暂时用固定值,实际应该从API获取 */}
-
- {/*

*/}
-
-
- {formatNumberToKMB(character.likedNum ?? 0)}
-
-
-
-
-
- {/* 角色信息 */}
-
- {/* 角色名称 */}
-
{character.nickname}
-
- {/* 标签 */}
-
- {/* 性格标签 */}
- {character.characterName && {character.characterName}}
-
- {/* 标签 */}
- {character.tagName && {character.tagName}}
-
-
-
-
- )
-}
-
-export default CharacterCard
diff --git a/src/app/(main)/profile/components/CharacterCardAdd.tsx b/src/app/(main)/profile/components/CharacterCardAdd.tsx
deleted file mode 100644
index 40f018d..0000000
--- a/src/app/(main)/profile/components/CharacterCardAdd.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { useCurrentUser } from '@/hooks/auth'
-import { useGetAICharacterList } from '@/hooks/create'
-import useCreatorNavigation from '@/hooks/useCreatorNavigation'
-import Image from 'next/image'
-import { useSetAtom } from 'jotai'
-import { isVipDrawerOpenAtom } from '@/atoms/im'
-import { VipType } from '@/services/wallet'
-
-const CharacterCardAdd = () => {
- const { routerToCreate } = useCreatorNavigation()
- const { data: user } = useCurrentUser()
- const { isMember, canCreateAiCount } = user || {}
- const { data: characters = [] } = useGetAICharacterList()
- const isFull = characters.length >= (canCreateAiCount || 0)
- const setIsVipDrawerOpen = useSetAtom(isVipDrawerOpenAtom)
-
- const handleVip = () => {
- setIsVipDrawerOpen({ open: true, vipType: VipType.ADD_CREATE_AI })
- }
-
- if (isMember && isFull) {
- return null
- }
-
- if (!isMember && isFull) {
- return (
-
-
-
-
-
- Add More Characters
-
-
-
-
- )
- }
-
- return (
-
-
-
-
- Create a Character
-
-
-
- )
-}
-
-export default CharacterCardAdd
diff --git a/src/app/(main)/profile/components/CharacterCardVipAdd.tsx b/src/app/(main)/profile/components/CharacterCardVipAdd.tsx
deleted file mode 100644
index 9468d9a..0000000
--- a/src/app/(main)/profile/components/CharacterCardVipAdd.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-const CharacterCardVipAdd = () => {
- return (
-
-
-
-
- Add More Character
-
-
-
- )
-}
-
-export default CharacterCardVipAdd
diff --git a/src/app/(main)/profile/components/CharacterList.tsx b/src/app/(main)/profile/components/CharacterList.tsx
deleted file mode 100644
index 2616da2..0000000
--- a/src/app/(main)/profile/components/CharacterList.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { useGetAICharacterList } from '@/hooks/create'
-import { useState } from 'react'
-import CharacterCard from './CharacterCard'
-import CharacterCardAdd from './CharacterCardAdd'
-import { useCurrentUser } from '@/hooks/auth'
-import { isVipDrawerOpenAtom } from '@/atoms/im'
-import { useSetAtom } from 'jotai'
-import { VipType } from '@/services/wallet'
-
-const AlbumSkeleton = () => (
-
-)
-
-const CharacterList = () => {
- const { data: characters = [], isPending } = useGetAICharacterList()
- const { data: user } = useCurrentUser()
- const { isMember, canCreateAiCount } = user || {}
- const setIsVipDrawerOpen = useSetAtom(isVipDrawerOpenAtom)
-
- const [hoveredCard, setHoveredCard] = useState
(null)
-
- const handleUnlockMore = () => {
- setIsVipDrawerOpen({ open: true, vipType: VipType.ADD_CREATE_AI })
- }
-
- const renderList = () => {
- if (isPending) {
- return (
-
- {Array.from({ length: 3 }).map((_, index) => (
-
- ))}
-
- )
- }
-
- return (
-
- {characters.map((character) => (
- setHoveredCard(hovered ? (character.aiId ?? null) : null)}
- />
- ))}
-
-
- )
- }
-
- return (
-
- {/* 标题和按钮 */}
-
-
-
Characters
-
- {characters.length}/{canCreateAiCount}
-
-
- {!isMember && (
-
-
-
-

-
- Unlock More
-
-
-
-
- )}
-
-
- {renderList()}
-
- )
-}
-
-export default CharacterList
diff --git a/src/app/(main)/profile/components/ProfileDropdown.tsx b/src/app/(main)/profile/components/ProfileDropdown.tsx
index 8f0b5a4..6e3da29 100644
--- a/src/app/(main)/profile/components/ProfileDropdown.tsx
+++ b/src/app/(main)/profile/components/ProfileDropdown.tsx
@@ -14,13 +14,14 @@ import { useState } from 'react';
import { useLayoutStore } from '@/stores';
import { useStreamChatStore } from '../../chat/[id]/stream-chat';
import { useAsyncFn } from '@/hooks/tools';
+import IconFont from '@/components/ui/iconFont';
const ProfileDropdownItem = ({
icon,
children,
onClick,
}: {
- icon: string;
+ icon: string | React.ReactNode;
children: React.ReactNode;
onClick?: () => void;
}) => {
@@ -30,7 +31,7 @@ const ProfileDropdownItem = ({
className="flex w-full items-center justify-between gap-3 cursor-pointer px-5 py-4 hover:bg-white/5 transition-colors"
>
-
+ {typeof icon === 'string' ?
: icon}
{children}
@@ -61,7 +62,7 @@ const ProfileDropdown = () => {
| {
type: 'item';
label: string;
- icon: string;
+ icon: string | React.ReactNode;
href?: string;
target?: string;
onClick?: () => void;
@@ -73,6 +74,12 @@ const ProfileDropdown = () => {
icon: 'icon-icon_order_remark',
href: '/profile/edit',
},
+ {
+ type: 'item',
+ label: 'Masked Identity Mode',
+ icon: ,
+ href: '/profile/mask',
+ },
{
type: 'item',
label: 'Account',
diff --git a/src/app/(main)/profile/edit/page.tsx b/src/app/(main)/profile/edit/page.tsx
index 73ff8d1..5062144 100644
--- a/src/app/(main)/profile/edit/page.tsx
+++ b/src/app/(main)/profile/edit/page.tsx
@@ -27,6 +27,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { calculateAge } from '@/lib/utils';
import dayjs from 'dayjs';
+import ProfileLayout from '@/layout/ProfileLayout';
const schema = z
.object({
@@ -196,15 +197,7 @@ const EditPage = () => {
}, [selectedYear, selectedMonth, selectedDay, form]);
return (
-
- {/* 标题栏 */}
-
-
-
-
-
Edit Profile
-
-
+
{/* 表单容器 */}
-
+
);
};
diff --git a/src/app/(main)/profile/mask/MaskForm.tsx b/src/app/(main)/profile/mask/MaskForm.tsx
new file mode 100644
index 0000000..97ca3cc
--- /dev/null
+++ b/src/app/(main)/profile/mask/MaskForm.tsx
@@ -0,0 +1,155 @@
+'use client';
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '@/components/ui/form';
+import * as z from 'zod';
+import { Gender } from '@/types/user';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useCurrentUser } from '@/hooks/auth';
+import { useAsyncFn } from '@/hooks/tools';
+import { Input } from '@/components/ui/input';
+import GenderInput from '@/components/features/genderInput';
+import { Button } from '@/components/ui/button';
+import { InputNumber } from '@/components/ui/inputnumber';
+import { Textarea } from '@/components/ui/textarea';
+
+const schema = z.object({
+ nickname: z
+ .string()
+ .trim()
+ .min(1, 'Nickname is required')
+ .min(2, 'Nickname must be between 2 and 20 characters'),
+ gender: z.enum(Gender, { message: 'Please select a gender' }),
+ age: z.number().min(1, 'Age is required'),
+ whoAmI: z.string(),
+});
+
+type MaskFormProps = {
+ onSubmitSuccess?: (data: any) => void;
+};
+
+export const MaskForm = ({ onSubmitSuccess }: MaskFormProps) => {
+ const { data: user } = useCurrentUser();
+ const form = useForm>({
+ resolver: zodResolver(schema),
+ defaultValues: {
+ nickname: user?.nickname || '',
+ gender: user?.sex,
+ age: 18,
+ whoAmI: '',
+ },
+ mode: 'onChange',
+ });
+
+ const { run: onSubmitFn, loading } = useAsyncFn(async (data: any) => {
+ onSubmitSuccess?.(data);
+ });
+
+ const {
+ formState: { isValid, isDirty },
+ } = form;
+
+ return (
+
+
+
+
+ );
+};
diff --git a/src/app/(main)/profile/mask/MaskList.tsx b/src/app/(main)/profile/mask/MaskList.tsx
new file mode 100644
index 0000000..32a555f
--- /dev/null
+++ b/src/app/(main)/profile/mask/MaskList.tsx
@@ -0,0 +1,70 @@
+'use client';
+
+import { Gender } from '@/types/user';
+import { useCurrentUser } from '@/hooks/auth';
+import { useRouter } from 'next/navigation';
+import { Checkbox } from '@/components/ui/checkbox';
+import { Button } from '@/components/ui/button';
+type MaskListProps = {
+ selectAble?: boolean;
+ value?: null;
+ onChange?: (mask: any) => void;
+ onAdd?: () => void;
+};
+
+export default function MaskList(props: MaskListProps) {
+ const { selectAble, value, onChange, onAdd } = props;
+ const { data: user } = useCurrentUser();
+ const router = useRouter();
+
+ const masks = [
+ {
+ nickname: 'NickName',
+ gender: 0,
+ age: 18,
+ whoAmI: ' whoAmI whoAmI whoAmI whoAmI whoAmI whoAmI whoAmI whoAmI',
+ },
+ ];
+
+ const handleSelect = (mask: any) => {
+ if (selectAble) {
+ onChange?.(mask);
+ } else {
+ router.push(`/profile/mask/${mask.nickname}`);
+ }
+ };
+
+ const iconRender = (mask: any) => {
+ if (selectAble) {
+ return ;
+ } else {
+ return ;
+ }
+ };
+
+ return (
+
+ {masks.map((mask) => (
+
+
handleSelect(mask)}
+ className="flex bg-white/10 px-4 py-3 items-center justify-between"
+ >
+
+ {mask.nickname}、{mask.gender === Gender.MALE ? 'Male' : 'Female'}、{mask.age}
+
+ {iconRender(mask)}
+
+
{mask.whoAmI}
+
+ ))}
+ {onAdd && (
+
+
+
+ )}
+
+ );
+}
diff --git a/src/app/(main)/profile/mask/[id]/page.tsx b/src/app/(main)/profile/mask/[id]/page.tsx
new file mode 100644
index 0000000..421ac66
--- /dev/null
+++ b/src/app/(main)/profile/mask/[id]/page.tsx
@@ -0,0 +1,19 @@
+'use client';
+import { useCurrentUser } from '@/hooks/auth';
+import { MaskForm } from '../MaskForm';
+import { IconButton } from '@/components/ui/button';
+import { useRouter } from 'next/navigation';
+import ProfileLayout from '@/layout/ProfileLayout';
+
+export default function MaskPage() {
+ const { data: user } = useCurrentUser();
+ const router = useRouter();
+
+ return (
+
+
+ router.push(`/profile/mask`)} />
+
+
+ );
+}
diff --git a/src/app/(main)/profile/mask/page.tsx b/src/app/(main)/profile/mask/page.tsx
new file mode 100644
index 0000000..757dfd0
--- /dev/null
+++ b/src/app/(main)/profile/mask/page.tsx
@@ -0,0 +1,41 @@
+'use client';
+import { Button, IconButton } from '@/components/ui/button';
+import { useRouter } from 'next/navigation';
+import ProfileLayout from '@/layout/ProfileLayout';
+import Link from 'next/link';
+import IconFont from '@/components/ui/iconFont';
+import { useLayoutStore } from '@/stores';
+import MaskList from './MaskList';
+
+export default function MaskPage() {
+ const response = useLayoutStore((s) => s.response);
+
+ const router = useRouter();
+
+ return (
+
+
+
+ }
+ rightDom={
+ response?.isPC && (
+ router.push('/profile/mask/new')}
+ size={32}
+ className="text-white cursor-pointer"
+ type="icon-tianjia"
+ />
+ )
+ }
+ title="Masked Identity Mode"
+ >
+
+
+
+
+ );
+}
diff --git a/src/app/(main)/profile/page.tsx b/src/app/(main)/profile/page.tsx
index 546e01f..3c39208 100644
--- a/src/app/(main)/profile/page.tsx
+++ b/src/app/(main)/profile/page.tsx
@@ -82,8 +82,6 @@ export default function ProfilePage() {
- {/* */}
-
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 7f7edd4..e74e4b2 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,9 +4,9 @@ import localFont from 'next/font/local';
import '../css/iconfont.css';
import '../css/iconfont-v2.css';
import './globals.css';
-import { Providers } from '@/lib/providers';
-import { DeviceIdProvider } from '@/components/device-id-provider';
-import ProgressBar from '@/context/progress';
+import { Providers } from '@/layout/Providers';
+import ProgressBar from '@/layout/Providers/ProgressBar';
+import Script from 'next/script';
const poppins = Poppins({
variable: '--font-poppins',
@@ -50,11 +50,12 @@ export default async function RootLayout({
-
-
- {children}
-
-
+
+ {children}