This commit is contained in:
@@ -10,3 +10,6 @@ GITHUB_CLIENT_SECRET=
|
|||||||
|
|
||||||
// Database
|
// Database
|
||||||
DATABASE_URL=
|
DATABASE_URL=
|
||||||
|
|
||||||
|
// DashScore
|
||||||
|
DASHSCORE_API_KEY=
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ GITHUB_CLIENT_SECRET=your-client-secret
|
|||||||
|
|
||||||
# 数据库
|
# 数据库
|
||||||
DATABASE_URL=postgresql://username:password@localhost:5432/database_name
|
DATABASE_URL=postgresql://username:password@localhost:5432/database_name
|
||||||
|
|
||||||
|
// DashScore
|
||||||
|
DASHSCORE_API_KEY=
|
||||||
```
|
```
|
||||||
|
|
||||||
## 重要配置细节
|
## 重要配置细节
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
### 国际化与辅助功能
|
### 国际化与辅助功能
|
||||||
- **next-intl** - 国际化解决方案
|
- **next-intl** - 国际化解决方案
|
||||||
- **edge-tts-universal** - 跨平台文本转语音
|
- **qwen3-tts-flash** - 通义千问语音合成
|
||||||
|
|
||||||
### 开发工具
|
### 开发工具
|
||||||
- **ESLint** - 代码质量检查
|
- **ESLint** - 代码质量检查
|
||||||
|
|||||||
@@ -40,8 +40,8 @@
|
|||||||
"update": "Aktualisieren",
|
"update": "Aktualisieren",
|
||||||
"text1": "Text 1",
|
"text1": "Text 1",
|
||||||
"text2": "Text 2",
|
"text2": "Text 2",
|
||||||
"locale1": "Sprache 1",
|
"language1": "Sprache 1",
|
||||||
"locale2": "Sprache 2",
|
"language2": "Sprache 2",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"delete": "Löschen"
|
"delete": "Löschen"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,8 +40,8 @@
|
|||||||
"update": "Update",
|
"update": "Update",
|
||||||
"text1": "Text 1",
|
"text1": "Text 1",
|
||||||
"text2": "Text 2",
|
"text2": "Text 2",
|
||||||
"locale1": "Locale 1",
|
"language1": "Locale 1",
|
||||||
"locale2": "Locale 2",
|
"language2": "Locale 2",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"delete": "Delete"
|
"delete": "Delete"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,8 +40,8 @@
|
|||||||
"update": "Mettre à jour",
|
"update": "Mettre à jour",
|
||||||
"text1": "Texte 1",
|
"text1": "Texte 1",
|
||||||
"text2": "Texte 2",
|
"text2": "Texte 2",
|
||||||
"locale1": "Langue 1",
|
"language1": "Langue 1",
|
||||||
"locale2": "Langue 2",
|
"language2": "Langue 2",
|
||||||
"edit": "Modifier",
|
"edit": "Modifier",
|
||||||
"delete": "Supprimer"
|
"delete": "Supprimer"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,8 +40,8 @@
|
|||||||
"update": "Aggiorna",
|
"update": "Aggiorna",
|
||||||
"text1": "Testo 1",
|
"text1": "Testo 1",
|
||||||
"text2": "Testo 2",
|
"text2": "Testo 2",
|
||||||
"locale1": "Lingua 1",
|
"language1": "Lingua 1",
|
||||||
"locale2": "Lingua 2",
|
"language2": "Lingua 2",
|
||||||
"edit": "Modifica",
|
"edit": "Modifica",
|
||||||
"delete": "Elimina"
|
"delete": "Elimina"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,8 +40,8 @@
|
|||||||
"update": "更新",
|
"update": "更新",
|
||||||
"text1": "テキスト1",
|
"text1": "テキスト1",
|
||||||
"text2": "テキスト2",
|
"text2": "テキスト2",
|
||||||
"locale1": "言語1",
|
"language1": "言語1",
|
||||||
"locale2": "言語2",
|
"language2": "言語2",
|
||||||
"edit": "編集",
|
"edit": "編集",
|
||||||
"delete": "削除"
|
"delete": "削除"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,8 +40,8 @@
|
|||||||
"update": "업데이트",
|
"update": "업데이트",
|
||||||
"text1": "텍스트 1",
|
"text1": "텍스트 1",
|
||||||
"text2": "텍스트 2",
|
"text2": "텍스트 2",
|
||||||
"locale1": "언어 1",
|
"language1": "언어 1",
|
||||||
"locale2": "언어 2",
|
"language2": "언어 2",
|
||||||
"edit": "편집",
|
"edit": "편집",
|
||||||
"delete": "삭제"
|
"delete": "삭제"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,8 +40,8 @@
|
|||||||
"update": "يېڭىلاش",
|
"update": "يېڭىلاش",
|
||||||
"text1": "تېكىست 1",
|
"text1": "تېكىست 1",
|
||||||
"text2": "تېكىست 2",
|
"text2": "تېكىست 2",
|
||||||
"locale1": "تىل 1",
|
"language1": "تىل 1",
|
||||||
"locale2": "تىل 2",
|
"language2": "تىل 2",
|
||||||
"edit": "تەھرىرلەش",
|
"edit": "تەھرىرلەش",
|
||||||
"delete": "ئۆچۈرۈش"
|
"delete": "ئۆچۈرۈش"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,8 +40,8 @@
|
|||||||
"update": "更新",
|
"update": "更新",
|
||||||
"text1": "文本1",
|
"text1": "文本1",
|
||||||
"text2": "文本2",
|
"text2": "文本2",
|
||||||
"locale1": "语言1",
|
"language1": "语言1",
|
||||||
"locale2": "语言2",
|
"language2": "语言2",
|
||||||
"edit": "编辑",
|
"edit": "编辑",
|
||||||
"delete": "删除"
|
"delete": "删除"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"better-auth": "^1.4.10",
|
"better-auth": "^1.4.10",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"edge-tts-universal": "^1.3.3",
|
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"next": "16.1.1",
|
"next": "16.1.1",
|
||||||
"next-intl": "^4.7.0",
|
"next-intl": "^4.7.0",
|
||||||
|
|||||||
186
pnpm-lock.yaml
generated
186
pnpm-lock.yaml
generated
@@ -27,9 +27,6 @@ importers:
|
|||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^17.2.3
|
specifier: ^17.2.3
|
||||||
version: 17.2.3
|
version: 17.2.3
|
||||||
edge-tts-universal:
|
|
||||||
specifier: ^1.3.3
|
|
||||||
version: 1.3.3
|
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: ^0.562.0
|
specifier: ^0.562.0
|
||||||
version: 0.562.0(react@19.2.3)
|
version: 0.562.0(react@19.2.3)
|
||||||
@@ -1238,10 +1235,6 @@ packages:
|
|||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
agent-base@7.1.4:
|
|
||||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
|
||||||
engines: {node: '>= 14'}
|
|
||||||
|
|
||||||
ajv@6.12.6:
|
ajv@6.12.6:
|
||||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||||
|
|
||||||
@@ -1299,9 +1292,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
asynckit@0.4.0:
|
|
||||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
|
||||||
|
|
||||||
available-typed-arrays@1.0.7:
|
available-typed-arrays@1.0.7:
|
||||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1314,9 +1304,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==}
|
resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
axios@1.13.2:
|
|
||||||
resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==}
|
|
||||||
|
|
||||||
axobject-query@4.1.0:
|
axobject-query@4.1.0:
|
||||||
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
|
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1503,10 +1490,6 @@ packages:
|
|||||||
color-name@1.1.4:
|
color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
combined-stream@1.0.8:
|
|
||||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
|
|
||||||
commander@12.1.0:
|
commander@12.1.0:
|
||||||
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
|
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -1527,9 +1510,6 @@ packages:
|
|||||||
cookie-es@1.2.2:
|
cookie-es@1.2.2:
|
||||||
resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==}
|
resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==}
|
||||||
|
|
||||||
cross-fetch@4.1.0:
|
|
||||||
resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==}
|
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -1613,10 +1593,6 @@ packages:
|
|||||||
defu@6.1.4:
|
defu@6.1.4:
|
||||||
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
|
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
|
||||||
|
|
||||||
delayed-stream@1.0.0:
|
|
||||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
|
||||||
engines: {node: '>=0.4.0'}
|
|
||||||
|
|
||||||
denque@2.1.0:
|
denque@2.1.0:
|
||||||
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
@@ -1738,10 +1714,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
edge-tts-universal@1.3.3:
|
|
||||||
resolution: {integrity: sha512-jlUvYGsJ+o93FRPWQDQa/E7jqYbuLJAKKq9qvylo0/yHE1vtp4HCSzSAamviBewmpaNHlEJm+eEMimJXfu98zw==}
|
|
||||||
engines: {node: ^18.17 || ^20.9 || >=22, npm: '>=9'}
|
|
||||||
|
|
||||||
effect@3.18.4:
|
effect@3.18.4:
|
||||||
resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==}
|
resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==}
|
||||||
|
|
||||||
@@ -1976,15 +1948,6 @@ packages:
|
|||||||
flatted@3.3.3:
|
flatted@3.3.3:
|
||||||
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
|
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
|
||||||
|
|
||||||
follow-redirects@1.15.11:
|
|
||||||
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
|
|
||||||
engines: {node: '>=4.0'}
|
|
||||||
peerDependencies:
|
|
||||||
debug: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
debug:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
for-each@0.3.5:
|
for-each@0.3.5:
|
||||||
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1993,10 +1956,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
form-data@4.0.5:
|
|
||||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
|
||||||
engines: {node: '>= 6'}
|
|
||||||
|
|
||||||
fs-constants@1.0.0:
|
fs-constants@1.0.0:
|
||||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||||
|
|
||||||
@@ -2122,10 +2081,6 @@ packages:
|
|||||||
http-status-codes@2.3.0:
|
http-status-codes@2.3.0:
|
||||||
resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==}
|
resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==}
|
||||||
|
|
||||||
https-proxy-agent@7.0.6:
|
|
||||||
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
|
||||||
engines: {node: '>= 14'}
|
|
||||||
|
|
||||||
iconv-lite@0.7.0:
|
iconv-lite@0.7.0:
|
||||||
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
|
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -2291,11 +2246,6 @@ packages:
|
|||||||
isexe@2.0.0:
|
isexe@2.0.0:
|
||||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
|
|
||||||
isomorphic-ws@5.0.0:
|
|
||||||
resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==}
|
|
||||||
peerDependencies:
|
|
||||||
ws: '*'
|
|
||||||
|
|
||||||
iterator.prototype@1.1.5:
|
iterator.prototype@1.1.5:
|
||||||
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
|
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2484,14 +2434,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
mime-db@1.52.0:
|
|
||||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
|
|
||||||
mime-types@2.1.35:
|
|
||||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
|
|
||||||
mimic-response@3.1.0:
|
mimic-response@3.1.0:
|
||||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2588,15 +2530,6 @@ packages:
|
|||||||
node-fetch-native@1.6.7:
|
node-fetch-native@1.6.7:
|
||||||
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
||||||
|
|
||||||
node-fetch@2.7.0:
|
|
||||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
|
||||||
engines: {node: 4.x || >=6.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
encoding: ^0.1.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
encoding:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
node-mock-http@1.0.3:
|
node-mock-http@1.0.3:
|
||||||
resolution: {integrity: sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==}
|
resolution: {integrity: sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==}
|
||||||
|
|
||||||
@@ -2821,9 +2754,6 @@ packages:
|
|||||||
proper-lockfile@4.1.2:
|
proper-lockfile@4.1.2:
|
||||||
resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
|
resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
|
||||||
|
|
||||||
proxy-from-env@1.1.0:
|
|
||||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
|
||||||
|
|
||||||
pump@3.0.3:
|
pump@3.0.3:
|
||||||
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
|
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
|
||||||
|
|
||||||
@@ -3120,9 +3050,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
|
|
||||||
tr46@0.0.3:
|
|
||||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
|
||||||
|
|
||||||
ts-api-utils@2.1.0:
|
ts-api-utils@2.1.0:
|
||||||
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
|
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
|
||||||
engines: {node: '>=18.12'}
|
engines: {node: '>=18.12'}
|
||||||
@@ -3275,10 +3202,6 @@ packages:
|
|||||||
util-deprecate@1.0.2:
|
util-deprecate@1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
uuid@11.1.0:
|
|
||||||
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
valibot@1.2.0:
|
valibot@1.2.0:
|
||||||
resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==}
|
resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3287,12 +3210,6 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
webidl-conversions@3.0.1:
|
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
|
||||||
|
|
||||||
which-boxed-primitive@1.1.1:
|
which-boxed-primitive@1.1.1:
|
||||||
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3321,25 +3238,10 @@ packages:
|
|||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
ws@8.18.3:
|
|
||||||
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
|
||||||
engines: {node: '>=10.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
bufferutil: ^4.0.1
|
|
||||||
utf-8-validate: '>=5.0.2'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
bufferutil:
|
|
||||||
optional: true
|
|
||||||
utf-8-validate:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
wsl-utils@0.1.0:
|
wsl-utils@0.1.0:
|
||||||
resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
|
resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
xml-escape@1.1.0:
|
|
||||||
resolution: {integrity: sha512-B/T4sDK8Z6aUh/qNr7mjKAwwncIljFuUP+DO/D5hloYFj+90O88z8Wf7oSucZTHxBAsC1/CTP4rtx/x1Uf72Mg==}
|
|
||||||
|
|
||||||
xtend@4.0.2:
|
xtend@4.0.2:
|
||||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||||
engines: {node: '>=0.4'}
|
engines: {node: '>=0.4'}
|
||||||
@@ -4582,8 +4484,6 @@ snapshots:
|
|||||||
|
|
||||||
acorn@8.15.0: {}
|
acorn@8.15.0: {}
|
||||||
|
|
||||||
agent-base@7.1.4: {}
|
|
||||||
|
|
||||||
ajv@6.12.6:
|
ajv@6.12.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal: 3.1.3
|
fast-deep-equal: 3.1.3
|
||||||
@@ -4675,8 +4575,6 @@ snapshots:
|
|||||||
|
|
||||||
async-function@1.0.0: {}
|
async-function@1.0.0: {}
|
||||||
|
|
||||||
asynckit@0.4.0: {}
|
|
||||||
|
|
||||||
available-typed-arrays@1.0.7:
|
available-typed-arrays@1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
possible-typed-array-names: 1.1.0
|
possible-typed-array-names: 1.1.0
|
||||||
@@ -4685,14 +4583,6 @@ snapshots:
|
|||||||
|
|
||||||
axe-core@4.11.0: {}
|
axe-core@4.11.0: {}
|
||||||
|
|
||||||
axios@1.13.2:
|
|
||||||
dependencies:
|
|
||||||
follow-redirects: 1.15.11
|
|
||||||
form-data: 4.0.5
|
|
||||||
proxy-from-env: 1.1.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- debug
|
|
||||||
|
|
||||||
axobject-query@4.1.0: {}
|
axobject-query@4.1.0: {}
|
||||||
|
|
||||||
babel-plugin-react-compiler@1.0.0:
|
babel-plugin-react-compiler@1.0.0:
|
||||||
@@ -4896,10 +4786,6 @@ snapshots:
|
|||||||
|
|
||||||
color-name@1.1.4: {}
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
combined-stream@1.0.8:
|
|
||||||
dependencies:
|
|
||||||
delayed-stream: 1.0.0
|
|
||||||
|
|
||||||
commander@12.1.0: {}
|
commander@12.1.0: {}
|
||||||
|
|
||||||
concat-map@0.0.1: {}
|
concat-map@0.0.1: {}
|
||||||
@@ -4912,12 +4798,6 @@ snapshots:
|
|||||||
|
|
||||||
cookie-es@1.2.2: {}
|
cookie-es@1.2.2: {}
|
||||||
|
|
||||||
cross-fetch@4.1.0:
|
|
||||||
dependencies:
|
|
||||||
node-fetch: 2.7.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
@@ -4993,8 +4873,6 @@ snapshots:
|
|||||||
|
|
||||||
defu@6.1.4: {}
|
defu@6.1.4: {}
|
||||||
|
|
||||||
delayed-stream@1.0.0: {}
|
|
||||||
|
|
||||||
denque@2.1.0: {}
|
denque@2.1.0: {}
|
||||||
|
|
||||||
destr@2.0.5: {}
|
destr@2.0.5: {}
|
||||||
@@ -5031,22 +4909,6 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
gopd: 1.2.0
|
gopd: 1.2.0
|
||||||
|
|
||||||
edge-tts-universal@1.3.3:
|
|
||||||
dependencies:
|
|
||||||
axios: 1.13.2
|
|
||||||
cross-fetch: 4.1.0
|
|
||||||
https-proxy-agent: 7.0.6
|
|
||||||
isomorphic-ws: 5.0.0(ws@8.18.3)
|
|
||||||
uuid: 11.1.0
|
|
||||||
ws: 8.18.3
|
|
||||||
xml-escape: 1.1.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- bufferutil
|
|
||||||
- debug
|
|
||||||
- encoding
|
|
||||||
- supports-color
|
|
||||||
- utf-8-validate
|
|
||||||
|
|
||||||
effect@3.18.4:
|
effect@3.18.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/spec': 1.0.0
|
'@standard-schema/spec': 1.0.0
|
||||||
@@ -5427,8 +5289,6 @@ snapshots:
|
|||||||
|
|
||||||
flatted@3.3.3: {}
|
flatted@3.3.3: {}
|
||||||
|
|
||||||
follow-redirects@1.15.11: {}
|
|
||||||
|
|
||||||
for-each@0.3.5:
|
for-each@0.3.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
@@ -5438,14 +5298,6 @@ snapshots:
|
|||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
|
|
||||||
form-data@4.0.5:
|
|
||||||
dependencies:
|
|
||||||
asynckit: 0.4.0
|
|
||||||
combined-stream: 1.0.8
|
|
||||||
es-set-tostringtag: 2.1.0
|
|
||||||
hasown: 2.0.2
|
|
||||||
mime-types: 2.1.35
|
|
||||||
|
|
||||||
fs-constants@1.0.0: {}
|
fs-constants@1.0.0: {}
|
||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
@@ -5579,13 +5431,6 @@ snapshots:
|
|||||||
|
|
||||||
http-status-codes@2.3.0: {}
|
http-status-codes@2.3.0: {}
|
||||||
|
|
||||||
https-proxy-agent@7.0.6:
|
|
||||||
dependencies:
|
|
||||||
agent-base: 7.1.4
|
|
||||||
debug: 4.4.3
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
iconv-lite@0.7.0:
|
iconv-lite@0.7.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
@@ -5750,10 +5595,6 @@ snapshots:
|
|||||||
|
|
||||||
isexe@2.0.0: {}
|
isexe@2.0.0: {}
|
||||||
|
|
||||||
isomorphic-ws@5.0.0(ws@8.18.3):
|
|
||||||
dependencies:
|
|
||||||
ws: 8.18.3
|
|
||||||
|
|
||||||
iterator.prototype@1.1.5:
|
iterator.prototype@1.1.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
define-data-property: 1.1.4
|
define-data-property: 1.1.4
|
||||||
@@ -5903,12 +5744,6 @@ snapshots:
|
|||||||
braces: 3.0.3
|
braces: 3.0.3
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
||||||
mime-db@1.52.0: {}
|
|
||||||
|
|
||||||
mime-types@2.1.35:
|
|
||||||
dependencies:
|
|
||||||
mime-db: 1.52.0
|
|
||||||
|
|
||||||
mimic-response@3.1.0: {}
|
mimic-response@3.1.0: {}
|
||||||
|
|
||||||
minimatch@3.1.2:
|
minimatch@3.1.2:
|
||||||
@@ -6004,10 +5839,6 @@ snapshots:
|
|||||||
|
|
||||||
node-fetch-native@1.6.7: {}
|
node-fetch-native@1.6.7: {}
|
||||||
|
|
||||||
node-fetch@2.7.0:
|
|
||||||
dependencies:
|
|
||||||
whatwg-url: 5.0.0
|
|
||||||
|
|
||||||
node-mock-http@1.0.3: {}
|
node-mock-http@1.0.3: {}
|
||||||
|
|
||||||
node-releases@2.0.27: {}
|
node-releases@2.0.27: {}
|
||||||
@@ -6252,8 +6083,6 @@ snapshots:
|
|||||||
retry: 0.12.0
|
retry: 0.12.0
|
||||||
signal-exit: 3.0.7
|
signal-exit: 3.0.7
|
||||||
|
|
||||||
proxy-from-env@1.1.0: {}
|
|
||||||
|
|
||||||
pump@3.0.3:
|
pump@3.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
end-of-stream: 1.4.5
|
end-of-stream: 1.4.5
|
||||||
@@ -6608,8 +6437,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
|
|
||||||
tr46@0.0.3: {}
|
|
||||||
|
|
||||||
ts-api-utils@2.1.0(typescript@5.9.3):
|
ts-api-utils@2.1.0(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
@@ -6750,19 +6577,10 @@ snapshots:
|
|||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
uuid@11.1.0: {}
|
|
||||||
|
|
||||||
valibot@1.2.0(typescript@5.9.3):
|
valibot@1.2.0(typescript@5.9.3):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
webidl-conversions@3.0.1: {}
|
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
|
||||||
dependencies:
|
|
||||||
tr46: 0.0.3
|
|
||||||
webidl-conversions: 3.0.1
|
|
||||||
|
|
||||||
which-boxed-primitive@1.1.1:
|
which-boxed-primitive@1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-bigint: 1.1.0
|
is-bigint: 1.1.0
|
||||||
@@ -6812,14 +6630,10 @@ snapshots:
|
|||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
ws@8.18.3: {}
|
|
||||||
|
|
||||||
wsl-utils@0.1.0:
|
wsl-utils@0.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-wsl: 3.1.0
|
is-wsl: 3.1.0
|
||||||
|
|
||||||
xml-escape@1.1.0: {}
|
|
||||||
|
|
||||||
xtend@4.0.2: {}
|
xtend@4.0.2: {}
|
||||||
|
|
||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
||||||
import { getTTSAudioUrl } from "@/lib/browser/tts";
|
import { getTTSUrl, TTS_SUPPORTED_LANGUAGES } from "@/lib/server/bigmodel/tts";
|
||||||
import { VOICES } from "@/config/locales";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import localFont from "next/font/local";
|
import localFont from "next/font/local";
|
||||||
import { isNonNegativeInteger, SeededRandom } from "@/lib/utils";
|
import { isNonNegativeInteger, SeededRandom } from "@/lib/utils";
|
||||||
@@ -59,20 +58,32 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
|
|||||||
if (show === "answer") {
|
if (show === "answer") {
|
||||||
const newIndex = (index + 1) % getTextPairs().length;
|
const newIndex = (index + 1) % getTextPairs().length;
|
||||||
setIndex(newIndex);
|
setIndex(newIndex);
|
||||||
if (dictation)
|
if (dictation) {
|
||||||
getTTSAudioUrl(
|
const textPair = getTextPairs()[newIndex];
|
||||||
getTextPairs()[newIndex][reverse ? "text2" : "text1"],
|
const language = textPair[reverse ? "language2" : "language1"];
|
||||||
VOICES.find(
|
const text = textPair[reverse ? "text2" : "text1"];
|
||||||
(v) =>
|
|
||||||
v.locale ===
|
// 映射语言到 TTS 支持的格式
|
||||||
getTextPairs()[newIndex][
|
const languageMap: Record<string, TTS_SUPPORTED_LANGUAGES> = {
|
||||||
reverse ? "locale2" : "locale1"
|
"chinese": "Chinese",
|
||||||
],
|
"english": "English",
|
||||||
)!.short_name,
|
"japanese": "Japanese",
|
||||||
).then((url) => {
|
"korean": "Korean",
|
||||||
|
"french": "French",
|
||||||
|
"german": "German",
|
||||||
|
"italian": "Italian",
|
||||||
|
"portuguese": "Portuguese",
|
||||||
|
"spanish": "Spanish",
|
||||||
|
"russian": "Russian",
|
||||||
|
};
|
||||||
|
|
||||||
|
const ttsLanguage = languageMap[language?.toLowerCase()] || "Auto";
|
||||||
|
|
||||||
|
getTTSUrl(text, ttsLanguage).then((url) => {
|
||||||
load(url);
|
load(url);
|
||||||
play();
|
play();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setShow(show === "question" ? "answer" : "question");
|
setShow(show === "question" ? "answer" : "question");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { ChangeEvent, useEffect, useRef, useState } from "react";
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
import SaveList from "./SaveList";
|
import SaveList from "./SaveList";
|
||||||
|
|
||||||
import { VOICES } from "@/config/locales";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { getLocalStorageOperator } from "@/lib/browser/localStorageOperators";
|
import { getLocalStorageOperator } from "@/lib/browser/localStorageOperators";
|
||||||
import { genIPA, genLanguage } from "@/lib/server/bigmodel/translatorActions";
|
import { genIPA, genLanguage } from "@/lib/server/bigmodel/translatorActions";
|
||||||
@@ -160,7 +159,7 @@ export default function TextSpeakerPage() {
|
|||||||
const handleUseItem = (item: z.infer<typeof TextSpeakerItemSchema>) => {
|
const handleUseItem = (item: z.infer<typeof TextSpeakerItemSchema>) => {
|
||||||
if (textareaRef.current) textareaRef.current.value = item.text;
|
if (textareaRef.current) textareaRef.current.value = item.text;
|
||||||
textRef.current = item.text;
|
textRef.current = item.text;
|
||||||
setLanguage(item.locale);
|
setLanguage(item.language);
|
||||||
setIPA(item.ipa || "");
|
setIPA(item.ipa || "");
|
||||||
if (objurlRef.current) URL.revokeObjectURL(objurlRef.current);
|
if (objurlRef.current) URL.revokeObjectURL(objurlRef.current);
|
||||||
objurlRef.current = null;
|
objurlRef.current = null;
|
||||||
@@ -202,12 +201,12 @@ export default function TextSpeakerPage() {
|
|||||||
} else if (theIPA.length === 0) {
|
} else if (theIPA.length === 0) {
|
||||||
save.push({
|
save.push({
|
||||||
text: textRef.current,
|
text: textRef.current,
|
||||||
locale: theLanguage as string,
|
language: theLanguage as string,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
save.push({
|
save.push({
|
||||||
text: textRef.current,
|
text: textRef.current,
|
||||||
locale: theLanguage as string,
|
language: theLanguage as string,
|
||||||
ipa: theIPA,
|
ipa: theIPA,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ const AddToFolder: React.FC<AddToFolderProps> = ({ item, setShow }) => {
|
|||||||
createPair({
|
createPair({
|
||||||
text1: item.text1,
|
text1: item.text1,
|
||||||
text2: item.text2,
|
text2: item.text2,
|
||||||
locale1: item.locale1,
|
language1: item.language1,
|
||||||
locale2: item.locale2,
|
language2: item.language2,
|
||||||
folder: {
|
folder: {
|
||||||
connect: {
|
connect: {
|
||||||
id: folder.id,
|
id: folder.id,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import { LightButton } from "@/components/ui/buttons";
|
import { LightButton } from "@/components/ui/buttons";
|
||||||
import { IconClick } from "@/components/ui/buttons";
|
import { IconClick } from "@/components/ui/buttons";
|
||||||
import IMAGES from "@/config/images";
|
import IMAGES from "@/config/images";
|
||||||
import { VOICES } from "@/config/locales";
|
|
||||||
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
||||||
import { TranslationHistorySchema } from "@/lib/interfaces";
|
import { TranslationHistorySchema } from "@/lib/interfaces";
|
||||||
import { tlsoPush, tlso } from "@/lib/browser/localStorageOperators";
|
import { tlsoPush, tlso } from "@/lib/browser/localStorageOperators";
|
||||||
@@ -23,7 +22,7 @@ import FolderSelector from "./FolderSelector";
|
|||||||
import { createPair } from "@/lib/server/services/pairService";
|
import { createPair } from "@/lib/server/services/pairService";
|
||||||
import { shallowEqual } from "@/lib/utils";
|
import { shallowEqual } from "@/lib/utils";
|
||||||
import { authClient } from "@/lib/auth-client";
|
import { authClient } from "@/lib/auth-client";
|
||||||
import { getTTSUrl } from "@/lib/server/bigmodel/tts";
|
import { getTTSUrl, TTS_SUPPORTED_LANGUAGES } from "@/lib/server/bigmodel/tts";
|
||||||
|
|
||||||
export default function TranslatorPage() {
|
export default function TranslatorPage() {
|
||||||
const t = useTranslations("translator");
|
const t = useTranslations("translator");
|
||||||
@@ -51,7 +50,20 @@ export default function TranslatorPage() {
|
|||||||
const tts = async (text: string, locale: string) => {
|
const tts = async (text: string, locale: string) => {
|
||||||
if (lastTTS.current.text !== text) {
|
if (lastTTS.current.text !== text) {
|
||||||
try {
|
try {
|
||||||
const url = await getTTSUrl(text, locale);
|
// Map language name to TTS format
|
||||||
|
let theLanguage = locale.toLowerCase().replace(/[^a-z]/g, '').replace(/^./, match => match.toUpperCase());
|
||||||
|
|
||||||
|
// Check if language is in TTS supported list
|
||||||
|
const supportedLanguages: TTS_SUPPORTED_LANGUAGES[] = [
|
||||||
|
"Auto", "Chinese", "English", "German", "Italian", "Portuguese",
|
||||||
|
"Spanish", "Japanese", "Korean", "French", "Russian"
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!supportedLanguages.includes(theLanguage as TTS_SUPPORTED_LANGUAGES)) {
|
||||||
|
theLanguage = "Auto";
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = await getTTSUrl(text, theLanguage as TTS_SUPPORTED_LANGUAGES);
|
||||||
await load(url);
|
await load(url);
|
||||||
lastTTS.current.text = text;
|
lastTTS.current.text = text;
|
||||||
lastTTS.current.url = url;
|
lastTTS.current.url = url;
|
||||||
@@ -74,15 +86,15 @@ export default function TranslatorPage() {
|
|||||||
const llmres: {
|
const llmres: {
|
||||||
text1: string | null;
|
text1: string | null;
|
||||||
text2: string | null;
|
text2: string | null;
|
||||||
locale1: string | null;
|
language1: string | null;
|
||||||
locale2: string | null;
|
language2: string | null;
|
||||||
ipa1: string | null;
|
ipa1: string | null;
|
||||||
ipa2: string | null;
|
ipa2: string | null;
|
||||||
} = {
|
} = {
|
||||||
text1: text1,
|
text1: text1,
|
||||||
text2: null,
|
text2: null,
|
||||||
locale1: null,
|
language1: null,
|
||||||
locale2: null,
|
language2: null,
|
||||||
ipa1: null,
|
ipa1: null,
|
||||||
ipa2: null,
|
ipa2: null,
|
||||||
};
|
};
|
||||||
@@ -92,21 +104,21 @@ export default function TranslatorPage() {
|
|||||||
// 检查更新历史记录
|
// 检查更新历史记录
|
||||||
const checkUpdateLocalStorage = () => {
|
const checkUpdateLocalStorage = () => {
|
||||||
if (historyUpdated) return;
|
if (historyUpdated) return;
|
||||||
if (llmres.text1 && llmres.text2 && llmres.locale1 && llmres.locale2) {
|
if (llmres.text1 && llmres.text2 && llmres.language1 && llmres.language2) {
|
||||||
setHistory(
|
setHistory(
|
||||||
tlsoPush({
|
tlsoPush({
|
||||||
text1: llmres.text1,
|
text1: llmres.text1,
|
||||||
text2: llmres.text2,
|
text2: llmres.text2,
|
||||||
locale1: llmres.locale1,
|
language1: llmres.language1,
|
||||||
locale2: llmres.locale2,
|
language2: llmres.language2,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if (autoSave && autoSaveFolderId) {
|
if (autoSave && autoSaveFolderId) {
|
||||||
createPair({
|
createPair({
|
||||||
text1: llmres.text1,
|
text1: llmres.text1,
|
||||||
text2: llmres.text2,
|
text2: llmres.text2,
|
||||||
locale1: llmres.locale1,
|
language1: llmres.language1,
|
||||||
locale2: llmres.locale2,
|
language2: llmres.language2,
|
||||||
folder: {
|
folder: {
|
||||||
connect: {
|
connect: {
|
||||||
id: autoSaveFolderId,
|
id: autoSaveFolderId,
|
||||||
@@ -143,10 +155,10 @@ export default function TranslatorPage() {
|
|||||||
setTresult(text2);
|
setTresult(text2);
|
||||||
// 生成两个locale
|
// 生成两个locale
|
||||||
genLocale(text1).then((locale) => {
|
genLocale(text1).then((locale) => {
|
||||||
updateState("locale1", locale);
|
updateState("language1", locale);
|
||||||
});
|
});
|
||||||
genLocale(text2).then((locale) => {
|
genLocale(text2).then((locale) => {
|
||||||
updateState("locale2", locale);
|
updateState("language2", locale);
|
||||||
});
|
});
|
||||||
// 生成俩IPA
|
// 生成俩IPA
|
||||||
if (genIpa) {
|
if (genIpa) {
|
||||||
@@ -202,7 +214,7 @@ export default function TranslatorPage() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const t = taref.current?.value;
|
const t = taref.current?.value;
|
||||||
if (!t) return;
|
if (!t) return;
|
||||||
tts(t, tlso.get().find((v) => v.text1 === t)?.locale1 || "");
|
tts(t, tlso.get().find((v) => v.text1 === t)?.language1 || "");
|
||||||
}}
|
}}
|
||||||
></IconClick>
|
></IconClick>
|
||||||
</div>
|
</div>
|
||||||
@@ -240,7 +252,7 @@ export default function TranslatorPage() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
tts(
|
tts(
|
||||||
tresult,
|
tresult,
|
||||||
tlso.get().find((v) => v.text2 === tresult)?.locale2 || "",
|
tlso.get().find((v) => v.text2 === tresult)?.language2 || "",
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
></IconClick>
|
></IconClick>
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ interface AddTextPairModalProps {
|
|||||||
onAdd: (
|
onAdd: (
|
||||||
text1: string,
|
text1: string,
|
||||||
text2: string,
|
text2: string,
|
||||||
locale1: string,
|
language1: string,
|
||||||
locale2: string,
|
language2: string,
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ export default function AddTextPairModal({
|
|||||||
const t = useTranslations("folder_id");
|
const t = useTranslations("folder_id");
|
||||||
const input1Ref = useRef<HTMLInputElement>(null);
|
const input1Ref = useRef<HTMLInputElement>(null);
|
||||||
const input2Ref = useRef<HTMLInputElement>(null);
|
const input2Ref = useRef<HTMLInputElement>(null);
|
||||||
const [locale1, setLocale1] = useState("en-US");
|
const [language1, setLanguage1] = useState("english");
|
||||||
const [locale2, setLocale2] = useState("zh-CN");
|
const [language2, setLanguage2] = useState("chinese");
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
@@ -33,8 +33,8 @@ export default function AddTextPairModal({
|
|||||||
if (
|
if (
|
||||||
!input1Ref.current?.value ||
|
!input1Ref.current?.value ||
|
||||||
!input2Ref.current?.value ||
|
!input2Ref.current?.value ||
|
||||||
!locale1 ||
|
!language1 ||
|
||||||
!locale2
|
!language2
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -44,14 +44,14 @@ export default function AddTextPairModal({
|
|||||||
if (
|
if (
|
||||||
typeof text1 === "string" &&
|
typeof text1 === "string" &&
|
||||||
typeof text2 === "string" &&
|
typeof text2 === "string" &&
|
||||||
typeof locale1 === "string" &&
|
typeof language1 === "string" &&
|
||||||
typeof locale2 === "string" &&
|
typeof language2 === "string" &&
|
||||||
text1.trim() !== "" &&
|
text1.trim() !== "" &&
|
||||||
text2.trim() !== "" &&
|
text2.trim() !== "" &&
|
||||||
locale1.trim() !== "" &&
|
language1.trim() !== "" &&
|
||||||
locale2.trim() !== ""
|
language2.trim() !== ""
|
||||||
) {
|
) {
|
||||||
onAdd(text1, text2, locale1, locale2);
|
onAdd(text1, text2, language1, language2);
|
||||||
input1Ref.current.value = "";
|
input1Ref.current.value = "";
|
||||||
input2Ref.current.value = "";
|
input2Ref.current.value = "";
|
||||||
}
|
}
|
||||||
@@ -84,12 +84,12 @@ export default function AddTextPairModal({
|
|||||||
<Input ref={input2Ref} className="w-full"></Input>
|
<Input ref={input2Ref} className="w-full"></Input>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{t("locale1")}
|
{t("language1")}
|
||||||
<LocaleSelector value={locale1} onChange={setLocale1} />
|
<LocaleSelector value={language1} onChange={setLanguage1} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{t("locale2")}
|
{t("language2")}
|
||||||
<LocaleSelector value={locale2} onChange={setLocale2} />
|
<LocaleSelector value={language2} onChange={setLanguage2} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<LightButton onClick={handleAdd}>{t("add")}</LightButton>
|
<LightButton onClick={handleAdd}>{t("add")}</LightButton>
|
||||||
|
|||||||
@@ -140,14 +140,14 @@ export default function InFolder({ folderId }: { folderId: number }) {
|
|||||||
onAdd={async (
|
onAdd={async (
|
||||||
text1: string,
|
text1: string,
|
||||||
text2: string,
|
text2: string,
|
||||||
locale1: string,
|
language1: string,
|
||||||
locale2: string,
|
language2: string,
|
||||||
) => {
|
) => {
|
||||||
await createPair({
|
await createPair({
|
||||||
text1: text1,
|
text1: text1,
|
||||||
text2: text2,
|
text2: text2,
|
||||||
language1: locale1,
|
language1: language1,
|
||||||
language2: locale2,
|
language2: language2,
|
||||||
folder: {
|
folder: {
|
||||||
connect: {
|
connect: {
|
||||||
id: folderId,
|
id: folderId,
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ export default function UpdateTextPairModal({
|
|||||||
const t = useTranslations("folder_id");
|
const t = useTranslations("folder_id");
|
||||||
const input1Ref = useRef<HTMLInputElement>(null);
|
const input1Ref = useRef<HTMLInputElement>(null);
|
||||||
const input2Ref = useRef<HTMLInputElement>(null);
|
const input2Ref = useRef<HTMLInputElement>(null);
|
||||||
const [locale1, setLocale1] = useState(textPair.language1);
|
const [language1, setLanguage1] = useState(textPair.language1);
|
||||||
const [locale2, setLocale2] = useState(textPair.language2);
|
const [language2, setLanguage2] = useState(textPair.language2);
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ export default function UpdateTextPairModal({
|
|||||||
if (
|
if (
|
||||||
!input1Ref.current?.value ||
|
!input1Ref.current?.value ||
|
||||||
!input2Ref.current?.value ||
|
!input2Ref.current?.value ||
|
||||||
!locale1 ||
|
!language1 ||
|
||||||
!locale2
|
!language2
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -43,14 +43,14 @@ export default function UpdateTextPairModal({
|
|||||||
if (
|
if (
|
||||||
typeof text1 === "string" &&
|
typeof text1 === "string" &&
|
||||||
typeof text2 === "string" &&
|
typeof text2 === "string" &&
|
||||||
typeof locale1 === "string" &&
|
typeof language1 === "string" &&
|
||||||
typeof locale2 === "string" &&
|
typeof language2 === "string" &&
|
||||||
text1.trim() !== "" &&
|
text1.trim() !== "" &&
|
||||||
text2.trim() !== "" &&
|
text2.trim() !== "" &&
|
||||||
locale1.trim() !== "" &&
|
language1.trim() !== "" &&
|
||||||
locale2.trim() !== ""
|
language2.trim() !== ""
|
||||||
) {
|
) {
|
||||||
onUpdate(textPair.id, { text1, text2, locale1, locale2 });
|
onUpdate(textPair.id, { text1, text2, language1, language2 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@@ -88,12 +88,12 @@ export default function UpdateTextPairModal({
|
|||||||
></Input>
|
></Input>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{t("locale1")}
|
{t("language1")}
|
||||||
<LocaleSelector value={locale1} onChange={setLocale1} />
|
<LocaleSelector value={language1} onChange={setLanguage1} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{t("locale2")}
|
{t("language2")}
|
||||||
<LocaleSelector value={locale2} onChange={setLocale2} />
|
<LocaleSelector value={language2} onChange={setLanguage2} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<LightButton onClick={handleUpdate}>{t("update")}</LightButton>
|
<LightButton onClick={handleUpdate}>{t("update")}</LightButton>
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { LOCALES } from "@/config/locales";
|
import { useState } from "react";
|
||||||
|
|
||||||
const COMMON_LOCALES = [
|
const COMMON_LANGUAGES = [
|
||||||
{ label: "中文", value: "zh-CN" },
|
{ label: "中文", value: "chinese" },
|
||||||
{ label: "英文", value: "en-US" },
|
{ label: "英文", value: "english" },
|
||||||
{ label: "意大利语", value: "it-IT" },
|
{ label: "意大利语", value: "italian" },
|
||||||
{ label: "日语", value: "ja-JP" },
|
{ label: "日语", value: "japanese" },
|
||||||
|
{ label: "韩语", value: "korean" },
|
||||||
|
{ label: "法语", value: "french" },
|
||||||
|
{ label: "德语", value: "german" },
|
||||||
|
{ label: "西班牙语", value: "spanish" },
|
||||||
|
{ label: "葡萄牙语", value: "portuguese" },
|
||||||
|
{ label: "俄语", value: "russian" },
|
||||||
{ label: "其他", value: "other" },
|
{ label: "其他", value: "other" },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -14,34 +20,50 @@ interface LocaleSelectorProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function LocaleSelector({ value, onChange }: LocaleSelectorProps) {
|
export function LocaleSelector({ value, onChange }: LocaleSelectorProps) {
|
||||||
const isCommonLocale = COMMON_LOCALES.some((l) => l.value === value && l.value !== "other");
|
const [customInput, setCustomInput] = useState("");
|
||||||
const showFullList = value === "other" || !isCommonLocale;
|
const isCommonLanguage = COMMON_LANGUAGES.some((l) => l.value === value && l.value !== "other");
|
||||||
|
const showCustomInput = value === "other" || !isCommonLanguage;
|
||||||
|
|
||||||
|
// 计算输入框的值:如果是"other"使用自定义输入,否则使用外部传入的值
|
||||||
|
const inputValue = value === "other" ? customInput : value;
|
||||||
|
|
||||||
|
// 处理自定义输入
|
||||||
|
const handleCustomInputChange = (inputValue: string) => {
|
||||||
|
setCustomInput(inputValue);
|
||||||
|
onChange(inputValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 当选择常见语言或"其他"时
|
||||||
|
const handleSelectChange = (selectedValue: string) => {
|
||||||
|
if (selectedValue === "other") {
|
||||||
|
setCustomInput("");
|
||||||
|
onChange("other");
|
||||||
|
} else {
|
||||||
|
onChange(selectedValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<select
|
<select
|
||||||
value={isCommonLocale ? value : "other"}
|
value={isCommonLanguage ? value : "other"}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => handleSelectChange(e.target.value)}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-[#35786f]"
|
className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-[#35786f]"
|
||||||
>
|
>
|
||||||
{COMMON_LOCALES.map((locale) => (
|
{COMMON_LANGUAGES.map((lang) => (
|
||||||
<option key={locale.value} value={locale.value}>
|
<option key={lang.value} value={lang.value}>
|
||||||
{locale.label}
|
{lang.label}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{showFullList && (
|
{showCustomInput && (
|
||||||
<select
|
<input
|
||||||
value={value === "other" ? LOCALES[0] : value}
|
type="text"
|
||||||
onChange={(e) => onChange(e.target.value)}
|
value={inputValue}
|
||||||
|
onChange={(e) => handleCustomInputChange(e.target.value)}
|
||||||
|
placeholder="请输入语言名称"
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-[#35786f] mt-2"
|
className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-[#35786f] mt-2"
|
||||||
>
|
/>
|
||||||
{LOCALES.map((locale) => (
|
|
||||||
<option key={locale} value={locale}>
|
|
||||||
{locale}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -19,15 +19,15 @@ export type SupportedAlphabets =
|
|||||||
export const TextSpeakerItemSchema = z.object({
|
export const TextSpeakerItemSchema = z.object({
|
||||||
text: z.string(),
|
text: z.string(),
|
||||||
ipa: z.string().optional(),
|
ipa: z.string().optional(),
|
||||||
locale: z.string(),
|
language: z.string(),
|
||||||
});
|
});
|
||||||
export const TextSpeakerArraySchema = z.array(TextSpeakerItemSchema);
|
export const TextSpeakerArraySchema = z.array(TextSpeakerItemSchema);
|
||||||
|
|
||||||
export const WordDataSchema = z.object({
|
export const WordDataSchema = z.object({
|
||||||
locales: z
|
languages: z
|
||||||
.tuple([z.string(), z.string()])
|
.tuple([z.string(), z.string()])
|
||||||
.refine(([first, second]) => first !== second, {
|
.refine(([first, second]) => first !== second, {
|
||||||
message: "Locales must be different",
|
message: "Languages must be different",
|
||||||
}),
|
}),
|
||||||
wordPairs: z
|
wordPairs: z
|
||||||
.array(z.tuple([z.string(), z.string()]))
|
.array(z.tuple([z.string(), z.string()]))
|
||||||
@@ -47,8 +47,8 @@ export const WordDataSchema = z.object({
|
|||||||
export const TranslationHistorySchema = z.object({
|
export const TranslationHistorySchema = z.object({
|
||||||
text1: z.string(),
|
text1: z.string(),
|
||||||
text2: z.string(),
|
text2: z.string(),
|
||||||
locale1: z.string(),
|
language1: z.string(),
|
||||||
locale2: z.string(),
|
language2: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const TranslationHistoryArraySchema = z.array(TranslationHistorySchema);
|
export const TranslationHistoryArraySchema = z.array(TranslationHistorySchema);
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
|
||||||
// ==================== 类型定义 ====================
|
// ==================== 类型定义 ====================
|
||||||
/**
|
/**
|
||||||
* 支持的语音合成模型
|
* 支持的语音合成模型
|
||||||
@@ -135,13 +138,12 @@ class QwenTTSService {
|
|||||||
body: JSON.stringify(requestBody),
|
body: JSON.stringify(requestBody),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data: TTSResponse = await response.json();
|
|
||||||
|
|
||||||
// 4. 错误处理
|
// 4. 错误处理
|
||||||
if (data.status_code !== 200) {
|
if (response.status !== 200) {
|
||||||
throw new Error(`API错误: [${data.code}] ${data.message}`);
|
throw new Error(`TTS API错误: [${response.status}] ${response.statusText}}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data: TTSResponse = await response.json();
|
||||||
return data;
|
return data;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -149,74 +151,6 @@ class QwenTTSService {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 流式合成语音(边生成边输出Base64音频数据)
|
|
||||||
*/
|
|
||||||
async synthesizeStream(
|
|
||||||
text: string,
|
|
||||||
options: {
|
|
||||||
voice?: string;
|
|
||||||
language?: SupportedLanguage;
|
|
||||||
model?: TTSModel;
|
|
||||||
onAudioChunk?: (chunk: string) => void; // 接收音频片段的回调
|
|
||||||
} = {}
|
|
||||||
): Promise<void> {
|
|
||||||
const {
|
|
||||||
voice = 'Cherry',
|
|
||||||
language = 'Auto',
|
|
||||||
model = 'qwen3-tts-flash',
|
|
||||||
onAudioChunk
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
this.validateTextLength(text, model);
|
|
||||||
|
|
||||||
const requestBody: TTSRequest = {
|
|
||||||
model,
|
|
||||||
input: {
|
|
||||||
text,
|
|
||||||
voice,
|
|
||||||
language_type: language
|
|
||||||
},
|
|
||||||
parameters: {
|
|
||||||
stream: true // 启用流式输出
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(this.baseUrl, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${this.apiKey}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-DashScope-SSE': 'enable' // 关键:启用服务器发送事件
|
|
||||||
},
|
|
||||||
body: JSON.stringify(requestBody),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok || !response.body) {
|
|
||||||
throw new Error(`流式请求失败: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理流式响应(此处为简化示例,实际需解析SSE格式)
|
|
||||||
const reader = response.body.getReader();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = await reader.read();
|
|
||||||
if (done) break;
|
|
||||||
|
|
||||||
const chunk = decoder.decode(value);
|
|
||||||
if (onAudioChunk && chunk.trim()) {
|
|
||||||
onAudioChunk(chunk); // 处理音频数据片段
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('流式合成失败:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TTS_SUPPORTED_LANGUAGES = 'Auto' | 'Chinese' | 'English' | 'German' | 'Italian' | 'Portuguese' | 'Spanish' | 'Japanese' | 'Korean' | 'French' | 'Russian';
|
export type TTS_SUPPORTED_LANGUAGES = 'Auto' | 'Chinese' | 'English' | 'German' | 'Italian' | 'Portuguese' | 'Spanish' | 'Japanese' | 'Korean' | 'French' | 'Russian';
|
||||||
@@ -231,7 +165,7 @@ export async function getTTSUrl(text: string, lang: TTS_SUPPORTED_LANGUAGES) {
|
|||||||
throw "API Key设置错误";
|
throw "API Key设置错误";
|
||||||
}
|
}
|
||||||
const ttsService = new QwenTTSService(
|
const ttsService = new QwenTTSService(
|
||||||
process.env.DASHSCOPE_API_KEY || 'sk-xxx',
|
process.env.DASHSCORE_API_KEY
|
||||||
);
|
);
|
||||||
const result = await ttsService.synthesize(
|
const result = await ttsService.synthesize(
|
||||||
text,
|
text,
|
||||||
|
|||||||
Reference in New Issue
Block a user