diff --git a/locales/bg-BG/chat.json b/locales/bg-BG/chat.json index 8ce531d6..edd0062e 100644 --- a/locales/bg-BG/chat.json +++ b/locales/bg-BG/chat.json @@ -29,6 +29,7 @@ "totalCount": "Общо {{total}} елемента" }, "chat": "Чат", + "chatList": "Списък с чатове", "danceList": "Списък с танци", "danceMarket": "Пазар на танци", "delSession": "Изтриване на сесия", @@ -55,6 +56,7 @@ }, "interactive": "Интерактивен", "noDanceList": "Няма наличен списък за възпроизвеждане, можете да се абонирате за любимите си танци на пазара", + "noRoleList": "Няма наличен списък с роли", "selectModel": "Моля, изберете модел", "sessionCreate": "Създаване на чат", "sessionList": "Списък с сесии", diff --git a/locales/bg-BG/common.json b/locales/bg-BG/common.json index 41fa3796..3490f690 100644 --- a/locales/bg-BG/common.json +++ b/locales/bg-BG/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Чат", - "market": "Открийте", + "discover": "Открий", "role": "Роля", "settings": "Настройки", "tips": "Проектът в момента е в строеж и не гарантира стабилността на данните. Ако срещнете проблеми, можете да изчистите съобщенията на сесията и да нулирате системните настройки в системните настройки. Извиняваме се за причиненото неудобство." diff --git a/locales/bg-BG/error.json b/locales/bg-BG/error.json index 535c5d03..e06bf617 100644 --- a/locales/bg-BG/error.json +++ b/locales/bg-BG/error.json @@ -18,5 +18,6 @@ "s3envError": "S3 променливата на средата не е напълно настроена, моля, проверете вашите променливи на средата", "serverError": "Сървърна грешка, моля свържете се с администратора", "triggerError": "Възникна грешка", + "ttsTransformFailed": "Неуспешно преобразуване на реч, моля, проверете мрежата или опитайте отново след като активирате клиентското извикване в настройките.", "unknownError": "Неизвестна грешка" } diff --git a/locales/bg-BG/role.json b/locales/bg-BG/role.json index 0207000f..d962c069 100644 --- a/locales/bg-BG/role.json +++ b/locales/bg-BG/role.json @@ -6,6 +6,7 @@ "other": "Други" }, "category": { + "all": "Всички", "animal": "Животно", "anime": "Аниме", "book": "Книга", @@ -78,6 +79,8 @@ }, "noRole": "Няма налични роли. Можете да създадете персонализирана роля чрез + или да добавите роли от страницата за открития.", "role": { + "create": "Създаване на роля", + "createRoleFailed": "Създаването на ролята не успя", "greetTip": "Моля, въведете поздравителната фраза за ролята", "inputRoleSetting": "Моля, въведете системните настройки на ролята", "roleDescriptionTip": "Моля, въведете описание на ролята", @@ -85,6 +88,7 @@ "roleReadmeTip": "Моля, въведете описание на ролята", "roleSettingDescription": "Фоновата настройка на ролята, която ще бъде изпратена на модела по време на чат с ролята", "roleSettingLabel": "Системни настройки на ролята", + "selectGender": "Изберете пол на ролята", "uploadSize": "Поддържа качване на един файл, препоръчителният размер е кратно на {{width}} * {{height}}" }, "roleBook": "Ролевата книга", diff --git a/locales/bg-BG/settings.json b/locales/bg-BG/settings.json index a12506e5..3542fc66 100644 --- a/locales/bg-BG/settings.json +++ b/locales/bg-BG/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Настройки на допир" + }, + "tts": { + "clientCall": { + "desc": "След активиране, ще се използва извикване от клиента за услугата за синтез на глас, което ще ускори скоростта на синтез, но изисква достъп до интернет или способност за достъп до външни мрежи.", + "title": "Извикване от клиента" + }, + "title": "Настройки на гласа" } } diff --git a/locales/de-DE/chat.json b/locales/de-DE/chat.json index 6f721e49..d821733e 100644 --- a/locales/de-DE/chat.json +++ b/locales/de-DE/chat.json @@ -29,6 +29,7 @@ "totalCount": "Insgesamt {{total}} Elemente" }, "chat": "Chat", + "chatList": "Chatliste", "danceList": "Tanzliste", "danceMarket": "Tanzmarkt", "delSession": "Sitzung löschen", @@ -55,6 +56,7 @@ }, "interactive": "interaktiv", "noDanceList": "Derzeit keine Wiedergabeliste verfügbar. Sie können im Markt die Tänze abonnieren, die Ihnen gefallen.", + "noRoleList": "Keine Rollenliste vorhanden", "selectModel": "Bitte wählen Sie ein Modell aus", "sessionCreate": "Chat erstellen", "sessionList": "Sitzungsliste", diff --git a/locales/de-DE/common.json b/locales/de-DE/common.json index 6595957f..1328af1d 100644 --- a/locales/de-DE/common.json +++ b/locales/de-DE/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Chat", - "market": "Entdecken", + "discover": "Entdecken", "role": "Rolle", "settings": "Einstellungen", "tips": "Das Projekt befindet sich derzeit in der Bauphase, daher kann die Datenstabilität nicht garantiert werden. Bei Problemen können Sie in den Systemeinstellungen die Sitzungsnachrichten löschen und die Systemeinstellungen zurücksetzen. Wir bitten um Ihr Verständnis für eventuelle Unannehmlichkeiten." diff --git a/locales/de-DE/error.json b/locales/de-DE/error.json index cf9f3f42..1e660510 100644 --- a/locales/de-DE/error.json +++ b/locales/de-DE/error.json @@ -18,5 +18,6 @@ "s3envError": "Die S3-Umgebungsvariablen sind nicht vollständig gesetzt. Bitte überprüfen Sie Ihre Umgebungsvariablen.", "serverError": "Serverfehler, bitte kontaktieren Sie den Administrator", "triggerError": "Fehler auslösen", + "ttsTransformFailed": "Die Sprachumwandlung ist fehlgeschlagen. Bitte überprüfen Sie Ihre Internetverbindung oder versuchen Sie es nach dem Aktivieren der Clientanrufe in den Einstellungen erneut.", "unknownError": "Unbekannter Fehler" } diff --git a/locales/de-DE/role.json b/locales/de-DE/role.json index 5f45ced0..b3e9b591 100644 --- a/locales/de-DE/role.json +++ b/locales/de-DE/role.json @@ -6,6 +6,7 @@ "other": "Andere" }, "category": { + "all": "Alle", "animal": "Tiere", "anime": "Anime", "book": "Bücher", @@ -78,6 +79,8 @@ }, "noRole": "Keine Rolle vorhanden. Sie können eine benutzerdefinierte Rolle durch Klicken auf + erstellen oder Rollen über die Entdeckungsseite hinzufügen.", "role": { + "create": "Rolle erstellen", + "createRoleFailed": "Rolle konnte nicht erstellt werden", "greetTip": "Bitte geben Sie die Begrüßungsformel für den Charakter ein", "inputRoleSetting": "Bitte geben Sie die Systemeinstellungen des Charakters ein", "roleDescriptionTip": "Bitte geben Sie die Charakterbeschreibung ein", @@ -85,6 +88,7 @@ "roleReadmeTip": "Bitte geben Sie die Beschreibung des Charakters ein", "roleSettingDescription": "Hintergrundinformationen des Charakters, die beim Chatten mit dem Charakter an das Modell gesendet werden", "roleSettingLabel": "Systemeinstellungen des Charakters", + "selectGender": "Geschlecht der Rolle auswählen", "uploadSize": "Unterstützt den Upload von Einzeldateien, empfohlen wird eine Größe, die ein Vielfaches von {{width}} * {{height}} ist" }, "roleBook": "Charakterbuch", diff --git a/locales/de-DE/settings.json b/locales/de-DE/settings.json index 3c4bbb85..960f5c0f 100644 --- a/locales/de-DE/settings.json +++ b/locales/de-DE/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Berührungseinstellungen" + }, + "tts": { + "clientCall": { + "desc": "Wenn aktiviert, wird der Client den Sprachsynthesedienst aufrufen, was die Sprachsynthese schneller macht, jedoch eine wissenschaftliche Internetverbindung oder die Fähigkeit erfordert, auf das externe Internet zuzugreifen.", + "title": "Client Aufruf" + }, + "title": "Sprach Einstellungen" } } diff --git a/locales/en-US/chat.json b/locales/en-US/chat.json index b5424d52..e69c04dd 100644 --- a/locales/en-US/chat.json +++ b/locales/en-US/chat.json @@ -29,6 +29,7 @@ "totalCount": "Total {{total}} items" }, "chat": "Chat", + "chatList": "Chat List", "danceList": "Dance List", "danceMarket": "Dance Market", "delSession": "Delete Session", @@ -55,6 +56,7 @@ }, "interactive": "Interactive", "noDanceList": "No playlist available. You can subscribe to your favorite dances through the market.", + "noRoleList": "No Role List Available", "selectModel": "Please select a model", "sessionCreate": "Create Chat", "sessionList": "Session List", diff --git a/locales/en-US/common.json b/locales/en-US/common.json index 054a7ebf..744cec5a 100644 --- a/locales/en-US/common.json +++ b/locales/en-US/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Chat", - "market": "Discover", + "discover": "Discover", "role": "Role", "settings": "Settings", "tips": "The project is currently under construction, and data stability is not guaranteed. If you encounter any issues, you can clear session messages and reset system settings in the system settings. We apologize for any inconvenience this may cause." diff --git a/locales/en-US/error.json b/locales/en-US/error.json index 8b66e939..d93e1448 100644 --- a/locales/en-US/error.json +++ b/locales/en-US/error.json @@ -18,5 +18,6 @@ "s3envError": "The S3 environment variables are not fully set. Please check your environment variables.", "serverError": "Server error, please contact the administrator.", "triggerError": "Trigger Error", + "ttsTransformFailed": "Voice conversion failed. Please check your network or enable client invocation in the settings and try again.", "unknownError": "Unknown error" } diff --git a/locales/en-US/role.json b/locales/en-US/role.json index b3d98a76..ebcebe6c 100644 --- a/locales/en-US/role.json +++ b/locales/en-US/role.json @@ -6,6 +6,7 @@ "other": "Other" }, "category": { + "all": "All", "animal": "Animals", "anime": "Anime", "book": "Books", @@ -78,6 +79,8 @@ }, "noRole": "No roles available. You can create a custom role by clicking +, or add roles through the discovery page.", "role": { + "create": "Create Role", + "createRoleFailed": "Role Creation Failed", "greetTip": "Please enter the greeting phrase for the character", "inputRoleSetting": "Please enter the system settings for the character", "roleDescriptionTip": "Please enter the character's description", @@ -85,6 +88,7 @@ "roleReadmeTip": "Please enter the character's description", "roleSettingDescription": "The background settings for the character, which will be sent to the model during chats with the character", "roleSettingLabel": "System Role Settings", + "selectGender": "Select Character Gender", "uploadSize": "Supports single file uploads, recommended dimensions are multiples of {{width}} * {{height}}" }, "roleBook": "Character Book", diff --git a/locales/en-US/settings.json b/locales/en-US/settings.json index b6f29c47..88fdca94 100644 --- a/locales/en-US/settings.json +++ b/locales/en-US/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Touch Settings" + }, + "tts": { + "clientCall": { + "desc": "When enabled, the voice synthesis service will be called from the client, resulting in faster voice synthesis, but it requires a VPN or the ability to access the internet.", + "title": "Client Call" + }, + "title": "Voice Settings" } } diff --git a/locales/es-ES/chat.json b/locales/es-ES/chat.json index cfdcd112..1183fbf5 100644 --- a/locales/es-ES/chat.json +++ b/locales/es-ES/chat.json @@ -29,6 +29,7 @@ "totalCount": "Total {{total}} elementos" }, "chat": "charlar", + "chatList": "Lista de chat", "danceList": "Lista de danzas", "danceMarket": "Mercado de danzas", "delSession": "Eliminar sesión", @@ -55,6 +56,7 @@ }, "interactive": "Interactivo", "noDanceList": "No hay listas de reproducción disponibles. Puedes suscribirte a tus danzas favoritas a través del mercado.", + "noRoleList": "No hay lista de roles disponible", "selectModel": "Por favor, seleccione un modelo", "sessionCreate": "Crear chat", "sessionList": "Lista de sesiones", diff --git a/locales/es-ES/common.json b/locales/es-ES/common.json index c7af26f6..adbf25ca 100644 --- a/locales/es-ES/common.json +++ b/locales/es-ES/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Chat", - "market": "Descubrir", + "discover": "Descubrir", "role": "Rol", "settings": "Configuración", "tips": "El proyecto está actualmente en construcción, no se garantiza la estabilidad de los datos. Si encuentra problemas, puede borrar los mensajes de sesión y restablecer la configuración del sistema en la configuración del sistema. Disculpe las molestias." diff --git a/locales/es-ES/error.json b/locales/es-ES/error.json index a9f85cb7..9c7c7a11 100644 --- a/locales/es-ES/error.json +++ b/locales/es-ES/error.json @@ -18,5 +18,6 @@ "s3envError": "Las variables de entorno de S3 no están completamente configuradas, por favor revise sus variables de entorno", "serverError": "Error del servidor, por favor contacte al administrador", "triggerError": "Error desencadenado", + "ttsTransformFailed": "La conversión de voz ha fallado, por favor verifica la conexión a internet o intenta habilitar la llamada del cliente en la configuración.", "unknownError": "Error desconocido" } diff --git a/locales/es-ES/role.json b/locales/es-ES/role.json index b557e39e..9a5f5e2a 100644 --- a/locales/es-ES/role.json +++ b/locales/es-ES/role.json @@ -6,6 +6,7 @@ "other": "Otro" }, "category": { + "all": "Todos", "animal": "Animales", "anime": "Anime", "book": "Libros", @@ -78,6 +79,8 @@ }, "noRole": "No hay roles disponibles. Puedes crear un rol personalizado haciendo clic en +, o agregar roles a través de la página de descubrimiento.", "role": { + "create": "Crear rol", + "createRoleFailed": "Error al crear el rol", "greetTip": "Introduce la frase de saludo que usarás con el personaje", "inputRoleSetting": "Introduce la configuración del sistema del personaje", "roleDescriptionTip": "Introduce la descripción del personaje", @@ -85,6 +88,7 @@ "roleReadmeTip": "Introduce la descripción del personaje", "roleSettingDescription": "La configuración de fondo del personaje, que se enviará al modelo durante la conversación con el personaje", "roleSettingLabel": "Configuración del rol del sistema", + "selectGender": "Seleccionar género del rol", "uploadSize": "Soporta la carga de un solo archivo, se recomienda que las dimensiones sean múltiplos de {{width}} * {{height}}" }, "roleBook": "Libro de Roles", diff --git a/locales/es-ES/settings.json b/locales/es-ES/settings.json index 8d071324..4c99489c 100644 --- a/locales/es-ES/settings.json +++ b/locales/es-ES/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Configuración de toque" + }, + "tts": { + "clientCall": { + "desc": "Al habilitar esta opción, se utilizará el servicio de síntesis de voz mediante llamada del cliente, lo que permitirá una velocidad de síntesis de voz más rápida, pero requerirá acceso a internet o la capacidad de acceder a redes externas.", + "title": "Llamada del cliente" + }, + "title": "Configuración de voz" } } diff --git a/locales/fr-FR/chat.json b/locales/fr-FR/chat.json index 9d6d2bdc..2f7b3256 100644 --- a/locales/fr-FR/chat.json +++ b/locales/fr-FR/chat.json @@ -29,6 +29,7 @@ "totalCount": "Total de {{total}} éléments" }, "chat": "Discussion", + "chatList": "Liste de discussion", "danceList": "Liste des danses", "danceMarket": "Marché de la danse", "delSession": "Supprimer la session", @@ -55,6 +56,7 @@ }, "interactive": "Interactif", "noDanceList": "Aucune liste de lecture disponible pour le moment, vous pouvez vous abonner à vos danses préférées sur le marché", + "noRoleList": "Aucune liste de rôles disponible", "selectModel": "Veuillez sélectionner un modèle", "sessionCreate": "Créer une conversation", "sessionList": "Liste des sessions", diff --git a/locales/fr-FR/common.json b/locales/fr-FR/common.json index 6b93f927..cab1a222 100644 --- a/locales/fr-FR/common.json +++ b/locales/fr-FR/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Discuter", - "market": "Découvrir", + "discover": "Découvrir", "role": "Rôle", "settings": "Paramètres", "tips": "Le projet est actuellement en cours de construction, ce qui ne garantit pas la stabilité des données. Si vous rencontrez des problèmes, vous pouvez effacer les messages de session et réinitialiser les paramètres du système dans les paramètres. Nous vous prions de bien vouloir nous excuser pour les désagréments causés." diff --git a/locales/fr-FR/error.json b/locales/fr-FR/error.json index b2f221e1..55415479 100644 --- a/locales/fr-FR/error.json +++ b/locales/fr-FR/error.json @@ -18,5 +18,6 @@ "s3envError": "Les variables d'environnement S3 ne sont pas complètement configurées, veuillez vérifier vos variables d'environnement.", "serverError": "Erreur du serveur, veuillez contacter l'administrateur", "triggerError": "Erreur déclenchée", + "ttsTransformFailed": "Échec de la conversion vocale, veuillez vérifier votre connexion réseau ou essayer d'activer l'appel client dans les paramètres.", "unknownError": "Erreur inconnue" } diff --git a/locales/fr-FR/role.json b/locales/fr-FR/role.json index d50e2602..6147df29 100644 --- a/locales/fr-FR/role.json +++ b/locales/fr-FR/role.json @@ -6,6 +6,7 @@ "other": "Autre" }, "category": { + "all": "Tous", "animal": "Animal", "anime": "Anime", "book": "Livre", @@ -78,6 +79,8 @@ }, "noRole": "Aucun rôle disponible. Vous pouvez créer un rôle personnalisé en cliquant sur + ou ajouter des rôles via la page de découverte.", "role": { + "create": "Créer un rôle", + "createRoleFailed": "Échec de la création du rôle", "greetTip": "Veuillez entrer une salutation pour le personnage", "inputRoleSetting": "Veuillez entrer les paramètres système du personnage", "roleDescriptionTip": "Veuillez entrer la description du personnage", @@ -85,6 +88,7 @@ "roleReadmeTip": "Veuillez entrer la description du personnage", "roleSettingDescription": "Contexte du personnage, qui sera envoyé au modèle lors de la conversation avec le personnage", "roleSettingLabel": "Paramètres système du personnage", + "selectGender": "Sélectionner le sexe du rôle", "uploadSize": "Prise en charge du téléchargement de fichiers uniques, taille recommandée étant un multiple de {{width}} * {{height}}" }, "roleBook": "Livre de rôle", diff --git a/locales/fr-FR/settings.json b/locales/fr-FR/settings.json index 89cb88f2..ef83b64c 100644 --- a/locales/fr-FR/settings.json +++ b/locales/fr-FR/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Paramètres de toucher" + }, + "tts": { + "clientCall": { + "desc": "Une fois activé, le service de synthèse vocale sera appelé par le client, ce qui permet une vitesse de synthèse vocale plus rapide, mais nécessite un accès à Internet ou la capacité d'accéder à des réseaux externes.", + "title": "Appel client" + }, + "title": "Paramètres de synthèse vocale" } } diff --git a/locales/it-IT/chat.json b/locales/it-IT/chat.json index a27f3947..a48899bb 100644 --- a/locales/it-IT/chat.json +++ b/locales/it-IT/chat.json @@ -29,6 +29,7 @@ "totalCount": "Totale {{total}} elementi" }, "chat": "Chat", + "chatList": "Elenco chat", "danceList": "Elenco delle danze", "danceMarket": "Mercato delle danze", "delSession": "Elimina la sessione", @@ -55,6 +56,7 @@ }, "interactive": "Interattivo", "noDanceList": "Nessuna playlist disponibile, puoi iscriverti alle danze che ti piacciono tramite il mercato", + "noRoleList": "Nessun elenco di ruoli disponibile", "selectModel": "Seleziona un modello", "sessionCreate": "Crea chat", "sessionList": "Elenco delle sessioni", diff --git a/locales/it-IT/common.json b/locales/it-IT/common.json index a5fea24f..43834de3 100644 --- a/locales/it-IT/common.json +++ b/locales/it-IT/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Chat", - "market": "Scopri", + "discover": "Scopri", "role": "Ruolo", "settings": "Impostazioni", "tips": "Il progetto è attualmente in fase di costruzione, non garantiamo la stabilità dei dati. Se riscontri problemi, puoi cancellare i messaggi di sessione e ripristinare le impostazioni di sistema nelle impostazioni. Ci scusiamo per l'inconveniente." diff --git a/locales/it-IT/error.json b/locales/it-IT/error.json index 62a5a584..0b890714 100644 --- a/locales/it-IT/error.json +++ b/locales/it-IT/error.json @@ -18,5 +18,6 @@ "s3envError": "Le variabili d'ambiente S3 non sono completamente impostate, si prega di controllare le proprie variabili d'ambiente", "serverError": "Erro del server, contattare l'amministratore", "triggerError": "Errore di attivazione", + "ttsTransformFailed": "La conversione vocale è fallita, controlla la connessione di rete o prova ad attivare l'app nelle impostazioni.", "unknownError": "Erro sconosciuto" } diff --git a/locales/it-IT/role.json b/locales/it-IT/role.json index 3b86b468..8cdfd29a 100644 --- a/locales/it-IT/role.json +++ b/locales/it-IT/role.json @@ -6,6 +6,7 @@ "other": "Altro" }, "category": { + "all": "Tutti", "animal": "Animale", "anime": "Anime", "book": "Libri", @@ -78,6 +79,8 @@ }, "noRole": "Nessun ruolo disponibile. Puoi creare un ruolo personalizzato cliccando su + oppure aggiungere ruoli dalla pagina di scoperta.", "role": { + "create": "Crea ruolo", + "createRoleFailed": "Creazione del ruolo fallita", "greetTip": "Inserisci il saluto che utilizzerai per interagire con il ruolo", "inputRoleSetting": "Inserisci le impostazioni del sistema per il ruolo", "roleDescriptionTip": "Inserisci la descrizione del ruolo", @@ -85,6 +88,7 @@ "roleReadmeTip": "Inserisci la descrizione del ruolo", "roleSettingDescription": "Impostazione del background del ruolo, verrà inviata al modello durante la chat con il ruolo", "roleSettingLabel": "Impostazioni del ruolo di sistema", + "selectGender": "Seleziona genere del ruolo", "uploadSize": "Supporta il caricamento di un singolo file, si consiglia una dimensione che sia un multiplo di {{width}} * {{height}}" }, "roleBook": "Libro dei Ruoli", diff --git a/locales/it-IT/settings.json b/locales/it-IT/settings.json index c8b93e88..4882f2dd 100644 --- a/locales/it-IT/settings.json +++ b/locales/it-IT/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Impostazioni di tocco" + }, + "tts": { + "clientCall": { + "desc": "Se abilitato, verrà utilizzato il servizio di sintesi vocale tramite chiamata client, la sintesi vocale sarà più veloce, ma è necessario avere accesso a Internet o la capacità di accedere a reti esterne.", + "title": "Chiamata client" + }, + "title": "Impostazioni vocali" } } diff --git a/locales/ja-JP/chat.json b/locales/ja-JP/chat.json index b2bb5ef2..eb39a007 100644 --- a/locales/ja-JP/chat.json +++ b/locales/ja-JP/chat.json @@ -29,6 +29,7 @@ "totalCount": "合計 {{total}} 件" }, "chat": "チャット", + "chatList": "チャットリスト", "danceList": "ダンスリスト", "danceMarket": "ダンスマーケット", "delSession": "セッションを削除", @@ -55,6 +56,7 @@ }, "interactive": "インタラクティブ", "noDanceList": "現在再生リストはありません。お気に入りのダンスをマーケットで購読できます。", + "noRoleList": "ロールリストはありません", "selectModel": "モデルを選択してください", "sessionCreate": "チャットを作成", "sessionList": "セッションリスト", diff --git a/locales/ja-JP/common.json b/locales/ja-JP/common.json index f0402140..e011f512 100644 --- a/locales/ja-JP/common.json +++ b/locales/ja-JP/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "チャット", - "market": "発見", + "discover": "発見", "role": "役割", "settings": "設定", "tips": "プロジェクトは現在施工中であり、データの安定性は保証されていません。問題が発生した場合は、システム設定でセッションメッセージをクリアし、システム設定をリセットしてください。ご不便をおかけしますが、ご了承ください。" diff --git a/locales/ja-JP/error.json b/locales/ja-JP/error.json index e6c55df5..201cc21b 100644 --- a/locales/ja-JP/error.json +++ b/locales/ja-JP/error.json @@ -18,5 +18,6 @@ "s3envError": "S3 環境変数が完全に設定されていません。環境変数を確認してください。", "serverError": "サーバーエラーが発生しました。管理者にお問い合わせください", "triggerError": "エラーをトリガーする", + "ttsTransformFailed": "音声変換に失敗しました。ネットワークを確認するか、設定でクライアント呼び出しを有効にして再試行してください。", "unknownError": "不明なエラー" } diff --git a/locales/ja-JP/role.json b/locales/ja-JP/role.json index 836d44ef..bf1125a2 100644 --- a/locales/ja-JP/role.json +++ b/locales/ja-JP/role.json @@ -6,6 +6,7 @@ "other": "その他" }, "category": { + "all": "すべて", "animal": "動物", "anime": "アニメ", "book": "書籍", @@ -78,6 +79,8 @@ }, "noRole": "ロールがありません。+ をクリックしてカスタムロールを作成するか、発見ページからロールを追加できます。", "role": { + "create": "ロールを作成", + "createRoleFailed": "ロールの作成に失敗しました", "greetTip": "キャラクターに挨拶する際の言葉を入力してください。", "inputRoleSetting": "キャラクターのシステム設定を入力してください。", "roleDescriptionTip": "キャラクターの詳細を入力してください。", @@ -85,6 +88,7 @@ "roleReadmeTip": "キャラクターの説明を入力してください。", "roleSettingDescription": "キャラクターの背景設定で、キャラクターとのチャット時にモデルに送信されます。", "roleSettingLabel": "システムキャラクター設定", + "selectGender": "ロールの性別を選択", "uploadSize": "単一ファイルのアップロードをサポートしています。推奨サイズは {{width}} * {{height}} の倍数です。" }, "roleBook": "キャラクターシート", diff --git a/locales/ja-JP/settings.json b/locales/ja-JP/settings.json index 1393fbe3..bf986a9e 100644 --- a/locales/ja-JP/settings.json +++ b/locales/ja-JP/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "タッチ設定" + }, + "tts": { + "clientCall": { + "desc": "有効にすると、クライアントが音声合成サービスを呼び出し、音声合成の速度が向上しますが、インターネットにアクセスする能力が必要です。", + "title": "クライアント呼び出し" + }, + "title": "音声設定" } } diff --git a/locales/ko-KR/chat.json b/locales/ko-KR/chat.json index 74266c0b..f7d0e1a3 100644 --- a/locales/ko-KR/chat.json +++ b/locales/ko-KR/chat.json @@ -29,6 +29,7 @@ "totalCount": "총 {{total}} 항목" }, "chat": "채팅", + "chatList": "채팅 목록", "danceList": "댄스 목록", "danceMarket": "댄스 마켓", "delSession": "세션 삭제", @@ -55,6 +56,7 @@ }, "interactive": "상호작용 가능", "noDanceList": "재생 목록이 없습니다. 좋아하는 댄스를 마켓에서 구독할 수 있습니다.", + "noRoleList": "역할 목록이 없습니다", "selectModel": "모델을 선택하세요", "sessionCreate": "채팅 생성", "sessionList": "세션 목록", diff --git a/locales/ko-KR/common.json b/locales/ko-KR/common.json index 5c0e1ee7..d0c347c5 100644 --- a/locales/ko-KR/common.json +++ b/locales/ko-KR/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "채팅", - "market": "발견", + "discover": "발견", "role": "역할", "settings": "설정", "tips": "현재 프로젝트가 진행 중이며 데이터의 안정성을 보장할 수 없습니다. 문제가 발생할 경우 시스템 설정에서 세션 메시지를 지우고 시스템 설정을 초기화할 수 있습니다. 불편을 드려 죄송합니다." diff --git a/locales/ko-KR/error.json b/locales/ko-KR/error.json index ca6c745f..46d99879 100644 --- a/locales/ko-KR/error.json +++ b/locales/ko-KR/error.json @@ -18,5 +18,6 @@ "s3envError": "S3 환경 변수가 완전히 설정되지 않았습니다. 환경 변수를 확인해 주세요.", "serverError": "서버 오류가 발생했습니다. 관리자에게 문의하세요.", "triggerError": "오류 발생", + "ttsTransformFailed": "음성 변환에 실패했습니다. 네트워크를 확인하거나 설정에서 클라이언트 호출을 활성화한 후 다시 시도해 주세요.", "unknownError": "알 수 없는 오류" } diff --git a/locales/ko-KR/role.json b/locales/ko-KR/role.json index 5b2f5389..314ec0c4 100644 --- a/locales/ko-KR/role.json +++ b/locales/ko-KR/role.json @@ -6,6 +6,7 @@ "other": "기타" }, "category": { + "all": "모두", "animal": "동물", "anime": "애니메이션", "book": "책", @@ -78,6 +79,8 @@ }, "noRole": "역할이 없습니다. +를 클릭하여 사용자 정의 역할을 생성하거나 발견 페이지에서 역할을 추가할 수 있습니다.", "role": { + "create": "역할 생성", + "createRoleFailed": "역할 생성 실패", "greetTip": "캐릭터와 인사할 때 사용할 문구를 입력하세요.", "inputRoleSetting": "캐릭터의 시스템 설정을 입력하세요.", "roleDescriptionTip": "캐릭터의 설명을 입력하세요.", @@ -85,6 +88,7 @@ "roleReadmeTip": "캐릭터 설명을 입력하세요.", "roleSettingDescription": "캐릭터의 배경 설정으로, 캐릭터와 대화할 때 모델에 전송됩니다.", "roleSettingLabel": "시스템 캐릭터 설정", + "selectGender": "역할 성별 선택", "uploadSize": "단일 파일 업로드를 지원하며, 추천 크기는 {{width}} * {{height}}의 배수입니다." }, "roleBook": "역할 책", diff --git a/locales/ko-KR/settings.json b/locales/ko-KR/settings.json index eaf77cd8..15027fac 100644 --- a/locales/ko-KR/settings.json +++ b/locales/ko-KR/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "터치 설정" + }, + "tts": { + "clientCall": { + "desc": "활성화하면 클라이언트가 음성 합성 서비스를 호출하여 음성 합성 속도가 더 빨라지지만, 과학적인 인터넷 사용 또는 외부 네트워크 접근 능력이 필요합니다.", + "title": "클라이언트 호출" + }, + "title": "음성 설정" } } diff --git a/locales/nl-NL/chat.json b/locales/nl-NL/chat.json index 658595a6..c774d734 100644 --- a/locales/nl-NL/chat.json +++ b/locales/nl-NL/chat.json @@ -29,6 +29,7 @@ "totalCount": "Totaal {{total}} items" }, "chat": "Chatten", + "chatList": "Chatlijst", "danceList": "Danslijst", "danceMarket": "Dansmarkt", "delSession": "Verwijder sessie", @@ -55,6 +56,7 @@ }, "interactive": "Interactief", "noDanceList": "Geen afspeellijst beschikbaar. U kunt zich abonneren op uw favoriete dansen via de markt.", + "noRoleList": "Geen rol lijst beschikbaar", "selectModel": "Selecteer een model", "sessionCreate": "Chat aanmaken", "sessionList": "Sessielijst", diff --git a/locales/nl-NL/common.json b/locales/nl-NL/common.json index cbc542f5..c12c0212 100644 --- a/locales/nl-NL/common.json +++ b/locales/nl-NL/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Chat", - "market": "Ontdekken", + "discover": "Ontdekken", "role": "Rol", "settings": "Instellingen", "tips": "Het project is momenteel in uitvoering, waardoor de stabiliteit van de gegevens niet gegarandeerd kan worden. Als u problemen ondervindt, kunt u in de systeeminstellingen de sessieberichten wissen en de systeeminstellingen resetten. Onze excuses voor het ongemak." diff --git a/locales/nl-NL/error.json b/locales/nl-NL/error.json index bb77e805..1fff0c6f 100644 --- a/locales/nl-NL/error.json +++ b/locales/nl-NL/error.json @@ -18,5 +18,6 @@ "s3envError": "S3-omgeving variabelen zijn niet volledig ingesteld, controleer uw omgevingsvariabelen", "serverError": "Serverfout, neem contact op met de beheerder", "triggerError": "Fout activeren", + "ttsTransformFailed": "Spraakconversie mislukt, controleer het netwerk of probeer het opnieuw nadat u de clientaanroep in de instellingen hebt ingeschakeld.", "unknownError": "Onbekende fout" } diff --git a/locales/nl-NL/role.json b/locales/nl-NL/role.json index 74a99105..ae675ff8 100644 --- a/locales/nl-NL/role.json +++ b/locales/nl-NL/role.json @@ -6,6 +6,7 @@ "other": "Anders" }, "category": { + "all": "Alle", "animal": "Dier", "anime": "Anime", "book": "Boek", @@ -78,6 +79,8 @@ }, "noRole": "Geen rol beschikbaar. U kunt een aangepaste rol maken door op + te klikken, of rollen toevoegen via de ontdekkingpagina.", "role": { + "create": "Rol aanmaken", + "createRoleFailed": "Rol aanmaken mislukt", "greetTip": "Voer de begroeting in die je met de rol wilt gebruiken", "inputRoleSetting": "Voer de systeeminstellingen van de rol in", "roleDescriptionTip": "Voer een beschrijving van de rol in", @@ -85,6 +88,7 @@ "roleReadmeTip": "Voer een beschrijving van de rol in", "roleSettingDescription": "De achtergrondinstellingen van de rol, die naar het model worden gestuurd tijdens het chatten met de rol", "roleSettingLabel": "Systeemrolinstellingen", + "selectGender": "Selecteer geslacht van de rol", "uploadSize": "Ondersteunt het uploaden van een enkel bestand, aanbevolen afmetingen zijn een veelvoud van {{width}} * {{height}}" }, "roleBook": "Rolboek", diff --git a/locales/nl-NL/settings.json b/locales/nl-NL/settings.json index a71a709d..5e7353dc 100644 --- a/locales/nl-NL/settings.json +++ b/locales/nl-NL/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Aanraakinstellingen" + }, + "tts": { + "clientCall": { + "desc": "Wanneer ingeschakeld, wordt de spraaksynthetiseringsdienst via de cliënt aangeroepen, wat de spraaksynthetisesnelheid verhoogt, maar vereist dat je toegang hebt tot het internet of in staat bent om het externe netwerk te bereiken.", + "title": "Cliëntaanroep" + }, + "title": "Spraakinstellingen" } } diff --git a/locales/pl-PL/chat.json b/locales/pl-PL/chat.json index a8d39a9b..885d9a32 100644 --- a/locales/pl-PL/chat.json +++ b/locales/pl-PL/chat.json @@ -29,6 +29,7 @@ "totalCount": "Łącznie {{total}} pozycji" }, "chat": "Czat", + "chatList": "Lista czatów", "danceList": "Lista tańców", "danceMarket": "Rynek tańca", "delSession": "Usuń sesję", @@ -55,6 +56,7 @@ }, "interactive": "Interaktywny", "noDanceList": "Brak dostępnych list odtwarzania. Możesz subskrybować ulubione tańce na rynku.", + "noRoleList": "Brak dostępnych ról", "selectModel": "Wybierz model", "sessionCreate": "Utwórz czat", "sessionList": "Lista sesji", diff --git a/locales/pl-PL/common.json b/locales/pl-PL/common.json index 216a0cbd..096ba89e 100644 --- a/locales/pl-PL/common.json +++ b/locales/pl-PL/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Czat", - "market": "Odkryj", + "discover": "Odkryj", "role": "Rola", "settings": "Ustawienia", "tips": "Projekt jest obecnie w trakcie budowy, nie gwarantujemy stabilności danych. W przypadku problemów można w ustawieniach systemu wyczyścić wiadomości sesji i zresetować ustawienia systemowe. Przepraszamy za wszelkie niedogodności." diff --git a/locales/pl-PL/error.json b/locales/pl-PL/error.json index 098759cc..77c6563f 100644 --- a/locales/pl-PL/error.json +++ b/locales/pl-PL/error.json @@ -18,5 +18,6 @@ "s3envError": "Zmienne środowiskowe S3 nie są w pełni skonfigurowane, proszę sprawdzić swoje zmienne środowiskowe", "serverError": "Błąd serwera, proszę skontaktować się z administratorem", "triggerError": "Wystąpił błąd", + "ttsTransformFailed": "Nie udało się przetworzyć mowy, sprawdź połączenie sieciowe lub spróbuj ponownie po włączeniu wywołania klienta w ustawieniach.", "unknownError": "Nieznany błąd" } diff --git a/locales/pl-PL/role.json b/locales/pl-PL/role.json index d61fc687..6d4ae37c 100644 --- a/locales/pl-PL/role.json +++ b/locales/pl-PL/role.json @@ -6,6 +6,7 @@ "other": "Inne" }, "category": { + "all": "Wszystko", "animal": "Zwierzęta", "anime": "Anime", "book": "Książki", @@ -78,6 +79,8 @@ }, "noRole": "Brak ról, możesz stworzyć własną rolę klikając + lub dodać rolę przez stronę odkrywania.", "role": { + "create": "Utwórz postać", + "createRoleFailed": "Nie udało się utworzyć postaci", "greetTip": "Wprowadź powitanie, które chcesz użyć w rozmowie z postacią", "inputRoleSetting": "Wprowadź ustawienia systemowe postaci", "roleDescriptionTip": "Wprowadź opis postaci", @@ -85,6 +88,7 @@ "roleReadmeTip": "Wprowadź opis postaci", "roleSettingDescription": "Tło postaci, które zostanie przesłane do modelu podczas rozmowy z postacią", "roleSettingLabel": "Ustawienia systemowe postaci", + "selectGender": "Wybierz płeć postaci", "uploadSize": "Obsługuje przesyłanie pojedynczych plików, zalecany rozmiar to wielokrotność {{width}} * {{height}}" }, "roleBook": "Księga postaci", diff --git a/locales/pl-PL/settings.json b/locales/pl-PL/settings.json index 480b204f..f8bc8a32 100644 --- a/locales/pl-PL/settings.json +++ b/locales/pl-PL/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Ustawienia dotykowe" + }, + "tts": { + "clientCall": { + "desc": "Po włączeniu, usługa syntezatora mowy będzie wywoływana przez klienta, co przyspieszy proces syntezowania mowy, ale wymaga dostępu do internetu lub możliwości korzystania z sieci zewnętrznej.", + "title": "Wywołanie klienta" + }, + "title": "Ustawienia głosowe" } } diff --git a/locales/pt-BR/chat.json b/locales/pt-BR/chat.json index 1602653f..70795117 100644 --- a/locales/pt-BR/chat.json +++ b/locales/pt-BR/chat.json @@ -29,6 +29,7 @@ "totalCount": "Total de {{total}} itens" }, "chat": "Bate-papo", + "chatList": "Lista de Chats", "danceList": "Lista de Danças", "danceMarket": "Mercado de Danças", "delSession": "Excluir sessão", @@ -55,6 +56,7 @@ }, "interactive": "Interativo", "noDanceList": "Nenhuma lista de reprodução disponível no momento. Você pode assinar suas danças favoritas no mercado.", + "noRoleList": "Nenhuma lista de papéis disponível", "selectModel": "Por favor, selecione um modelo", "sessionCreate": "Criar chat", "sessionList": "Lista de Sessões", diff --git a/locales/pt-BR/common.json b/locales/pt-BR/common.json index f8c54c69..d904d47c 100644 --- a/locales/pt-BR/common.json +++ b/locales/pt-BR/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Bate-papo", - "market": "Descobrir", + "discover": "Descobrir", "role": "Função", "settings": "Configurações", "tips": "O projeto está atualmente em construção, não garantimos a estabilidade dos dados. Se você encontrar problemas, pode limpar as mensagens de sessão e redefinir as configurações do sistema nas configurações. Pedimos desculpas pelo inconveniente." diff --git a/locales/pt-BR/error.json b/locales/pt-BR/error.json index 31db0d28..c48067fd 100644 --- a/locales/pt-BR/error.json +++ b/locales/pt-BR/error.json @@ -18,5 +18,6 @@ "s3envError": "As variáveis de ambiente S3 não estão completamente configuradas, por favor verifique suas variáveis de ambiente", "serverError": "Erro no servidor, por favor entre em contato com o administrador", "triggerError": "Erro acionado", + "ttsTransformFailed": "Falha na conversão de voz, por favor verifique a conexão com a internet ou ative a chamada do cliente nas configurações e tente novamente.", "unknownError": "Erro desconhecido" } diff --git a/locales/pt-BR/role.json b/locales/pt-BR/role.json index f6f67517..bfc4f026 100644 --- a/locales/pt-BR/role.json +++ b/locales/pt-BR/role.json @@ -6,6 +6,7 @@ "other": "Outro" }, "category": { + "all": "Todos", "animal": "Animais", "anime": "Anime", "book": "Livros", @@ -78,6 +79,8 @@ }, "noRole": "Nenhum papel disponível. Você pode criar um papel personalizado clicando em + ou adicionar papéis pela página de descoberta.", "role": { + "create": "Criar papel", + "createRoleFailed": "Falha ao criar papel", "greetTip": "Digite a saudação que você usaria ao cumprimentar o personagem", "inputRoleSetting": "Digite as configurações do sistema do personagem", "roleDescriptionTip": "Digite a descrição do personagem", @@ -85,6 +88,7 @@ "roleReadmeTip": "Digite a descrição do personagem", "roleSettingDescription": "Configuração de fundo do personagem, que será enviada ao modelo durante a conversa com o personagem", "roleSettingLabel": "Configurações do personagem do sistema", + "selectGender": "Selecionar gênero do papel", "uploadSize": "Suporta o upload de um único arquivo, recomendado em múltiplos de {{width}} * {{height}}" }, "roleBook": "Livro de Personagens", diff --git a/locales/pt-BR/settings.json b/locales/pt-BR/settings.json index 5059cec0..8e865e2f 100644 --- a/locales/pt-BR/settings.json +++ b/locales/pt-BR/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Configurações de Toque" + }, + "tts": { + "clientCall": { + "desc": "Quando ativado, o serviço de síntese de voz será chamado pelo cliente, resultando em uma velocidade de síntese de voz mais rápida, mas requer acesso à internet ou a capacidade de contornar restrições de rede.", + "title": "Chamada do Cliente" + }, + "title": "Configurações de Voz" } } diff --git a/locales/ru-RU/chat.json b/locales/ru-RU/chat.json index 95197ee9..fc80efc2 100644 --- a/locales/ru-RU/chat.json +++ b/locales/ru-RU/chat.json @@ -29,6 +29,7 @@ "totalCount": "Всего {{total}} элементов" }, "chat": "Чат", + "chatList": "Список чатов", "danceList": "Список танцев", "danceMarket": "Рынок танцев", "delSession": "Удалить сессию", @@ -55,6 +56,7 @@ }, "interactive": "Интерактивный", "noDanceList": "Нет доступных плейлистов, вы можете подписаться на любимые танцы через рынок", + "noRoleList": "Список ролей отсутствует", "selectModel": "Пожалуйста, выберите модель", "sessionCreate": "Создать чат", "sessionList": "Список сессий", diff --git a/locales/ru-RU/common.json b/locales/ru-RU/common.json index c8c595d5..f622b3be 100644 --- a/locales/ru-RU/common.json +++ b/locales/ru-RU/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Чат", - "market": "Обнаружить", + "discover": "Открыть", "role": "Роль", "settings": "Настройки", "tips": "Проект в настоящее время находится в стадии разработки, стабильность данных не гарантируется. Если возникнут проблемы, вы можете очистить сообщения сессии и сбросить настройки системы в настройках. Приносим извинения за возможные неудобства." diff --git a/locales/ru-RU/error.json b/locales/ru-RU/error.json index 144fa7a3..4ff88dc7 100644 --- a/locales/ru-RU/error.json +++ b/locales/ru-RU/error.json @@ -18,5 +18,6 @@ "s3envError": "Переменные окружения S3 не полностью настроены, пожалуйста, проверьте ваши переменные окружения", "serverError": "Ошибка сервера, пожалуйста, свяжитесь с администратором", "triggerError": "Ошибка срабатывания", + "ttsTransformFailed": "Не удалось преобразовать речь, пожалуйста, проверьте соединение с сетью или попробуйте снова, включив вызов клиента в настройках.", "unknownError": "Неизвестная ошибка" } diff --git a/locales/ru-RU/role.json b/locales/ru-RU/role.json index 9bfe029b..8d55f199 100644 --- a/locales/ru-RU/role.json +++ b/locales/ru-RU/role.json @@ -6,6 +6,7 @@ "other": "Другой" }, "category": { + "all": "Все", "animal": "Животные", "anime": "Аниме", "book": "Книги", @@ -78,6 +79,8 @@ }, "noRole": "Нет ролей. Вы можете создать пользовательскую роль, нажав на +, или добавить роли через страницу обнаружения.", "role": { + "create": "Создать роль", + "createRoleFailed": "Не удалось создать роль", "greetTip": "Введите приветственное слово для общения с ролью", "inputRoleSetting": "Введите системные настройки роли", "roleDescriptionTip": "Введите описание роли", @@ -85,6 +88,7 @@ "roleReadmeTip": "Введите описание роли", "roleSettingDescription": "Фоновая информация о роли, которая будет отправлена модели во время общения с ролью", "roleSettingLabel": "Системные настройки роли", + "selectGender": "Выбрать пол роли", "uploadSize": "Поддерживается загрузка одного файла, рекомендуется размер, кратный {{width}} * {{height}}" }, "roleBook": "Книга персонажей", diff --git a/locales/ru-RU/settings.json b/locales/ru-RU/settings.json index 827984a1..3558be05 100644 --- a/locales/ru-RU/settings.json +++ b/locales/ru-RU/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Настройки касания" + }, + "tts": { + "clientCall": { + "desc": "После включения будет использоваться вызов клиента для службы синтеза речи, скорость синтеза речи будет выше, но требуется доступ к интернету или возможность обхода блокировок.", + "title": "Вызов клиента" + }, + "title": "Настройки голоса" } } diff --git a/locales/tr-TR/chat.json b/locales/tr-TR/chat.json index 1db88153..e9354c2f 100644 --- a/locales/tr-TR/chat.json +++ b/locales/tr-TR/chat.json @@ -29,6 +29,7 @@ "totalCount": "Toplam {{total}} Öğe" }, "chat": "sohbet", + "chatList": "Sohbet Listesi", "danceList": "Dans Listesi", "danceMarket": "Dans Pazarı", "delSession": "Oturumu Sil", @@ -55,6 +56,7 @@ }, "interactive": "Etkileşimli", "noDanceList": "Henüz bir çalma listesi yok, sevdiğiniz dansları pazardan abone olabilirsiniz", + "noRoleList": "Rol Listesi Yok", "selectModel": "Lütfen bir model seçin", "sessionCreate": "Sohbet Oluştur", "sessionList": "Oturum Listesi", diff --git a/locales/tr-TR/common.json b/locales/tr-TR/common.json index 20f849c2..e5fd3566 100644 --- a/locales/tr-TR/common.json +++ b/locales/tr-TR/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Sohbet", - "market": "Keşfet", + "discover": "Keşfet", "role": "Rol", "settings": "Ayarlar", "tips": "Proje şu anda inşaat aşamasındadır, veri kararlılığı garanti edilmemektedir. Sorunlarla karşılaşırsanız, sistem ayarlarından oturum mesajlarını temizleyebilir ve sistem ayarlarını sıfırlayabilirsiniz. Verdiğimiz rahatsızlıktan dolayı özür dileriz." diff --git a/locales/tr-TR/error.json b/locales/tr-TR/error.json index 03f00490..386f44a3 100644 --- a/locales/tr-TR/error.json +++ b/locales/tr-TR/error.json @@ -18,5 +18,6 @@ "s3envError": "S3 ortam değişkenleri tam olarak ayarlanmamış, lütfen ortam değişkenlerinizi kontrol edin", "serverError": "Sunucu hatası, lütfen yönetici ile iletişime geçin", "triggerError": "Hata tetiklendi", + "ttsTransformFailed": "Ses dönüştürme başarısız oldu, lütfen ağı kontrol edin veya ayarlardan istemci çağrısını açmayı deneyin.", "unknownError": "Bilinmeyen hata" } diff --git a/locales/tr-TR/role.json b/locales/tr-TR/role.json index 0f7bee98..73468d65 100644 --- a/locales/tr-TR/role.json +++ b/locales/tr-TR/role.json @@ -6,6 +6,7 @@ "other": "Diğer" }, "category": { + "all": "Tümü", "animal": "Hayvan", "anime": "Anime", "book": "Kitap", @@ -78,6 +79,8 @@ }, "noRole": "Henüz bir rol yok, + ile özel bir rol oluşturabilir veya keşif sayfasından rol ekleyebilirsiniz.", "role": { + "create": "Rol oluştur", + "createRoleFailed": "Rol oluşturma başarısız oldu", "greetTip": "Rol ile selamlaşırken kullanacağınız ifadeyi girin", "inputRoleSetting": "Rolün sistem ayarlarını girin", "roleDescriptionTip": "Rol tanımını girin", @@ -85,6 +88,7 @@ "roleReadmeTip": "Rol açıklamasını girin", "roleSettingDescription": "Rolün arka plan ayarı, rol ile sohbet ederken modele gönderilecektir", "roleSettingLabel": "Sistem rol ayarı", + "selectGender": "Rol cinsiyetini seç", "uploadSize": "Tek bir dosya yüklemeyi destekler, önerilen boyut {{width}} * {{height}} katlarıdır" }, "roleBook": "Karakter Kitabı", diff --git a/locales/tr-TR/settings.json b/locales/tr-TR/settings.json index 8ec28c44..d74f1327 100644 --- a/locales/tr-TR/settings.json +++ b/locales/tr-TR/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Dokunma Ayarları" + }, + "tts": { + "clientCall": { + "desc": "Etkinleştirildiğinde, istemci ses sentez hizmetini çağıracak, ses sentez hızı daha hızlı olacak, ancak bilimsel bir şekilde internete erişim veya dış ağa erişim yeteneği gerektirir.", + "title": "İstemci Çağrısı" + }, + "title": "Ses Ayarları" } } diff --git a/locales/vi-VN/chat.json b/locales/vi-VN/chat.json index 90b5c761..d0ca2f3f 100644 --- a/locales/vi-VN/chat.json +++ b/locales/vi-VN/chat.json @@ -29,6 +29,7 @@ "totalCount": "Tổng cộng {{total}} mục" }, "chat": "Trò chuyện", + "chatList": "Danh sách trò chuyện", "danceList": "Danh sách khiêu vũ", "danceMarket": "Thị trường khiêu vũ", "delSession": "Xóa cuộc trò chuyện", @@ -55,6 +56,7 @@ }, "interactive": "Có thể tương tác", "noDanceList": "Chưa có danh sách phát, bạn có thể đăng ký những điệu nhảy yêu thích của mình qua thị trường", + "noRoleList": "Chưa có danh sách vai trò", "selectModel": "Vui lòng chọn mô hình", "sessionCreate": "Tạo cuộc trò chuyện", "sessionList": "Danh sách phiên", diff --git a/locales/vi-VN/common.json b/locales/vi-VN/common.json index 45b731bd..ebcbe57d 100644 --- a/locales/vi-VN/common.json +++ b/locales/vi-VN/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "Trò chuyện", - "market": "Khám phá", + "discover": "Khám phá", "role": "Vai trò", "settings": "Cài đặt", "tips": "Dự án hiện đang trong quá trình thi công, không đảm bảo tính ổn định của dữ liệu. Nếu gặp vấn đề, bạn có thể xóa tin nhắn phiên và đặt lại cài đặt hệ thống trong cài đặt hệ thống. Xin lỗi vì sự bất tiện này." diff --git a/locales/vi-VN/error.json b/locales/vi-VN/error.json index 1053e75d..a8d5e254 100644 --- a/locales/vi-VN/error.json +++ b/locales/vi-VN/error.json @@ -18,5 +18,6 @@ "s3envError": "Biến môi trường S3 chưa được thiết lập đầy đủ, vui lòng kiểm tra biến môi trường của bạn", "serverError": "Lỗi máy chủ, vui lòng liên hệ với quản trị viên", "triggerError": "Kích hoạt lỗi", + "ttsTransformFailed": "Chuyển đổi giọng nói thất bại, vui lòng kiểm tra kết nối mạng hoặc mở gọi ứng dụng trong cài đặt và thử lại", "unknownError": "Lỗi không xác định" } diff --git a/locales/vi-VN/role.json b/locales/vi-VN/role.json index d5cebdad..363d6583 100644 --- a/locales/vi-VN/role.json +++ b/locales/vi-VN/role.json @@ -6,6 +6,7 @@ "other": "Khác" }, "category": { + "all": "Tất cả", "animal": "Động vật", "anime": "Anime", "book": "Sách", @@ -78,6 +79,8 @@ }, "noRole": "Chưa có vai trò nào, bạn có thể tạo vai trò tùy chỉnh bằng cách nhấn + hoặc thêm vai trò qua trang khám phá.", "role": { + "create": "Tạo nhân vật", + "createRoleFailed": "Tạo nhân vật thất bại", "greetTip": "Vui lòng nhập lời chào khi bạn gặp gỡ nhân vật", "inputRoleSetting": "Vui lòng nhập cài đặt hệ thống cho nhân vật", "roleDescriptionTip": "Vui lòng nhập mô tả cho nhân vật", @@ -85,6 +88,7 @@ "roleReadmeTip": "Vui lòng nhập mô tả về nhân vật", "roleSettingDescription": "Bối cảnh của nhân vật, sẽ được gửi đến mô hình khi trò chuyện với nhân vật", "roleSettingLabel": "Cài đặt nhân vật hệ thống", + "selectGender": "Chọn giới tính nhân vật", "uploadSize": "Hỗ trợ tải lên tệp đơn, kích thước khuyến nghị là bội số của {{width}} * {{height}}" }, "roleBook": "Sách vai trò", diff --git a/locales/vi-VN/settings.json b/locales/vi-VN/settings.json index 3b93f7ad..95d84033 100644 --- a/locales/vi-VN/settings.json +++ b/locales/vi-VN/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "Cài đặt chạm" + }, + "tts": { + "clientCall": { + "desc": "Khi được kích hoạt, dịch vụ tổng hợp giọng nói sẽ được gọi từ khách hàng, tốc độ tổng hợp giọng nói nhanh hơn, nhưng cần có khả năng truy cập internet hoặc sử dụng mạng riêng ảo.", + "title": "Gọi từ khách hàng" + }, + "title": "Cài đặt giọng nói" } } diff --git a/locales/zh-CN/chat.json b/locales/zh-CN/chat.json index 4f8d95bd..d568637c 100644 --- a/locales/zh-CN/chat.json +++ b/locales/zh-CN/chat.json @@ -29,6 +29,7 @@ "totalCount": "共 {{total}} 项" }, "chat": "聊天", + "chatList": "聊天列表", "danceList": "舞蹈列表", "danceMarket": "舞蹈市场", "delSession": "删除会话", @@ -55,6 +56,7 @@ }, "interactive": "可交互", "noDanceList": "暂无播放列表,您可以通过市场订阅你喜欢的舞蹈", + "noRoleList": "暂无角色列表", "selectModel": "请选择模型", "sessionCreate": "创建聊天", "sessionList": "会话列表", diff --git a/locales/zh-CN/common.json b/locales/zh-CN/common.json index f60099f3..6e0bde90 100644 --- a/locales/zh-CN/common.json +++ b/locales/zh-CN/common.json @@ -21,7 +21,7 @@ "tips": "项目当前正在施工中,不保证数据稳定性,如果遇到问题可以在系统设置中清除会话消息与重置系统设置,造成不便敬请谅解", "chat": "聊天", "role": "角色", - "market": "发现", + "discover": "发现", "settings": "设置" }, "loading": "加载中...", diff --git a/locales/zh-CN/error.json b/locales/zh-CN/error.json index b2f750a9..8f5273f6 100644 --- a/locales/zh-CN/error.json +++ b/locales/zh-CN/error.json @@ -18,5 +18,6 @@ "s3envError": "S3 环境变量未完全设置,请检查您的环境变量", "serverError": "服务器错误,请联系管理员", "triggerError": "触发错误", + "ttsTransformFailed": "语音转换失败, 请检查网络或在设置中打开客户端调用后尝试", "unknownError": "未知错误" } diff --git a/locales/zh-CN/role.json b/locales/zh-CN/role.json index 2538eabd..9247e7b7 100644 --- a/locales/zh-CN/role.json +++ b/locales/zh-CN/role.json @@ -6,6 +6,7 @@ "other": "其他" }, "category": { + "all": "所有", "animal": "动物", "anime": "动漫", "book": "书籍", @@ -78,6 +79,8 @@ }, "noRole": "暂无角色,可以通过 + 创建自定义角色,也可通过发现页添加角色", "role": { + "create": "创建角色", + "selectGender": "选择角色性别", "uploadSize": "支持单个文件上传,推荐尺寸为 {{width}} * {{height}} 的倍数", "greetTip": "请输入角色与你打招呼时的用语", "roleReadmeTip": "请输入角色说明", @@ -85,6 +88,7 @@ "roleNameTip": "请输入角色名称", "inputRoleSetting": "请输入角色的系统设定", "roleSettingLabel": "系统角色设定", + "createRoleFailed": "角色创建失败", "roleSettingDescription": "角色的背景设定,在与角色聊天时会发送给模型" }, "roleBook": "角色书", diff --git a/locales/zh-CN/settings.json b/locales/zh-CN/settings.json index a94acf14..b20fdd66 100644 --- a/locales/zh-CN/settings.json +++ b/locales/zh-CN/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "触摸设定" + }, + "tts": { + "title": "语音设置", + "clientCall": { + "title": "客户端调用", + "desc": "启用后,将使用客户端调用语音合成服务,语音合成速度更快,但需要科学上网或具备访问外网的能力" + } } } diff --git a/locales/zh-TW/chat.json b/locales/zh-TW/chat.json index 318a170d..0c6b0a71 100644 --- a/locales/zh-TW/chat.json +++ b/locales/zh-TW/chat.json @@ -29,6 +29,7 @@ "totalCount": "共 {{total}} 項" }, "chat": "聊天", + "chatList": "聊天列表", "danceList": "舞蹈列表", "danceMarket": "舞蹈市場", "delSession": "刪除會話", @@ -55,6 +56,7 @@ }, "interactive": "可互動", "noDanceList": "暫無播放列表,您可以透過市場訂閱您喜歡的舞蹈", + "noRoleList": "目前沒有角色列表", "selectModel": "請選擇模型", "sessionCreate": "建立聊天", "sessionList": "會話列表", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 3c7f3ed5..a94d5a0c 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -19,7 +19,7 @@ }, "header": { "chat": "聊天", - "market": "發現", + "discover": "發現", "role": "角色", "settings": "設定", "tips": "專案目前正在施工中,不保證數據穩定性,如果遇到問題可以在系統設定中清除會話消息與重置系統設定,造成不便敬請見諒" diff --git a/locales/zh-TW/error.json b/locales/zh-TW/error.json index 8cc14ef9..f0f9a900 100644 --- a/locales/zh-TW/error.json +++ b/locales/zh-TW/error.json @@ -18,5 +18,6 @@ "s3envError": "S3 環境變數未完全設定,請檢查您的環境變數", "serverError": "伺服器錯誤,請聯絡管理員", "triggerError": "觸發錯誤", + "ttsTransformFailed": "語音轉換失敗,請檢查網路或在設定中開啟客戶端調用後再試", "unknownError": "未知錯誤" } diff --git a/locales/zh-TW/role.json b/locales/zh-TW/role.json index 539a1c15..657285ba 100644 --- a/locales/zh-TW/role.json +++ b/locales/zh-TW/role.json @@ -6,6 +6,7 @@ "other": "其他" }, "category": { + "all": "所有", "animal": "動物", "anime": "動畫", "book": "書籍", @@ -78,6 +79,8 @@ }, "noRole": "目前沒有角色,可以透過 + 創建自訂角色,也可以透過發現頁面添加角色", "role": { + "create": "建立角色", + "createRoleFailed": "角色建立失敗", "greetTip": "請輸入角色與你打招呼時的用語", "inputRoleSetting": "請輸入角色的系統設定", "roleDescriptionTip": "請輸入角色描述", @@ -85,6 +88,7 @@ "roleReadmeTip": "請輸入角色說明", "roleSettingDescription": "角色的背景設定,在與角色聊天時會發送給模型", "roleSettingLabel": "系統角色設定", + "selectGender": "選擇角色性別", "uploadSize": "支持單個檔案上傳,推薦尺寸為 {{width}} * {{height}} 的倍數" }, "roleBook": "角色書", diff --git a/locales/zh-TW/settings.json b/locales/zh-TW/settings.json index 4f325887..01ccc4a1 100644 --- a/locales/zh-TW/settings.json +++ b/locales/zh-TW/settings.json @@ -89,5 +89,12 @@ }, "touch": { "title": "觸摸設定" + }, + "tts": { + "clientCall": { + "desc": "啟用後,將使用客戶端調用語音合成服務,語音合成速度更快,但需要科學上網或具備訪問外網的能力", + "title": "客戶端調用" + }, + "title": "語音設定" } } diff --git a/next.config.mjs b/next.config.mjs index 0aaa214b..30c79d42 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -20,7 +20,14 @@ const nextConfig = { compress: isProd, pageExtensions: ['tsx', 'ts'], experimental: { - optimizePackageImports: ['@lobehub/ui', '@lobehub/icons', 'chroma-js', 'shiki', '@icons-pack/react-simple-icons','gpt-tokenizer'], + optimizePackageImports: [ + '@lobehub/ui', + '@lobehub/icons', + 'chroma-js', + 'shiki', + '@icons-pack/react-simple-icons', + 'gpt-tokenizer', + ], }, reactStrictMode: true, webpack(config) { @@ -31,13 +38,19 @@ const nextConfig = { // to fix shikiji compile error // refs: https://github.com/antfu/shikiji/issues/23 - config.module.rules.push({ - test: /\.m?js$/, - type: 'javascript/auto', - resolve: { - fullySpecified: false, + config.module.rules.push( + { + test: /\.m?js$/, + type: 'javascript/auto', + resolve: { + fullySpecified: false, + }, }, - }); + { + test: /\.(glsl|vs|fs|vert|frag)$/, + type: 'asset/source', + }, + ); return config; }, diff --git a/src/app/api/voice/edge/route.ts b/src/app/api/voice/edge/route.ts index 55d6876d..c8df174b 100644 --- a/src/app/api/voice/edge/route.ts +++ b/src/app/api/voice/edge/route.ts @@ -1,25 +1,9 @@ -import { EdgeSpeechTTS } from '@lobehub/tts'; -import { Buffer } from 'node:buffer'; +import { EdgeSpeechPayload, EdgeSpeechTTS } from '@lobehub/tts'; -// Instantiate EdgeSpeechTTS -const tts = new EdgeSpeechTTS({ locale: 'en-US' }); +export const runtime = 'edge'; export const POST = async (req: Request) => { - const { message, pitch, speed, style, voice } = await req.json(); - // Create speech synthesis request payload - const payload = { - input: message, - options: { - voice: voice || 'en-US-GuyNeural', - style, - pitch: (pitch - 1) / 2, - rate: speed - 1, - }, - }; + const payload = (await req.json()) as EdgeSpeechPayload; - // Call create method to synthesize speech - const response = await tts.create(payload); - const mp3Buffer = Buffer.from(await response.arrayBuffer()); - - return new Response(mp3Buffer); + return await EdgeSpeechTTS.createRequest({ payload }); }; diff --git a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/List/index.tsx b/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/List/index.tsx deleted file mode 100644 index bf90b921..00000000 --- a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/List/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { memo, useEffect } from 'react'; - -import GridList from '@/components/GridList'; -import { agentSelectors, useAgentStore } from '@/store/agent'; -import { useMarketStore } from '@/store/market'; - -const AgentList = () => { - const [activateAgent, agentList, agentLoading, currentAgentId, fetchAgentIndex] = useMarketStore( - (s) => [s.activateAgent, s.agentList, s.agentLoading, s.currentAgentId, s.fetchAgentIndex], - ); - useEffect(() => { - fetchAgentIndex(); - }, [fetchAgentIndex]); - - const [subscribed] = useAgentStore((s) => [agentSelectors.subscribed(s)]); - - return ( - ({ - avatar: items.meta.avatar, - id: items.agentId, - name: items.meta.name, - }))} - onClick={(id) => { - activateAgent(id); - }} - isActivated={(id) => id === currentAgentId} - isChecked={(id) => subscribed(id)} - /> - ); -}; - -export default memo(AgentList); diff --git a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/actions/ChatButton.tsx b/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/actions/ChatButton.tsx deleted file mode 100644 index 2a830459..00000000 --- a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/actions/ChatButton.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Button } from 'antd'; -import React, { memo } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { useSessionStore } from '@/store/session'; -import { Agent } from '@/types/agent'; - -interface ChatButtonProps { - agent: Agent; - onClick?: () => void; -} - -const ChatButton = memo(({ agent, onClick }) => { - const { t } = useTranslation('chat'); - const createSession = useSessionStore((s) => s.createSession); - - return ( - - ); -}); - -export default ChatButton; diff --git a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/index.tsx b/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/index.tsx deleted file mode 100644 index bda835c4..00000000 --- a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/index.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { DraggablePanel } from '@lobehub/ui'; -import { createStyles } from 'antd-style'; -import React, { memo, useState } from 'react'; - -import Author from '@/components/Author'; -import AgentCard from '@/components/agent/AgentCard'; -import SystemRole from '@/components/agent/SystemRole'; -import { SIDEBAR_MAX_WIDTH, SIDEBAR_WIDTH } from '@/constants/token'; -import { agentSelectors, useAgentStore } from '@/store/agent'; -import { marketStoreSelectors, useMarketStore } from '@/store/market'; - -import ChatButton from './actions/ChatButton'; -import Subscribe from './actions/Subscribe'; -import UnSubscribe from './actions/UnSubscribe'; - -const useStyles = createStyles(({ css, token }) => ({ - content: css` - overflow: auto; - display: flex; - flex-direction: column; - - height: 100% !important; - padding: 0; - `, - header: css` - border-bottom: 1px solid ${token.colorBorder}; - `, -})); - -interface MarketInfoProps { - setIsModalOpen: (isOpen: boolean) => void; -} - -const MarketInfo = (props: MarketInfoProps) => { - const { styles } = useStyles(); - const { setIsModalOpen } = props; - const [tempId, setTempId] = useState(''); - const [showAgentSidebar, activateAgent, deactivateAgent, currentAgentItem] = useMarketStore( - (s) => [ - marketStoreSelectors.showAgentSideBar(s), - s.activateAgent, - s.deactivateAgent, - marketStoreSelectors.currentAgentItem(s), - ], - ); - const [subscribed] = useAgentStore((s) => [agentSelectors.subscribed(s)]); - - const actions = []; - if (currentAgentItem) { - const isSubscribed = subscribed(currentAgentItem.agentId); - - if (isSubscribed) { - actions.push( - { - setIsModalOpen(false); - }} - />, - , - ); - } else { - actions.push( - , - ); - } - } - - return ( - { - if (!show) { - setTempId(useMarketStore.getState().currentAgentId); - deactivateAgent(); - } else if (tempId) { - activateAgent(tempId); - } - }} - placement={'right'} - > - - } - footer={} - /> - - ); -}; - -export default memo(MarketInfo); diff --git a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/index.tsx b/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/index.tsx deleted file mode 100644 index 59fbca77..00000000 --- a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/index.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { createStyles } from 'antd-style'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { Flexbox } from 'react-layout-kit'; - -import TopBanner from '@/components/TopBanner'; - -import AgentList from './List'; -import MarketInfo from './MarketInfo'; - -const useStyles = createStyles(({ css }) => ({ - container: css` - overflow-y: auto; - - width: 100%; - height: 100%; - min-height: 500px; - padding: 0 24px; - `, - content: css` - max-width: 1024px; - margin: 0 auto; - `, - title: css` - z-index: 2; - margin-top: 24px; - font-size: 36px; - font-weight: 800; - `, -})); - -interface AgentProps { - setIsModalOpen: (isOpen: boolean) => void; -} - -const Agent = (props: AgentProps) => { - const { styles } = useStyles(); - const { setIsModalOpen } = props; - const { t } = useTranslation('market'); - - return ( - -
-
- - -
-
- -
- ); -}; - -export default Agent; diff --git a/src/app/chat/SideBar/SessionList/SessionCreateModal/index.tsx b/src/app/chat/SideBar/SessionList/SessionCreateModal/index.tsx deleted file mode 100644 index 8c75fe03..00000000 --- a/src/app/chat/SideBar/SessionList/SessionCreateModal/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ActionIcon, Modal } from '@lobehub/ui'; -import { PlusCircle } from 'lucide-react'; -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { DESKTOP_HEADER_ICON_SIZE } from '@/constants/token'; - -import Market from './Market'; -import { useStyles } from './style'; - -export default () => { - const { t } = useTranslation('chat'); - const [isModalOpen, setIsModalOpen] = useState(false); - const { styles } = useStyles(); - - return ( - <> - setIsModalOpen(true)} - /> - - setIsModalOpen(false)} - open={isModalOpen} - title={t('agentMarket')} - > - - - - ); -}; diff --git a/src/app/chat/SideBar/SessionList/SessionCreateModal/style.ts b/src/app/chat/SideBar/SessionList/SessionCreateModal/style.ts deleted file mode 100644 index 1fa46db0..00000000 --- a/src/app/chat/SideBar/SessionList/SessionCreateModal/style.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createStyles } from 'antd-style'; - -export const useStyles = createStyles(({ css, token }) => ({ - modalBody: css` - height: 640px; - padding: 0; - padding-block: 0 !important; - padding-inline: 0 !important; - - border-top: 1px solid ${token.colorBorder}; - `, -})); diff --git a/src/app/chat/SideBar/SessionList/index.tsx b/src/app/chat/SideBar/SessionList/index.tsx index 92f4cb7a..a40b8bc8 100644 --- a/src/app/chat/SideBar/SessionList/index.tsx +++ b/src/app/chat/SideBar/SessionList/index.tsx @@ -1,12 +1,13 @@ -import { SearchBar } from '@lobehub/ui'; +import { Icon, SearchBar } from '@lobehub/ui'; +import { Collapse } from 'antd'; import { createStyles } from 'antd-style'; +import { ChevronDown } from 'lucide-react'; import dynamic from 'next/dynamic'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import SkeletonList from '@/components/SkeletonList'; -import { HEADER_HEIGHT } from '@/constants/token'; import V from './Elsa'; @@ -15,11 +16,11 @@ const List = dynamic(() => import('./List'), { loading: () => , }); -const SessionCreateModal = dynamic(() => import('./SessionCreateModal'), { - ssr: false, -}); - const useStyles = createStyles(({ css, token, prefixCls }) => ({ + session: css` + overflow-y: auto; + height: 100%; + `, list: css` padding: 8px; `, @@ -55,33 +56,49 @@ const useStyles = createStyles(({ css, token, prefixCls }) => ({ const SessionList = () => { const { styles } = useStyles(); const [searchName, setSearchName] = useState(); - const { t } = useTranslation('common'); + const { t } = useTranslation('chat'); return ( - <> - +
+ { setSearchName(e.target.value); }} - placeholder={t('search')} + placeholder={t('search', { ns: 'common' })} shortKey="f" spotlight type={'block'} value={searchName} /> -
- + ( + + )} + expandIconPosition={'end'} + ghost + size={'small'} + items={[ + { + children: , + label: t('chatList'), + key: 'default', + }, + ]} + />
- +
); }; diff --git a/src/app/chat/SideBar/index.tsx b/src/app/chat/SideBar/index.tsx index 1624a1d0..832d1499 100644 --- a/src/app/chat/SideBar/index.tsx +++ b/src/app/chat/SideBar/index.tsx @@ -5,15 +5,23 @@ import { createStyles, useResponsive } from 'antd-style'; import { rgba } from 'polished'; import { useEffect } from 'react'; -import { SIDEBAR_MAX_WIDTH, SIDEBAR_WIDTH } from '@/constants/token'; +import { HEADER_HEIGHT, SIDEBAR_MAX_WIDTH, SIDEBAR_WIDTH } from '@/constants/token'; import { useGlobalStore } from '@/store/global'; import SessionList from './SessionList'; const useStyles = createStyles(({ css, token }) => ({ + content: css` + display: flex; + flex-direction: column; + height: 100% !important; + `, sidebar: css` display: flex; flex-direction: column; + + height: calc(100vh - ${HEADER_HEIGHT}px); + background-color: ${rgba(token.colorBgLayout, 0.2)}; backdrop-filter: saturate(180%) blur(8px); `, @@ -43,6 +51,7 @@ const SideBar = () => { return ( ({ + container: css` + position: relative; + height: 100%; + background-color: rgba(255, 255, 255, 2%); + border-radius: ${token.borderRadius}px; + `, + header: css` + padding: ${token.padding}px ${token.paddingSM}px; + `, + list: css` + overflow-y: scroll; + width: 100%; + height: 100%; + padding: 0 ${token.paddingSM}px; + `, +})); + +interface AgentListProps { + activateAgent: (agentId: string) => void; + agents: Agent[]; + className?: string; + loading: boolean; + style?: React.CSSProperties; +} + +const AgentList = (props: AgentListProps) => { + const { activateAgent, className, style, agents = [], loading } = props; + const { styles } = useStyles(); + + const { t } = useTranslation('role'); + + // 定义分类选项 + const CATEGORIES = [ + { key: 'all', label: t('category.all') }, + { label: t('category.game'), key: CategoryEnum.GAME }, + { label: t('category.vroid'), key: CategoryEnum.VROID }, + { label: t('category.anime'), key: CategoryEnum.ANIME }, + { label: t('category.animal'), key: CategoryEnum.ANIMAL }, + { label: t('category.book'), key: CategoryEnum.BOOK }, + { label: t('category.history'), key: CategoryEnum.HISTORY }, + { label: t('category.movie'), key: CategoryEnum.MOVIE }, + { label: t('category.realistic'), key: CategoryEnum.REALISTIC }, + + { label: t('category.vtuber'), key: CategoryEnum.VTUBER }, + ]; + + // 添加状态管理 + const [activeCategory, setActiveCategory] = useState('all'); + const [searchKeyword, setSearchKeyword] = useState(''); + + // 根据分类和搜索关键词筛选 agents + const filteredAgents = agents.filter((agent) => { + const matchCategory = activeCategory === 'all' || agent.meta.category === activeCategory; + const matchSearch = + agent.meta.name.toLowerCase().includes(searchKeyword.toLowerCase()) || + agent.meta.description.toLowerCase().includes(searchKeyword.toLowerCase()); + return matchCategory && matchSearch; + }); + + return ( + + + setActiveCategory(key)} + /> + setSearchKeyword(e.target.value)} + allowClear + /> + + + {loading ? ( + + ) : ( + + {filteredAgents.length === 0 ? ( + + ) : ( + + {filteredAgents.map((agent) => ( + activateAgent(agent.agentId)} + /> + ))} + + )} + + )} + + ); +}; + +export default memo(AgentList); diff --git a/src/app/market/Market/MarketInfo/actions/ChatButton.tsx b/src/app/discover/MarketInfo/actions/ChatButton.tsx similarity index 100% rename from src/app/market/Market/MarketInfo/actions/ChatButton.tsx rename to src/app/discover/MarketInfo/actions/ChatButton.tsx diff --git a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/actions/Subscribe.tsx b/src/app/discover/MarketInfo/actions/Subscribe.tsx similarity index 100% rename from src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/actions/Subscribe.tsx rename to src/app/discover/MarketInfo/actions/Subscribe.tsx diff --git a/src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/actions/UnSubscribe.tsx b/src/app/discover/MarketInfo/actions/UnSubscribe.tsx similarity index 100% rename from src/app/chat/SideBar/SessionList/SessionCreateModal/Market/MarketInfo/actions/UnSubscribe.tsx rename to src/app/discover/MarketInfo/actions/UnSubscribe.tsx diff --git a/src/app/discover/MarketInfo/index.tsx b/src/app/discover/MarketInfo/index.tsx new file mode 100644 index 00000000..ad5580c2 --- /dev/null +++ b/src/app/discover/MarketInfo/index.tsx @@ -0,0 +1,118 @@ +import { DraggablePanel } from '@lobehub/ui'; +import { Skeleton, Space } from 'antd'; +import { createStyles } from 'antd-style'; +import React, { memo, useState } from 'react'; +import { Flexbox } from 'react-layout-kit'; +import useSWR from 'swr'; + +import Author from '@/components/Author'; +import AgentCard from '@/components/agent/AgentCard'; +import SystemRole from '@/components/agent/SystemRole'; +import { SIDEBAR_MAX_WIDTH, SIDEBAR_WIDTH } from '@/constants/token'; +import { getAgentDetail } from '@/services/agent'; +import { agentSelectors, useAgentStore } from '@/store/agent'; + +import ChatButton from './actions/ChatButton'; +import Subscribe from './actions/Subscribe'; +import UnSubscribe from './actions/UnSubscribe'; + +const useStyles = createStyles(({ css, token }) => ({ + content: css` + overflow: auto; + display: flex; + flex-direction: column; + height: 100% !important; + `, + header: css` + border-bottom: 1px solid ${token.colorBorder}; + `, +})); + +interface RoleInfoProps { + activateAgent: (agentId: string) => void; + currentAgentId: string; + deactivateAgent: () => void; +} + +const RoleInfo = (props: RoleInfoProps) => { + const { activateAgent, currentAgentId, deactivateAgent } = props; + const { styles } = useStyles(); + const [tempId, setTempId] = useState(''); + + const { data: currentAgentItem, isLoading } = useSWR( + currentAgentId ? `/api/agent/${currentAgentId}` : null, + () => (currentAgentId ? getAgentDetail(currentAgentId) : null), + ); + + const showAgentSideBar = !!currentAgentId; + + const [subscribed] = useAgentStore((s) => [agentSelectors.subscribed(s)]); + + const actions = []; + if (currentAgentId && currentAgentItem) { + const isSubscribed = subscribed(currentAgentId); + + if (isSubscribed) { + actions.push( + , + , + ); + } else { + actions.push( + , + ); + } + } + + return ( + { + if (!show) { + setTempId(currentAgentId); + deactivateAgent(); + } else if (tempId) { + activateAgent(tempId); + } + }} + placement={'right'} + > + {isLoading ? ( + + + + + + + + + + + ) : ( + currentAgentItem && ( + + } + footer={ + + } + /> + ) + )} + + ); +}; + +export default memo(RoleInfo); diff --git a/src/app/market/layout.tsx b/src/app/discover/layout.tsx similarity index 92% rename from src/app/market/layout.tsx rename to src/app/discover/layout.tsx index c9e10ea3..b1badf6b 100644 --- a/src/app/market/layout.tsx +++ b/src/app/discover/layout.tsx @@ -11,7 +11,7 @@ export interface LayoutProps { const Layout = (props: LayoutProps) => { const { children } = props; return ( - + {children} diff --git a/src/app/discover/page.tsx b/src/app/discover/page.tsx new file mode 100644 index 00000000..3e70d74f --- /dev/null +++ b/src/app/discover/page.tsx @@ -0,0 +1,78 @@ +'use client'; + +import { createStyles } from 'antd-style'; +import React, { useState } from 'react'; +import { Flexbox } from 'react-layout-kit'; +import useSWR from 'swr'; + +import { HEADER_HEIGHT } from '@/constants/token'; +import { getAgentIndex } from '@/services/agent'; +import { Agent } from '@/types/agent'; + +import DiscoverList from './List'; +import MarketInfo from './MarketInfo'; + +const FETCH_AGENT_INDEX_KEY = 'agentIndex'; + +const useStyles = createStyles(({ css }) => ({ + container: css` + overflow-y: auto; + + width: 100%; + height: calc(100% - ${HEADER_HEIGHT}px); + min-height: 500px; + padding: 0 32px 16px; + + @media screen and (max-width: 768px) { + padding: 0 16px; + } + `, + content: css` + max-width: 1280px; + margin: 0 auto; + `, + title: css` + z-index: 2; + margin-top: 24px; + font-size: 36px; + font-weight: 800; + `, +})); + +const Index = () => { + const { styles } = useStyles(); + + const { data, isLoading } = useSWR(FETCH_AGENT_INDEX_KEY, async () => { + const { agents = [] } = await getAgentIndex(); + return agents; + }); + + const [currentAgentId, setCurrentAgentId] = useState(''); + + const activateAgent = (identifier: string) => { + setCurrentAgentId(identifier); + }; + + const deactivateAgent = () => { + setCurrentAgentId(''); + }; + + return ( + + +
+
+ +
+
+ +
+
+ ); +}; + +export default Index; diff --git a/src/app/market/Market/List/index.tsx b/src/app/market/Market/List/index.tsx deleted file mode 100644 index bf90b921..00000000 --- a/src/app/market/Market/List/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { memo, useEffect } from 'react'; - -import GridList from '@/components/GridList'; -import { agentSelectors, useAgentStore } from '@/store/agent'; -import { useMarketStore } from '@/store/market'; - -const AgentList = () => { - const [activateAgent, agentList, agentLoading, currentAgentId, fetchAgentIndex] = useMarketStore( - (s) => [s.activateAgent, s.agentList, s.agentLoading, s.currentAgentId, s.fetchAgentIndex], - ); - useEffect(() => { - fetchAgentIndex(); - }, [fetchAgentIndex]); - - const [subscribed] = useAgentStore((s) => [agentSelectors.subscribed(s)]); - - return ( - ({ - avatar: items.meta.avatar, - id: items.agentId, - name: items.meta.name, - }))} - onClick={(id) => { - activateAgent(id); - }} - isActivated={(id) => id === currentAgentId} - isChecked={(id) => subscribed(id)} - /> - ); -}; - -export default memo(AgentList); diff --git a/src/app/market/Market/MarketInfo/actions/Subscribe.tsx b/src/app/market/Market/MarketInfo/actions/Subscribe.tsx deleted file mode 100644 index 702c01be..00000000 --- a/src/app/market/Market/MarketInfo/actions/Subscribe.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Button, Popover, Progress, Space } from 'antd'; -import React, { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Flexbox } from 'react-layout-kit'; - -import { useDownloadAgent } from '@/hooks/useDownloadAgent'; -import { agentSelectors, useAgentStore } from '@/store/agent'; -import { Agent } from '@/types/agent'; - -interface SubscribeButtonProps { - agent: Agent; -} - -const Subscribe = memo((props: SubscribeButtonProps) => { - const { agent } = props; - const subscribed = useAgentStore((s) => agentSelectors.subscribed(s)); - - const { fetchAgentData, percent, downloading } = useDownloadAgent(); - const isSubscribed = subscribed(agent.agentId); - - const { t } = useTranslation('common'); - - return ( - document.querySelector('#subscribe_button')!} - title={ - - - - {t('download.cover')} - - - - {t('download.avatar')} - - - - {t('download.model')} - - - } - > - - - ); -}); - -export default Subscribe; diff --git a/src/app/market/Market/MarketInfo/actions/UnSubscribe.tsx b/src/app/market/Market/MarketInfo/actions/UnSubscribe.tsx deleted file mode 100644 index db2b71f2..00000000 --- a/src/app/market/Market/MarketInfo/actions/UnSubscribe.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Button, Popconfirm, message } from 'antd'; -import React, { memo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { useAgentStore } from '@/store/agent'; -import { useSessionStore } from '@/store/session'; -import { Agent } from '@/types/agent'; - -interface UnSubscribeButtonProps { - agent: Agent; -} - -const UnSubscribe = memo((props: UnSubscribeButtonProps) => { - const { agent } = props; - const removeLocalAgent = useAgentStore((s) => s.removeLocalAgent); - const removeSessionByAgentId = useSessionStore((s) => s.removeSessionByAgentId); - const [loading, setLoading] = useState(false); - - const { t } = useTranslation('role'); - - return ( - { - if (!agent) return; - setLoading(true); - await removeLocalAgent(agent.agentId); - removeSessionByAgentId(agent.agentId); - setLoading(false); - message.success(t('subscribe.success', { ns: 'common' })); - }} - title={t('subscribe.undo', { ns: 'common' }) + '?'} - > - - - ); -}); - -export default UnSubscribe; diff --git a/src/app/market/Market/MarketInfo/index.tsx b/src/app/market/Market/MarketInfo/index.tsx deleted file mode 100644 index afa8b50f..00000000 --- a/src/app/market/Market/MarketInfo/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { DraggablePanel } from '@lobehub/ui'; -import { createStyles } from 'antd-style'; -import React, { memo, useState } from 'react'; - -import Author from '@/components/Author'; -import AgentCard from '@/components/agent/AgentCard'; -import SystemRole from '@/components/agent/SystemRole'; -import { SIDEBAR_MAX_WIDTH, SIDEBAR_WIDTH } from '@/constants/token'; -import { agentSelectors, useAgentStore } from '@/store/agent'; -import { marketStoreSelectors, useMarketStore } from '@/store/market'; - -import ChatButton from './actions/ChatButton'; -import Subscribe from './actions/Subscribe'; -import UnSubscribe from './actions/UnSubscribe'; - -const useStyles = createStyles(({ css, token }) => ({ - content: css` - overflow: auto; - display: flex; - flex-direction: column; - height: 100% !important; - `, - header: css` - border-bottom: 1px solid ${token.colorBorder}; - `, -})); - -const Header = () => { - const { styles } = useStyles(); - const [tempId, setTempId] = useState(''); - const [showAgentSidebar, activateAgent, deactivateAgent, currentAgentItem] = useMarketStore( - (s) => [ - marketStoreSelectors.showAgentSideBar(s), - s.activateAgent, - s.deactivateAgent, - marketStoreSelectors.currentAgentItem(s), - ], - ); - const [subscribed] = useAgentStore((s) => [agentSelectors.subscribed(s)]); - - const actions = []; - if (currentAgentItem) { - const isSubscribed = subscribed(currentAgentItem.agentId); - - if (isSubscribed) { - actions.push( - , - , - ); - } else { - actions.push( - , - ); - } - } - - return ( - { - if (!show) { - setTempId(useMarketStore.getState().currentAgentId); - deactivateAgent(); - } else if (tempId) { - activateAgent(tempId); - } - }} - placement={'right'} - > - - } - footer={} - /> - - ); -}; - -export default memo(Header); diff --git a/src/app/market/Market/index.tsx b/src/app/market/Market/index.tsx deleted file mode 100644 index 69b29e53..00000000 --- a/src/app/market/Market/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -'use client'; - -import { createStyles } from 'antd-style'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { Flexbox } from 'react-layout-kit'; - -import TopBanner from '@/components/TopBanner'; - -import AgentList from './List'; -import MarketInfo from './MarketInfo'; - -const useStyles = createStyles(({ css }) => ({ - container: css` - overflow-y: auto; - - width: 100%; - height: 100%; - min-height: 500px; - padding: 0 24px; - `, - content: css` - max-width: 1024px; - margin: 0 auto; - `, - title: css` - z-index: 2; - margin-top: 24px; - font-size: 36px; - font-weight: 800; - `, -})); - -const Agent = () => { - const { styles } = useStyles(); - const { t } = useTranslation('market'); - - return ( - -
-
- - -
-
- -
- ); -}; - -export default Agent; diff --git a/src/app/market/page.tsx b/src/app/market/page.tsx deleted file mode 100644 index edcc8089..00000000 --- a/src/app/market/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Flexbox } from 'react-layout-kit'; - -import AgentMarket from './Market'; - -const Index = () => { - return ( - - - - ); -}; - -export default Index; diff --git a/src/app/role/SideBar/RoleList/index.tsx b/src/app/role/SideBar/RoleList/index.tsx index ba0080e3..84d045ba 100644 --- a/src/app/role/SideBar/RoleList/index.tsx +++ b/src/app/role/SideBar/RoleList/index.tsx @@ -8,9 +8,7 @@ import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import SkeletonList from '@/components/SkeletonList'; -import { HEADER_HEIGHT } from '@/constants/token'; -import AgentCreate from './AgentCreate'; import Elsa from './List/Elsa'; const List = dynamic(() => import('./List'), { @@ -62,12 +60,7 @@ const RoleList = () => { return (
- + { @@ -79,7 +72,6 @@ const RoleList = () => { type={'block'} value={searchName} /> -
diff --git a/src/app/settings/Settings/index.tsx b/src/app/settings/Settings/index.tsx index 6e230899..44f0b7da 100644 --- a/src/app/settings/Settings/index.tsx +++ b/src/app/settings/Settings/index.tsx @@ -7,6 +7,7 @@ import OpenAIConfig from '@/app/settings/Settings/llm/openai'; import CommonConfig from './common'; import Touch from './touch'; +import TTSConfig from './tts'; interface ConfigProps { className?: string; @@ -36,6 +37,10 @@ const Config = (props: ConfigProps) => { key: 'touch', label: t('touch.title'), }, + { + key: 'tts', + label: t('tts.title'), + }, ]} onChange={(key) => { setTab(key); @@ -46,6 +51,7 @@ const Config = (props: ConfigProps) => { {tab === 'languageModel' ? : null} {tab === 'common' ? : null} {tab === 'touch' ? : null} + {tab === 'tts' ? : null} ); diff --git a/src/app/settings/Settings/tts/ClientCall/index.tsx b/src/app/settings/Settings/tts/ClientCall/index.tsx new file mode 100644 index 00000000..c71059bd --- /dev/null +++ b/src/app/settings/Settings/tts/ClientCall/index.tsx @@ -0,0 +1,25 @@ +import { Switch } from 'antd'; +import React, { memo } from 'react'; + +import { useSettingStore } from '@/store/setting'; +import { configSelectors } from '@/store/setting/selectors/config'; + +interface ClientCallProps { + style?: React.CSSProperties; +} + +export default memo(({ style }: ClientCallProps) => { + const [clientCall, setConfig] = useSettingStore((s) => [ + configSelectors.currentTTSConfig(s).clientCall, + s.setConfig, + ]); + return ( + { + setConfig({ tts: { clientCall: value } }); + }} + /> + ); +}); diff --git a/src/app/settings/Settings/tts/index.tsx b/src/app/settings/Settings/tts/index.tsx new file mode 100644 index 00000000..79b61feb --- /dev/null +++ b/src/app/settings/Settings/tts/index.tsx @@ -0,0 +1,19 @@ +import { Form, FormProps } from '@lobehub/ui'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import ClientCall from './ClientCall'; + +export default () => { + const { t } = useTranslation('settings'); + + const voice: FormProps['items'] = [ + { + label: t('tts.clientCall.title'), + desc: t('tts.clientCall.desc'), + name: 'clientCall', + children: , + }, + ]; + return
; +}; diff --git a/src/components/RoleCard/index.tsx b/src/components/RoleCard/index.tsx new file mode 100644 index 00000000..cfadf27b --- /dev/null +++ b/src/components/RoleCard/index.tsx @@ -0,0 +1,42 @@ +import { Avatar } from '@lobehub/ui'; +import { useHover } from 'ahooks'; +import React, { memo, useRef } from 'react'; + +import ListItem from '@/components/ListItem'; +import { Agent } from '@/types/agent'; + +import { useStyles } from './style'; + +interface RoleCardProps { + agent: Agent; + onClick?: () => void; +} + +const RoleCard = (props: RoleCardProps) => { + const { agent, onClick } = props; + const { styles } = useStyles(); + + const hoverRef = useRef(null); + const isHovered = useHover(hoverRef); + + return ( + { + onClick?.(); + }} + className={styles.listItem} + avatar={ +
+ +
+ } + title={agent?.meta?.name} + description={agent?.meta?.description} + active={isHovered} + addon={`By ${agent?.author}`} + /> + ); +}; + +export default memo(RoleCard); diff --git a/src/components/RoleCard/style.ts b/src/components/RoleCard/style.ts new file mode 100644 index 00000000..4118d614 --- /dev/null +++ b/src/components/RoleCard/style.ts @@ -0,0 +1,12 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ css, token }) => ({ + listItem: css` + font-size: ${token.fontSize}px; + background-color: ${token.colorBgContainer}; + border-radius: ${token.borderRadius}px; + transition: background-color 0.3s ease; + `, +})); + +export { useStyles }; diff --git a/src/constants/common.ts b/src/constants/common.ts index dc0c822d..07355631 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -1,4 +1,4 @@ -export const AGENT_INDEX_URL = 'https://vidol-market.lobehub.com/agents/index.json'; +export const AGENT_MARKET_URL = 'https://vidol-market.lobehub.com/agents'; export const DANCE_INDEX_URL = 'https://vidol-market.lobehub.com/dances/index.json'; diff --git a/src/features/AgentViewer/index.tsx b/src/features/AgentViewer/index.tsx index 062a77ba..24101ae3 100644 --- a/src/features/AgentViewer/index.tsx +++ b/src/features/AgentViewer/index.tsx @@ -1,4 +1,5 @@ import { VRMExpressionPresetName } from '@pixiv/three-vrm'; +import { message } from 'antd'; import classNames from 'classnames'; import React, { memo, useCallback, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -148,6 +149,8 @@ function AgentViewer(props: Props) { } } } + } catch (err) { + message.error((err as Error).message); } finally { setLoading(false); setLoadingStep(0); diff --git a/src/layout/Header/Actions/CreateRole/index.tsx b/src/layout/Header/Actions/CreateRole/index.tsx new file mode 100644 index 00000000..e281127d --- /dev/null +++ b/src/layout/Header/Actions/CreateRole/index.tsx @@ -0,0 +1,122 @@ +import { PlusOutlined } from '@ant-design/icons'; +import { Avatar, Button, Modal, message } from 'antd'; +import { createStyles } from 'antd-style'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import ListItem from '@/components/ListItem'; +import { useAgentStore } from '@/store/agent'; +import { GenderEnum } from '@/types/agent'; + +const useStyles = createStyles(({ css, token }) => ({ + genderList: css` + display: flex; + gap: 16px; + margin-top: 24px; + `, + genderCard: css` + cursor: pointer; + + flex: 1; + + padding: 16px; + + text-align: center; + + border: 1px solid ${token.colorBorder}; + border-radius: 8px; + + &:hover { + border-color: ${token.colorPrimary}; + } + + &.selected { + border-color: ${token.colorPrimary}; + } + `, + createButton: css` + width: 100%; + margin-top: 24px; + `, +})); + +export default function CreateRole() { + const { t } = useTranslation('role'); + const { styles } = useStyles(); + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedGender, setSelectedGender] = useState(null); + const [loading, setLoading] = useState(false); + const router = useRouter(); + const createNewAgent = useAgentStore((s) => s.createNewAgent); + + const handleCreateRole = async () => { + if (!selectedGender) return; + + try { + setLoading(true); + await createNewAgent(selectedGender); + router.push(`/role`); + setIsModalOpen(false); + } catch (error) { + console.error('create role failed:', error); + message.error(t('role.createRoleFailed')); + } finally { + setLoading(false); + } + }; + + const genderOptions = [ + { + key: GenderEnum.MALE, + label: t('agent.male'), + icon: 'https://r2.vidol.chat/common/Anime%20boy.png', + }, + { + key: GenderEnum.FEMALE, + label: t('agent.female'), + icon: 'https://r2.vidol.chat/common/Anime%20girl.png', + }, + ]; + + return ( + <> + + + !loading && setIsModalOpen(false)} + closable={!loading} + maskClosable={!loading} + > +
+ {genderOptions.map((option) => ( + } + className={`${styles.genderCard} ${selectedGender === option.key ? 'selected' : ''}`} + onClick={() => setSelectedGender(option.key as GenderEnum)} + active={selectedGender === option.key} + title={option.label} + /> + ))} +
+ + +
+ + ); +} diff --git a/src/layout/Header/index.tsx b/src/layout/Header/index.tsx index c4ffc7c7..64733e41 100644 --- a/src/layout/Header/index.tsx +++ b/src/layout/Header/index.tsx @@ -14,6 +14,7 @@ import Logo from '@/components/Logo'; import { HeaderNavKey } from '@/layout/type'; import Avatar from './Actions/Avatar'; +import CreateRole from './Actions/CreateRole'; import Documentation from './Actions/Documentation'; import Github from './Actions/Github'; import Support from './Actions/Support'; @@ -53,10 +54,10 @@ const Header = (props: Props) => { title: t('header.role'), }, { - key: HeaderNavKey.Market, + key: HeaderNavKey.Discover, icon: , - component: t('header.market'), - title: t('header.market'), + component: t('header.discover'), + title: t('header.discover'), }, { key: HeaderNavKey.Settings, @@ -159,6 +160,7 @@ const Header = (props: Props) => { + diff --git a/src/layout/type.ts b/src/layout/type.ts index 5d714250..7f616149 100644 --- a/src/layout/type.ts +++ b/src/layout/type.ts @@ -1,7 +1,6 @@ export enum HeaderNavKey { Chat = 'chat', - Market = 'market', - My = 'my', + Discover = 'discover', Role = 'role', Settings = 'settings', } diff --git a/src/libs/data/VRMMaterial.ts b/src/libs/data/VRMMaterial.ts new file mode 100644 index 00000000..3fd3407e --- /dev/null +++ b/src/libs/data/VRMMaterial.ts @@ -0,0 +1,10 @@ +export interface VRMMaterial { + name: string; + shader: string; + renderQueue: number; + floatProperties: { [key: string]: number }; + vectorProperties: { [key: string]: number[] }; + textureProperties: { [key: string]: number }; + keywordMap: { [key: string]: any }; + tagMap: { [key: string]: string }; +} diff --git a/src/libs/materials/VRMShaderMaterial.ts b/src/libs/materials/VRMShaderMaterial.ts new file mode 100644 index 00000000..99046c22 --- /dev/null +++ b/src/libs/materials/VRMShaderMaterial.ts @@ -0,0 +1,279 @@ +import * as THREE from 'three'; + +import { VRMMaterial } from '../data/VRMMaterial'; +import common_mtoon from '../shaders/common_mtoon.glsl'; +import lights_mtoon_pars_fragment from '../shaders/lights_mtoon_pars_fragment.glsl'; +import mtoon_frag from '../shaders/mtoon_frag.glsl'; +import mtoon_vert from '../shaders/mtoon_vert.glsl'; + +Object.assign(THREE.ShaderChunk, { + common_mtoon, + lights_mtoon_pars_fragment, +}); + +const defaultParameters = new Map([ + [ + 'VRM/UnlitTexture', + { + defines: {}, + uniforms: { + ...THREE.ShaderLib.basic.uniforms, + f_Cutoff: { value: 0.0 }, + v_Color: { value: new THREE.Vector4(1.0, 1.0, 1.0, 1.0) }, + }, + vertexShader: THREE.ShaderLib.basic.vertexShader, + fragmentShader: THREE.ShaderLib.basic.fragmentShader, + lights: false, + }, + ], + [ + 'VRM/UnlitCutout', + { + defines: {}, + uniforms: { + ...THREE.ShaderLib.basic.uniforms, + f_Cutoff: { value: 0.0 }, + v_Color: { value: new THREE.Vector4(1.0, 1.0, 1.0, 1.0) }, + }, + vertexShader: THREE.ShaderLib.basic.vertexShader, + fragmentShader: THREE.ShaderLib.basic.fragmentShader, + lights: false, + }, + ], + [ + 'VRM/UnlitTransparent', + { + defines: {}, + uniforms: { + ...THREE.ShaderLib.basic.uniforms, + f_Cutoff: { value: 0.0 }, + v_Color: { value: new THREE.Vector4(1.0, 1.0, 1.0, 1.0) }, + }, + vertexShader: THREE.ShaderLib.basic.vertexShader, + fragmentShader: THREE.ShaderLib.basic.fragmentShader, + lights: false, + }, + ], + [ + 'VRM/UnlitTransparentZWrite', + { + defines: {}, + uniforms: { + ...THREE.ShaderLib.basic.uniforms, + f_Cutoff: { value: 0.0 }, + v_Color: { value: new THREE.Vector4(1.0, 1.0, 1.0, 1.0) }, + }, + vertexShader: THREE.ShaderLib.basic.vertexShader, + fragmentShader: THREE.ShaderLib.basic.fragmentShader, + lights: false, + }, + ], + [ + 'VRM/MToon', + { + defines: {}, + uniforms: { + ...THREE.ShaderLib.phong.uniforms, + f_Cutoff: { value: 0.0 }, + v_Color: { value: new THREE.Vector4(1.0, 1.0, 1.0, 1.0) }, + }, + vertexShader: mtoon_vert, + fragmentShader: mtoon_frag, + lights: true, + }, + ], +]); + +const convertParameters = new Map void>([ + [ + 'common', + (material) => { + // if (material.defines._ALPHAPREMULTIPLY_ON !== undefined) { + // material.defines.PREMULTIPLIED_ALPHA = material.defines._ALPHAPREMULTIPLY_ON; + // } + + if (material.uniforms.f_Cutoff) { + material.defines.ALPHATEST = (material.uniforms.f_Cutoff.value as number).toFixed(6); + } + + const color = material.uniforms.v_Color.value; + material.uniforms.diffuse = { value: new THREE.Color(color.x, color.y, color.z) }; + material.uniforms.opacity = { value: color.w }; + + if (material.uniforms.t_MainTex) { + material.map = material.uniforms.t_MainTex.value; + material.uniforms.map = material.uniforms.t_MainTex; + } + }, + ], + ['VRM/UnlitTexture', (material) => null], + ['VRM/UnlitCutout', (material) => null], + [ + 'VRM/UnlitTransparent', + (material) => { + material.transparent = true; + }, + ], + [ + 'VRM/UnlitTransparentZWrite', + (material) => { + material.transparent = true; + }, + ], + [ + 'VRM/MToon', + (material) => { + if (!material.uniforms.t_SphereAdd) { + material.uniforms.t_SphereAdd = { + value: new THREE.DataTexture(new Uint8Array(3), 1, 1, THREE.RGBFormat), + }; + } + + material.uniforms.shininess = { value: 0.0 }; + + switch (material.userData.RenderType.value) { + case 'Opaque': { + delete material.defines.ALPHATEST; + break; + } + case 'Cutout': { + break; + } + case 'Transparent': { + delete material.defines.ALPHATEST; + material.transparent = true; + break; + } + case 'TransparentCutout': { + material.transparent = true; + break; + } + } + + if (material.uniforms.f_BumpScale) { + const normalScale = new THREE.Vector2(1, 1).multiplyScalar( + material.uniforms.f_BumpScale.value, + ); + material.normalScale = normalScale; + material.uniforms.normalScale = { value: normalScale }; + } + if (material.uniforms.t_BumpMap) { + material.normalMap = material.uniforms.t_BumpMap.value; + material.uniforms.normalMap = material.uniforms.t_BumpMap; + } + + if (material.uniforms.v_EmissionColor) { + material.emissive = material.uniforms.v_EmissionColor.value; + material.uniforms.emissive = material.uniforms.v_EmissionColor; + } + if (material.uniforms.t_EmissionMap) { + material.emissiveMap = material.uniforms.t_EmissionMap.value; + material.uniforms.emissiveMap = material.uniforms.t_EmissionMap; + } + + if (material.uniforms.f_CullMode) { + switch (material.uniforms.f_CullMode.value) { + case 0: { + material.side = THREE.DoubleSide; + break; + } + case 1: { + material.side = THREE.BackSide; + break; + } + case 2: { + material.side = THREE.FrontSide; + break; + } + } + } + }, + ], +]); + +export class VRMShaderMaterial extends THREE.ShaderMaterial { + [key: string]: any; + + constructor(parameters?: THREE.ShaderMaterialParameters) { + super(parameters); + + Object.assign(this.uniforms, { v_Color: { value: new THREE.Vector4(1.0, 0.0, 1.0, 1.0) } }); + this.vertexShader = THREE.ShaderLib.basic.vertexShader; + this.fragmentShader = THREE.ShaderLib.basic.fragmentShader; + + const commonConverter = convertParameters.get('common'); + if (commonConverter) { + commonConverter(this); + } + } + + public fromMaterialProperty(property: VRMMaterial, textures: THREE.Texture[]) { + this.name = property.shader; + + if (!defaultParameters.has(property.shader) || !convertParameters.has(property.shader)) { + return; + } + + const parameters = defaultParameters.get(property.shader); + if (!parameters) { + return; + } + + const defines: any = {}; + const uniforms: any = {}; + + for (const key of Object.keys(property.floatProperties)) { + uniforms['f' + key] = { value: property.floatProperties[key] }; + } + + for (const key of Object.keys(property.vectorProperties)) { + const array = property.vectorProperties[key].concat(); + array.length = 4; + uniforms['v' + key] = { value: new THREE.Vector4().fromArray(array) }; + } + + for (const key of Object.keys(property.textureProperties)) { + if (textures[property.textureProperties[key]] !== undefined) { + uniforms['t' + key] = { value: textures[property.textureProperties[key]] }; + } + } + + for (const key of Object.keys(property.keywordMap)) { + defines[key] = property.keywordMap[key]; + } + + for (const key of Object.keys(property.tagMap)) { + this.userData[key] = { value: property.tagMap[key] }; + } + + if (parameters.defines) { + Object.assign(this.defines, parameters.defines); + } + Object.assign(this.defines, defines); + + if (parameters.uniforms) { + Object.assign(this.uniforms, parameters.uniforms); + } + Object.assign(this.uniforms, uniforms); + + if (parameters.vertexShader) { + this.vertexShader = parameters.vertexShader; + } + if (parameters.fragmentShader) { + this.fragmentShader = parameters.fragmentShader; + } + if (parameters.lights !== undefined) { + this.lights = parameters.lights; + } + + const commonConverter = convertParameters.get('common'); + const shaderConverter = convertParameters.get(property.shader); + + if (commonConverter) { + commonConverter(this); + } + if (shaderConverter) { + shaderConverter(this); + } + } +} diff --git a/src/libs/materials/index.ts b/src/libs/materials/index.ts new file mode 100644 index 00000000..075df8be --- /dev/null +++ b/src/libs/materials/index.ts @@ -0,0 +1 @@ +export * from './VRMShaderMaterial'; diff --git a/src/libs/messages/speakCharacter.ts b/src/libs/messages/speakCharacter.ts index 8ae6c1e6..2270ff7b 100644 --- a/src/libs/messages/speakCharacter.ts +++ b/src/libs/messages/speakCharacter.ts @@ -1,3 +1,5 @@ +import { message } from 'antd'; + import { Viewer } from '@/libs/vrmViewer/viewer'; import { speechApi } from '@/services/tts'; import { Screenplay } from '@/types/touch'; @@ -23,7 +25,9 @@ const createSpeakCharacter = () => { const buffer = (await getPreloadedVoice(screenplay.tts)) || - (await speechApi(screenplay.tts).catch(() => null)); + (await speechApi(screenplay.tts).catch((err) => { + message.error((err as Error).message); + })); lastTime = Date.now(); return buffer; }); diff --git a/src/libs/shaders/common_mtoon.glsl b/src/libs/shaders/common_mtoon.glsl new file mode 100644 index 00000000..ddadc50e --- /dev/null +++ b/src/libs/shaders/common_mtoon.glsl @@ -0,0 +1,31 @@ +uniform float f_Cutoff; +uniform vec4 v_Color; +uniform vec4 v_ShadeColor; +uniform sampler2D t_MainTex; +uniform sampler2D t_ShadeTexture; +uniform float f_BumpScale; +uniform sampler2D t_BumpMap; +uniform float f_ReceiveShadowRate; +uniform sampler2D t_ReceiveShadowTexture; +uniform float f_ShadeShift; +uniform float f_ShadeToony; +uniform float f_LightColorAttenuation; +uniform sampler2D t_SphereAdd; +uniform vec4 v_EmissionColor; +uniform sampler2D t_EmissionMap; +uniform sampler2D t_OutlineWidthTexture; +uniform float f_OutlineWidth; +uniform float f_OutlineScaledMaxDistance; +uniform vec4 v_OutlineColor; +uniform float f_OutlineLightingMix; + +uniform int f_DebugMode; +uniform int f_BlendMode; +uniform int f_OutlineWidthMode; +uniform int f_OutlineColorMode; +uniform int f_CullMode; // Cull [0: Off | 1: Front | 2: Back] +uniform int f_OutlineCullMode; +uniform float f_SrcBlend; // Blend [SrcFactor] [DstFactor] +uniform float f_DstBlend; // Blend [SrcFactor] [DstFactor] +uniform int f_ZWrite; // ZWrite [On | Off] +uniform int f_IsFirstSetup; diff --git a/src/libs/shaders/lights_mtoon_pars_fragment.glsl b/src/libs/shaders/lights_mtoon_pars_fragment.glsl new file mode 100644 index 00000000..03845a64 --- /dev/null +++ b/src/libs/shaders/lights_mtoon_pars_fragment.glsl @@ -0,0 +1,46 @@ +varying vec3 vViewPosition; + +#ifndef FLAT_SHADED + + varying vec3 vNormal; + +#endif + +struct BlinnPhongMaterial { + + vec3 diffuseColor; + vec3 specularColor; + float specularShininess; + float specularStrength; + +}; + +void RE_Direct_BlinnPhong(const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight) { + + float dotNL = saturate(dot(geometry.normal, directLight.direction)); + dotNL = saturate(smoothstep(f_ShadeShift, f_ShadeShift + (1.0 + f_ShadeToony), dotNL)); + vec3 irradiance = mix(v_ShadeColor.rgb, vec3(1.0), dotNL); + + irradiance = irradiance * mix(directLight.color, vec3(average(directLight.color)), f_LightColorAttenuation); + + #ifndef PHYSICALLY_CORRECT_LIGHTS + + irradiance *= PI; + + #endif + + reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert(material.diffuseColor); + reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong(directLight, geometry, material.specularColor, material.specularShininess) * material.specularStrength; + +} + +void RE_IndirectDiffuse_BlinnPhong(const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight) { + + reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert(material.diffuseColor); + +} + +#define RE_Direct RE_Direct_BlinnPhong +#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong + +#define Material_LightProbeLOD(material) (0) diff --git a/src/libs/shaders/mtoon_frag.glsl b/src/libs/shaders/mtoon_frag.glsl new file mode 100644 index 00000000..c717663d --- /dev/null +++ b/src/libs/shaders/mtoon_frag.glsl @@ -0,0 +1,87 @@ +#include + +// Extend MeshPhongMaterial +#define PHONG + +uniform vec3 diffuse; +uniform vec3 emissive; +uniform vec3 specular; +uniform float shininess; +uniform float opacity; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include +#include +#include +// #include +#include +#include +#include +#include +#include +#include + +#include + +void main() { + + #include + + vec4 diffuseColor = vec4(diffuse, opacity); + ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); + vec3 totalEmissiveRadiance = emissive; + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // accumulation + #include + #include + #include + #include + + // modulation + #include + + // vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; + vec3 outgoingLight = reflectedLight.directDiffuse + totalEmissiveRadiance; + + #include + + // outgoingLight = mix(v_ShadeColor.rgb, diffuseColor.rgb, saturate(outgoingLight / diffuseColor.rgb)); + outgoingLight = clamp(outgoingLight, 0.0, 1.0); + + // MToon additive matcap + vec3 viewNormal = normalize(normal); + vec2 rimUv = vec2(dot(vec3(1.0, 0.0, 0.0), normal), -dot(vec3(0.0, 1.0, 0.0), normal)) * 0.5 + 0.5; + vec4 rimColor = texture2D(t_SphereAdd, rimUv); + outgoingLight += rimColor.rgb; + + gl_FragColor = vec4(outgoingLight, diffuseColor.a); + + #include + #include + #include + #include + #include + +} diff --git a/src/libs/shaders/mtoon_vert.glsl b/src/libs/shaders/mtoon_vert.glsl new file mode 100644 index 00000000..98a34cd6 --- /dev/null +++ b/src/libs/shaders/mtoon_vert.glsl @@ -0,0 +1,60 @@ +#include + +// Extend MeshPhongMaterial +#define PHONG + +varying vec3 vViewPosition; + +#ifndef FLAT_SHADED + + varying vec3 vNormal; + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void main() { + + #include + #include + #include + + #include + #include + #include + #include + #include + +#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED + + vNormal = normalize( transformedNormal ); + +#endif + + #include + #include + #include + #include + #include + #include + #include + + vViewPosition = - mvPosition.xyz; + + #include + #include + #include + #include + +} diff --git a/src/libs/vrmViewer/viewer.ts b/src/libs/vrmViewer/viewer.ts index 963d984b..28831547 100644 --- a/src/libs/vrmViewer/viewer.ts +++ b/src/libs/vrmViewer/viewer.ts @@ -228,7 +228,7 @@ export class Viewer { // 相机初始化 this._camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 2000); - this._camera.position.set(0, 1.5, 2.5); + this._camera.position.set(0, 1.5, 2.0); // camera 控制 this._cameraControls = new OrbitControls(this._camera, this._renderer.domElement); diff --git a/src/locales/default/chat.ts b/src/locales/default/chat.ts index 646abfdf..2a313dfa 100644 --- a/src/locales/default/chat.ts +++ b/src/locales/default/chat.ts @@ -41,7 +41,9 @@ export default { agentMarket: '角色市场', danceMarket: '舞蹈市场', danceList: '舞蹈列表', + chatList: '聊天列表', noDanceList: '暂无播放列表,您可以通过市场订阅你喜欢的舞蹈', + noRoleList: '暂无角色列表', tts: { record: '语音识别(需科学上网)', combine: '语音合成', diff --git a/src/locales/default/common.ts b/src/locales/default/common.ts index 9c1237d7..f442b68e 100644 --- a/src/locales/default/common.ts +++ b/src/locales/default/common.ts @@ -3,7 +3,7 @@ export default { tips: '项目当前正在施工中,不保证数据稳定性,如果遇到问题可以在系统设置中清除会话消息与重置系统设置,造成不便敬请谅解', chat: '聊天', role: '角色', - market: '发现', + discover: '发现', settings: '设置', }, menu: { diff --git a/src/locales/default/error.ts b/src/locales/default/error.ts index e581b6e5..3ee614c8 100644 --- a/src/locales/default/error.ts +++ b/src/locales/default/error.ts @@ -19,4 +19,5 @@ export default { s3envError: 'S3 环境变量未完全设置,请检查您的环境变量', formValidationFailed: '表单验证失败:', dancePlayError: '舞蹈文件播放失败, 请稍后重试', + ttsTransformFailed: '语音转换失败, 请检查网络或在设置中打开客户端调用后尝试', }; diff --git a/src/locales/default/role.ts b/src/locales/default/role.ts index 10760e52..628803d5 100644 --- a/src/locales/default/role.ts +++ b/src/locales/default/role.ts @@ -34,6 +34,8 @@ export default { delRoleDesc: '确定删除角色 {{name}} 以及相关联的会话消息吗?删除后无法恢复, 请谨慎操作!', noRole: '暂无角色,可以通过 + 创建自定义角色,也可通过发现页添加角色', role: { + create: '创建角色', + selectGender: '选择角色性别', uploadSize: '支持单个文件上传,推荐尺寸为 {{width}} * {{height}} 的倍数', greetTip: '请输入角色与你打招呼时的用语', roleReadmeTip: '请输入角色说明', @@ -41,6 +43,7 @@ export default { roleNameTip: '请输入角色名称', inputRoleSetting: '请输入角色的系统设定', roleSettingLabel: '系统角色设定', + createRoleFailed: '角色创建失败', roleSettingDescription: '角色的背景设定,在与角色聊天时会发送给模型', }, touch: { @@ -226,6 +229,7 @@ export default { model: '3D模型', }, category: { + all: '所有', animal: '动物', anime: '动漫', book: '书籍', diff --git a/src/locales/default/settings.ts b/src/locales/default/settings.ts index fd70f93d..e402da7f 100644 --- a/src/locales/default/settings.ts +++ b/src/locales/default/settings.ts @@ -90,4 +90,11 @@ export default { touch: { title: '触摸设定', }, + tts: { + title: '语音设置', + clientCall: { + title: '客户端调用', + desc: '启用后,将使用客户端调用语音合成服务,语音合成速度更快,但需要科学上网或具备访问外网的能力', + }, + }, }; diff --git a/src/services/agent.ts b/src/services/agent.ts index 618d097f..13d64c13 100644 --- a/src/services/agent.ts +++ b/src/services/agent.ts @@ -1,14 +1,20 @@ -import { AGENT_INDEX_URL } from '@/constants/common'; +import { AGENT_MARKET_URL } from '@/constants/common'; /** * 请求线上 Agent index */ -export const getAgentIndex = async (url: string = AGENT_INDEX_URL) => { +export const getAgentIndex = async (url: string = `${AGENT_MARKET_URL}/index.json`) => { const res = await fetch(url); return res.json(); }; +export const getAgentDetail = async (id: string) => { + const res = await fetch(`${AGENT_MARKET_URL}/${id}.json`); + + return res.json(); +}; + export const downloadGithubAgent = async (url: string) => { const res = await fetch('/api/agent/download', { body: JSON.stringify({ url }), diff --git a/src/services/tts.ts b/src/services/tts.ts index ad3fbd87..f8e92534 100644 --- a/src/services/tts.ts +++ b/src/services/tts.ts @@ -1,21 +1,51 @@ +import { EdgeSpeechTTS } from '@lobehub/tts'; +import { t } from 'i18next'; + +import { configSelectors, useSettingStore } from '@/store/setting'; import { TTS, TTS_ENGINE, Voice } from '@/types/tts'; export const speechApi = async (tts: TTS) => { - const { engine = 'edge' } = tts; - const res = await fetch(`/api/voice/${engine}`, { - body: JSON.stringify(tts), - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - }); - if (res.status !== 200) { - const data = await res.json(); - throw new Error(data.errorMessage); - } + const { engine = 'edge', message, style, pitch = 1, speed = 1, voice, locale = 'en-US' } = tts; - const buffer = await res.arrayBuffer(); - return buffer; + const settingStore = useSettingStore.getState(); + const clientCall = configSelectors.currentTTSConfig(settingStore).clientCall; + + if (engine === 'edge') { + const payload = { + input: message || '', + options: { + voice: voice || 'en-US-GuyNeural', + style, + pitch: (pitch - 1) / 2, + rate: speed - 1, + }, + }; + let res; + try { + if (clientCall) { + const instance = new EdgeSpeechTTS({ locale }); + res = await instance.create(payload); + } else { + res = await fetch(`/api/voice/${engine}`, { + body: JSON.stringify(payload), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }); + if (res.status !== 200) { + throw new Error(res.statusText); + } + } + const buffer = await res.arrayBuffer(); + return buffer; + } catch (error) { + console.error('TTS error', error); + throw new Error(t('ttsTransformFailed', { ns: 'error' })); + } + } else { + throw new Error('TTS engine not supported'); + } }; const getVoiceKey = (engine: TTS_ENGINE) => { diff --git a/src/store/agent/index.ts b/src/store/agent/index.ts index 498b6e87..e1cbf747 100644 --- a/src/store/agent/index.ts +++ b/src/store/agent/index.ts @@ -50,7 +50,7 @@ export interface AgentStore extends TouchStore { /** * 创建新角色 */ - createNewAgent: (gender: GenderEnum) => void; + createNewAgent: (gender: GenderEnum) => Agent; /** * 当前激活的角色 */ @@ -162,6 +162,8 @@ const createAgentStore: StateCreator }); set({ currentIdentifier: newAgent.agentId, localAgentList: newList }); + + return newAgent; }, updateAgentConfig: (agent, updateAgentId) => { const { localAgentList, currentIdentifier, defaultAgent } = get(); diff --git a/src/store/market/index.ts b/src/store/market/index.ts index 4cbe24e7..3effdb80 100644 --- a/src/store/market/index.ts +++ b/src/store/market/index.ts @@ -3,17 +3,14 @@ import { shallow } from 'zustand/shallow'; import { createWithEqualityFn } from 'zustand/traditional'; import { StateCreator } from 'zustand/vanilla'; -import { agentSelectors } from './selectors/agent'; import { danceSelectors } from './selectors/dance'; -import { AgentStore, createAgentStore } from './slices/agent'; import { DanceStore, createDanceStore } from './slices/dance'; -export type MarketStore = AgentStore & DanceStore; +export type MarketStore = DanceStore; const MARKET_STORAGE_KEY = 'vidol-chat-market-storage'; const createStore: StateCreator = (...parameters) => ({ - ...createAgentStore(...parameters), ...createDanceStore(...parameters), }); @@ -30,6 +27,5 @@ export const useMarketStore = createWithEqualityFn()( ); export const marketStoreSelectors = { - ...agentSelectors, ...danceSelectors, }; diff --git a/src/store/market/selectors/agent.ts b/src/store/market/selectors/agent.ts deleted file mode 100644 index 2dbe9aee..00000000 --- a/src/store/market/selectors/agent.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MarketStore } from '@/store/market'; -import { Agent } from '@/types/agent'; - -const showAgentSideBar = (s: MarketStore) => !!s.currentAgentId; - -const currentAgentItem = (s: MarketStore): Agent | undefined => { - const { currentAgentId, agentList } = s; - const currentAgent = agentList.find((item) => item.agentId === currentAgentId); - if (!currentAgent) return undefined; - - return currentAgent; -}; - -export const agentSelectors = { - currentAgentItem, - showAgentSideBar, -}; diff --git a/src/store/market/slices/agent.ts b/src/store/market/slices/agent.ts deleted file mode 100644 index c06a5b74..00000000 --- a/src/store/market/slices/agent.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { isEqual } from 'lodash-es'; -import { StateCreator } from 'zustand/vanilla'; - -import { getAgentIndex } from '@/services/agent'; -import { MarketStore } from '@/store/market'; -import { Agent } from '@/types/agent'; - -export interface AgentStore { - activateAgent: (identifier: string) => void; - agentList: Agent[]; - agentLoading: boolean; - currentAgentId: string; - deactivateAgent: () => void; - fetchAgentIndex: () => void; -} - -export const createAgentStore: StateCreator< - MarketStore, - [['zustand/devtools', never]], - [], - AgentStore -> = (set, get) => { - return { - activateAgent: (identifier) => { - set({ currentAgentId: identifier }); - }, - agentList: [], - agentLoading: false, - currentAgentId: '', - deactivateAgent: () => { - set({ currentAgentId: undefined }); - }, - fetchAgentIndex: async () => { - set({ agentLoading: true }); - try { - const { agents = [] } = await getAgentIndex(); - const { agentList } = get(); - if (!isEqual(agentList, agents)) set({ agentList: agents }); - } catch { - set({ agentList: [] }); - } finally { - set({ agentLoading: false }); - } - }, - }; -}; diff --git a/src/store/setting/initialState.ts b/src/store/setting/initialState.ts index 825e965a..19e1987c 100644 --- a/src/store/setting/initialState.ts +++ b/src/store/setting/initialState.ts @@ -13,6 +13,10 @@ const initialState: SettingState = { config: { locale: 'auto', backgroundEffect: 'glow', + tts: { + // 默认不启用客户端调用,本地调试时启用,等后续有成熟的解决方案再启用 + clientCall: false, + }, languageModel: { openAI: { apikey: '', diff --git a/src/store/setting/selectors/config.ts b/src/store/setting/selectors/config.ts index e8d3419f..20dce44f 100644 --- a/src/store/setting/selectors/config.ts +++ b/src/store/setting/selectors/config.ts @@ -2,7 +2,7 @@ import { t } from 'i18next'; import { SettingStore } from '@/store/setting'; import { GenderEnum } from '@/types/agent'; -import { OpenAIConfig, TouchConfig } from '@/types/config'; +import { OpenAIConfig, TTSConfig, TouchConfig } from '@/types/config'; import { TouchAction, TouchAreaEnum } from '@/types/touch'; const currentOpenAIConfig = (s: SettingStore): OpenAIConfig => { @@ -13,6 +13,10 @@ const currentTouchConfig = (s: SettingStore): TouchConfig => { return s.config.touch; }; +const currentTTSConfig = (s: SettingStore): TTSConfig => { + return s.config.tts; +}; + const getTouchActionsByGenderAndArea = ( s: SettingStore, gender: GenderEnum, @@ -25,5 +29,6 @@ const getTouchActionsByGenderAndArea = ( export const configSelectors = { currentOpenAIConfig, currentTouchConfig, + currentTTSConfig, getTouchActionsByGenderAndArea, }; diff --git a/src/types/config.ts b/src/types/config.ts index bf109bdc..45506c6d 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -17,6 +17,13 @@ export interface OpenAIConfig { endpoint?: string; } +export interface TTSConfig { + /** + * 是否客户端调用 + */ + clientCall: boolean; +} + export interface LanguageModelConfig { /** * OpenAI 配置 @@ -44,6 +51,10 @@ export interface Config extends CommonConfig { * 全局触摸配置 */ touch: TouchConfig; + /** + * 语音设置 + */ + tts: TTSConfig; } export interface CommonConfig { diff --git a/src/types/global.d.ts b/src/types/global.d.ts index df51054d..5028737b 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -43,3 +43,7 @@ declare module 'mmd-parser' { }[]; } } +declare module '*.glsl' { + const value: string; + export default value; +}