From 38d9d98b97fd76cc478a2731a460ff80e1820974 Mon Sep 17 00:00:00 2001 From: CanisMinor Date: Sat, 6 Sep 2025 09:51:52 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20ChatInput=20support=20rich?= =?UTF-8?q?=20text=20and=20support=20parallel=20send=20(#8964)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat: Add LobeEditor * fix tests * fix mobile --------- Co-authored-by: arvinxx --- locales/ar/chat.json | 8 +- locales/ar/editor.json | 47 +++ locales/bg-BG/chat.json | 8 +- locales/bg-BG/editor.json | 47 +++ locales/de-DE/chat.json | 8 +- locales/de-DE/editor.json | 47 +++ locales/en-US/chat.json | 8 +- locales/en-US/editor.json | 47 +++ locales/es-ES/chat.json | 8 +- locales/es-ES/editor.json | 47 +++ locales/es-ES/models.json | 4 +- locales/fa-IR/chat.json | 8 +- locales/fa-IR/editor.json | 47 +++ locales/fr-FR/chat.json | 8 +- locales/fr-FR/editor.json | 47 +++ locales/it-IT/chat.json | 8 +- locales/it-IT/editor.json | 47 +++ locales/ja-JP/chat.json | 8 +- locales/ja-JP/editor.json | 47 +++ locales/ko-KR/chat.json | 8 +- locales/ko-KR/editor.json | 47 +++ locales/ko-KR/models.json | 4 +- locales/nl-NL/chat.json | 8 +- locales/nl-NL/editor.json | 47 +++ locales/nl-NL/models.json | 4 +- locales/pl-PL/chat.json | 8 +- locales/pl-PL/editor.json | 47 +++ locales/pt-BR/chat.json | 8 +- locales/pt-BR/editor.json | 47 +++ locales/ru-RU/chat.json | 8 +- locales/ru-RU/editor.json | 47 +++ locales/tr-TR/chat.json | 8 +- locales/tr-TR/editor.json | 47 +++ locales/vi-VN/chat.json | 8 +- locales/vi-VN/editor.json | 47 +++ locales/zh-CN/chat.json | 8 +- locales/zh-CN/editor.json | 47 +++ locales/zh-TW/chat.json | 8 +- locales/zh-TW/editor.json | 47 +++ locales/zh-TW/models.json | 4 +- next.config.ts | 4 + package.json | 4 +- packages/const/src/layoutTokens.ts | 1 + packages/types/src/index.ts | 1 + packages/utils/src/index.ts | 1 + .../ChatInput/Desktop/Footer/SendMore.tsx | 104 ------ .../ChatInput/Desktop/Footer/ShortcutHint.tsx | 40 --- .../ChatInput/Desktop/Footer/index.tsx | 125 ------- .../Desktop/{Footer => }/MessageFromUrl.tsx | 5 +- .../ChatInput/Desktop/TextArea.test.tsx | 332 ------------------ .../features/ChatInput/Desktop/TextArea.tsx | 29 -- .../features/ChatInput/Desktop/index.tsx | 159 +++++++-- .../features/ChatInput/Mobile/Files/index.tsx | 33 -- .../ChatInput/Mobile/InputArea/Container.tsx | 41 --- .../ChatInput/Mobile/InputArea/index.tsx | 156 -------- .../features/ChatInput/Mobile/Send.tsx | 33 -- .../features/ChatInput/Mobile/index.tsx | 110 +++--- .../features/ChatInput/useSend.ts | 141 ++++++++ .../features/ChatList/Content.tsx | 8 +- .../InboxWelcome/QuestionSuggest.tsx | 5 +- .../WelcomeChatItem/OpeningQuestions.tsx | 5 +- .../Desktop/ChatHeader/HeaderAction.tsx | 20 +- .../ChatInput/ActionBar/STT/common.tsx | 88 +++-- .../{Topic => ActionBar/SaveTopic}/index.tsx | 19 +- .../ChatInput/ActionBar/Typo/index.tsx | 22 ++ .../ChatInput/ActionBar/components/Action.tsx | 4 + src/features/ChatInput/ActionBar/config.ts | 8 +- src/features/ChatInput/ActionBar/index.tsx | 91 +++-- src/features/ChatInput/ChatInputProvider.tsx | 54 +++ .../Desktop/FilePreview/FileItem/index.tsx | 31 +- .../Desktop/FilePreview/FileList.tsx | 31 +- .../ChatInput/Desktop/Header/index.tsx | 30 -- .../ChatInput/Desktop/InputArea/index.tsx | 143 -------- .../Desktop/__tests__/useAutoFocus.test.ts | 45 --- src/features/ChatInput/Desktop/index.tsx | 149 ++++---- .../ChatInput/Desktop/useAutoFocus.ts | 13 - src/features/ChatInput/InputEditor/index.tsx | 134 +++++++ .../Mobile/FilePreview}/FileItem/File.tsx | 3 +- .../Mobile/FilePreview}/FileItem/Image.tsx | 0 .../Mobile/FilePreview}/FileItem/index.tsx | 0 .../Mobile/FilePreview}/FileItem/style.ts | 0 .../ChatInput/Mobile/FilePreview/index.tsx | 44 +++ src/features/ChatInput/Mobile/index.tsx | 72 ++++ .../ChatInput/SendArea/ExpandButton.tsx | 30 ++ .../ChatInput/SendArea/SendButton.tsx | 29 ++ .../ChatInput/SendArea/ShortcutHint.tsx | 52 +++ src/features/ChatInput/SendArea/index.tsx | 36 ++ src/features/ChatInput/StoreUpdater.tsx | 41 +++ src/features/ChatInput/TypoBar/index.tsx | 139 ++++++++ .../ChatInput/hooks/useChatInputEditor.ts | 36 ++ src/features/ChatInput/index.ts | 7 + src/features/ChatInput/store/action.ts | 75 ++++ src/features/ChatInput/store/index.ts | 23 ++ src/features/ChatInput/store/initialState.ts | 54 +++ src/features/ChatInput/store/selectors.ts | 5 + src/features/ChatInput/useSend.ts | 102 ------ .../components/BackBottom/style.ts | 2 +- .../Conversation/components/SkeletonList.tsx | 13 +- .../components/VirtualizedList/index.tsx | 97 ++--- .../components/WideScreenContainer/index.tsx | 43 +++ .../Portal/Thread/Chat/ChatInput/Footer.tsx | 90 ----- .../Portal/Thread/Chat/ChatInput/TextArea.tsx | 30 -- .../Portal/Thread/Chat/ChatInput/index.tsx | 91 ++--- .../Portal/Thread/Chat/ChatInput/useSend.ts | 70 ++-- src/features/Portal/Thread/Chat/index.tsx | 4 +- src/features/Portal/Thread/Header/index.tsx | 2 +- src/hooks/useHotkeys/chatScope.ts | 8 +- src/layout/GlobalProvider/Editor.tsx | 27 ++ src/layout/GlobalProvider/Locale.tsx | 26 +- src/libs/trpc/client/types.ts | 18 - src/locales/default/chat.ts | 9 +- src/locales/default/editor.ts | 47 +++ src/locales/default/index.ts | 2 + src/services/aiChat.ts | 10 +- .../__tests__/generateAIChatV2.test.ts | 75 ++-- .../slices/aiChat/actions/generateAIChatV2.ts | 209 +++++++++-- src/store/chat/slices/aiChat/initialState.ts | 19 + src/store/chat/slices/aiChat/selectors.ts | 18 + src/store/global/action.test.ts | 11 +- .../global/actions/__tests__/general.test.ts | 12 +- src/store/global/actions/workspacePane.ts | 6 + src/store/global/initialState.ts | 6 +- .../global/selectors/systemStatus.test.ts | 3 +- src/store/global/selectors/systemStatus.ts | 7 +- 124 files changed, 2985 insertions(+), 1943 deletions(-) create mode 100644 locales/ar/editor.json create mode 100644 locales/bg-BG/editor.json create mode 100644 locales/de-DE/editor.json create mode 100644 locales/en-US/editor.json create mode 100644 locales/es-ES/editor.json create mode 100644 locales/fa-IR/editor.json create mode 100644 locales/fr-FR/editor.json create mode 100644 locales/it-IT/editor.json create mode 100644 locales/ja-JP/editor.json create mode 100644 locales/ko-KR/editor.json create mode 100644 locales/nl-NL/editor.json create mode 100644 locales/pl-PL/editor.json create mode 100644 locales/pt-BR/editor.json create mode 100644 locales/ru-RU/editor.json create mode 100644 locales/tr-TR/editor.json create mode 100644 locales/vi-VN/editor.json create mode 100644 locales/zh-CN/editor.json create mode 100644 locales/zh-TW/editor.json delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/ShortcutHint.tsx delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx rename src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/{Footer => }/MessageFromUrl.tsx (87%) delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/index.tsx delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/Container.tsx delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/index.tsx delete mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Send.tsx create mode 100644 src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts rename src/features/ChatInput/{Topic => ActionBar/SaveTopic}/index.tsx (83%) create mode 100644 src/features/ChatInput/ActionBar/Typo/index.tsx create mode 100644 src/features/ChatInput/ChatInputProvider.tsx delete mode 100644 src/features/ChatInput/Desktop/Header/index.tsx delete mode 100644 src/features/ChatInput/Desktop/InputArea/index.tsx delete mode 100644 src/features/ChatInput/Desktop/__tests__/useAutoFocus.test.ts delete mode 100644 src/features/ChatInput/Desktop/useAutoFocus.ts create mode 100644 src/features/ChatInput/InputEditor/index.tsx rename src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files => features/ChatInput/Mobile/FilePreview}/FileItem/File.tsx (94%) rename src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files => features/ChatInput/Mobile/FilePreview}/FileItem/Image.tsx (100%) rename src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files => features/ChatInput/Mobile/FilePreview}/FileItem/index.tsx (100%) rename src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files => features/ChatInput/Mobile/FilePreview}/FileItem/style.ts (100%) create mode 100644 src/features/ChatInput/Mobile/FilePreview/index.tsx create mode 100644 src/features/ChatInput/Mobile/index.tsx create mode 100644 src/features/ChatInput/SendArea/ExpandButton.tsx create mode 100644 src/features/ChatInput/SendArea/SendButton.tsx create mode 100644 src/features/ChatInput/SendArea/ShortcutHint.tsx create mode 100644 src/features/ChatInput/SendArea/index.tsx create mode 100644 src/features/ChatInput/StoreUpdater.tsx create mode 100644 src/features/ChatInput/TypoBar/index.tsx create mode 100644 src/features/ChatInput/hooks/useChatInputEditor.ts create mode 100644 src/features/ChatInput/index.ts create mode 100644 src/features/ChatInput/store/action.ts create mode 100644 src/features/ChatInput/store/index.ts create mode 100644 src/features/ChatInput/store/initialState.ts create mode 100644 src/features/ChatInput/store/selectors.ts delete mode 100644 src/features/ChatInput/useSend.ts create mode 100644 src/features/Conversation/components/WideScreenContainer/index.tsx delete mode 100644 src/features/Portal/Thread/Chat/ChatInput/Footer.tsx delete mode 100644 src/features/Portal/Thread/Chat/ChatInput/TextArea.tsx create mode 100644 src/layout/GlobalProvider/Editor.tsx delete mode 100644 src/libs/trpc/client/types.ts create mode 100644 src/locales/default/editor.ts diff --git a/locales/ar/chat.json b/locales/ar/chat.json index d001ff467e..4af2dea3d4 100644 --- a/locales/ar/chat.json +++ b/locales/ar/chat.json @@ -72,8 +72,8 @@ "addUser": "إضافة رسالة مستخدم", "more": "المزيد", "send": "إرسال", - "sendWithCmdEnter": "اضغط {{meta}} + Enter للإرسال", - "sendWithEnter": "اضغط Enter للإرسال", + "sendWithCmdEnter": "اضغط للإرسال", + "sendWithEnter": "اضغط للإرسال", "stop": "توقف", "warp": "تغيير السطر" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} رسالة", "title": "موضوع فرعي" }, + "toggleWideScreen": { + "off": "إيقاف وضع الشاشة العريضة", + "on": "تشغيل وضع الشاشة العريضة" + }, "tokenDetails": { "chats": "رسائل المحادثة", "historySummary": "ملخص التاريخ", diff --git a/locales/ar/editor.json b/locales/ar/editor.json new file mode 100644 index 0000000000..8295cdfac1 --- /dev/null +++ b/locales/ar/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "طي", + "on": "توسيع" + }, + "typobar": { + "off": "إخفاء شريط أدوات التنسيق", + "on": "إظهار شريط أدوات التنسيق" + } + }, + "file": { + "error": "خطأ: {{message}}", + "uploading": "جاري رفع الملف..." + }, + "image": { + "broken": "الصورة تالفة" + }, + "link": { + "edit": "تعديل الرابط", + "open": "فتح الرابط", + "placeholder": "أدخل عنوان URL للرابط", + "unlink": "إزالة الرابط" + }, + "table": { + "delete": "حذف الجدول", + "deleteColumn": "حذف العمود", + "deleteRow": "حذف الصف", + "insertColumnLeft": "إدراج {{count}} عمودًا إلى اليسار", + "insertColumnRight": "إدراج {{count}} عمودًا إلى اليمين", + "insertRowAbove": "إدراج {{count}} صفًا في الأعلى", + "insertRowBelow": "إدراج {{count}} صفًا في الأسفل" + }, + "typobar": { + "blockquote": "اقتباس", + "bold": "غامق", + "bulletList": "قائمة نقطية", + "code": "كود مضمن", + "codeblock": "كتلة كود", + "italic": "مائل", + "link": "رابط", + "numberList": "قائمة مرقمة", + "strikethrough": "شطب", + "table": "إدراج جدول", + "underline": "تسطير" + } +} diff --git a/locales/bg-BG/chat.json b/locales/bg-BG/chat.json index 22e6832ebe..74b7e39d0d 100644 --- a/locales/bg-BG/chat.json +++ b/locales/bg-BG/chat.json @@ -72,8 +72,8 @@ "addUser": "Добави потребителско съобщение", "more": "още", "send": "Изпрати", - "sendWithCmdEnter": "Натисни {{meta}} + Enter за да изпратиш", - "sendWithEnter": "Натисни Enter за да изпратиш", + "sendWithCmdEnter": "Натиснете за изпращане", + "sendWithEnter": "Натиснете за изпращане", "stop": "Спри", "warp": "Нов ред" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} съобщения", "title": "Подтема" }, + "toggleWideScreen": { + "off": "Изключване на широк екран", + "on": "Включване на широк екран" + }, "tokenDetails": { "chats": "Чат съобщения", "historySummary": "Историческо резюме", diff --git a/locales/bg-BG/editor.json b/locales/bg-BG/editor.json new file mode 100644 index 0000000000..09efac468a --- /dev/null +++ b/locales/bg-BG/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Сгъни", + "on": "Разгъни" + }, + "typobar": { + "off": "Скрий лентата за форматиране", + "on": "Покажи лентата за форматиране" + } + }, + "file": { + "error": "Грешка: {{message}}", + "uploading": "Качване на файл..." + }, + "image": { + "broken": "Изображението е повредено" + }, + "link": { + "edit": "Редактирай връзката", + "open": "Отвори връзката", + "placeholder": "Въведете URL адрес на връзката", + "unlink": "Премахни връзката" + }, + "table": { + "delete": "Премахни таблицата", + "deleteColumn": "Премахни колоната", + "deleteRow": "Премахни реда", + "insertColumnLeft": "Вмъкни {{count}} колони отляво", + "insertColumnRight": "Вмъкни {{count}} колони отдясно", + "insertRowAbove": "Вмъкни {{count}} реда отгоре", + "insertRowBelow": "Вмъкни {{count}} реда отдолу" + }, + "typobar": { + "blockquote": "Цитат", + "bold": "Удебели", + "bulletList": "Маркиран списък", + "code": "Код в реда", + "codeblock": "Блок с код", + "italic": "Курсив", + "link": "Връзка", + "numberList": "Номериран списък", + "strikethrough": "Зачеркване", + "table": "Вмъкване на таблица", + "underline": "Подчертаване" + } +} diff --git a/locales/de-DE/chat.json b/locales/de-DE/chat.json index 7c5e685fdb..7a46371aef 100644 --- a/locales/de-DE/chat.json +++ b/locales/de-DE/chat.json @@ -72,8 +72,8 @@ "addUser": "Fügen Sie eine Benutzer-Nachricht hinzu", "more": "Mehr", "send": "Senden", - "sendWithCmdEnter": "Mit {{meta}} + Eingabetaste senden", - "sendWithEnter": "Mit Eingabetaste senden", + "sendWithCmdEnter": "Drücken Sie , um zu senden", + "sendWithEnter": "Drücken Sie , um zu senden", "stop": "Stoppen", "warp": "Zeilenumbruch" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} Nachrichten", "title": "Unterthema" }, + "toggleWideScreen": { + "off": "Breitbildmodus deaktivieren", + "on": "Breitbildmodus aktivieren" + }, "tokenDetails": { "chats": "Chats", "historySummary": "Historische Zusammenfassung", diff --git a/locales/de-DE/editor.json b/locales/de-DE/editor.json new file mode 100644 index 0000000000..14fb5dc214 --- /dev/null +++ b/locales/de-DE/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Einklappen", + "on": "Ausklappen" + }, + "typobar": { + "off": "Formatierungsleiste ausblenden", + "on": "Formatierungsleiste anzeigen" + } + }, + "file": { + "error": "Fehler: {{message}}", + "uploading": "Datei wird hochgeladen..." + }, + "image": { + "broken": "Bild beschädigt" + }, + "link": { + "edit": "Link bearbeiten", + "open": "Link öffnen", + "placeholder": "Link-URL eingeben", + "unlink": "Link entfernen" + }, + "table": { + "delete": "Tabelle löschen", + "deleteColumn": "Spalte löschen", + "deleteRow": "Zeile löschen", + "insertColumnLeft": "Links {{count}} Spalten einfügen", + "insertColumnRight": "Rechts {{count}} Spalten einfügen", + "insertRowAbove": "Oben {{count}} Zeilen einfügen", + "insertRowBelow": "Unten {{count}} Zeilen einfügen" + }, + "typobar": { + "blockquote": "Zitat", + "bold": "Fett", + "bulletList": "Ungeordnete Liste", + "code": "Inline-Code", + "codeblock": "Codeblock", + "italic": "Kursiv", + "link": "Link", + "numberList": "Nummerierte Liste", + "strikethrough": "Durchgestrichen", + "table": "Tabelle einfügen", + "underline": "Unterstrichen" + } +} diff --git a/locales/en-US/chat.json b/locales/en-US/chat.json index 2c4496c405..95a34cf1c9 100644 --- a/locales/en-US/chat.json +++ b/locales/en-US/chat.json @@ -72,8 +72,8 @@ "addUser": "Add a user message", "more": "more", "send": "Send", - "sendWithCmdEnter": "Press {{meta}} + Enter to send", - "sendWithEnter": "Press Enter to send", + "sendWithCmdEnter": "Press to send", + "sendWithEnter": "Press to send", "stop": "Stop", "warp": "New Line" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} messages", "title": "Subtopic" }, + "toggleWideScreen": { + "off": "Turn off widescreen mode", + "on": "Turn on widescreen mode" + }, "tokenDetails": { "chats": "Chat Messages", "historySummary": "History Summary", diff --git a/locales/en-US/editor.json b/locales/en-US/editor.json new file mode 100644 index 0000000000..211926b8b8 --- /dev/null +++ b/locales/en-US/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Collapse", + "on": "Expand" + }, + "typobar": { + "off": "Hide formatting toolbar", + "on": "Show formatting toolbar" + } + }, + "file": { + "error": "Error: {{message}}", + "uploading": "Uploading file..." + }, + "image": { + "broken": "Image is corrupted" + }, + "link": { + "edit": "Edit link", + "open": "Open link", + "placeholder": "Enter link URL", + "unlink": "Unlink" + }, + "table": { + "delete": "Delete table", + "deleteColumn": "Delete column", + "deleteRow": "Delete row", + "insertColumnLeft": "Insert {{count}} column(s) to the left", + "insertColumnRight": "Insert {{count}} column(s) to the right", + "insertRowAbove": "Insert {{count}} row(s) above", + "insertRowBelow": "Insert {{count}} row(s) below" + }, + "typobar": { + "blockquote": "Blockquote", + "bold": "Bold", + "bulletList": "Bulleted list", + "code": "Inline code", + "codeblock": "Code block", + "italic": "Italic", + "link": "Link", + "numberList": "Numbered list", + "strikethrough": "Strikethrough", + "table": "Insert table", + "underline": "Underline" + } +} diff --git a/locales/es-ES/chat.json b/locales/es-ES/chat.json index 16af691273..2426157ba4 100644 --- a/locales/es-ES/chat.json +++ b/locales/es-ES/chat.json @@ -72,8 +72,8 @@ "addUser": "Agregar un mensaje de usuario", "more": "más", "send": "Enviar", - "sendWithCmdEnter": "Enviar con {{meta}} + Enter", - "sendWithEnter": "Enviar con Enter", + "sendWithCmdEnter": "Presiona para enviar", + "sendWithEnter": "Presiona para enviar", "stop": "Detener", "warp": "Salto de línea" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} mensajes", "title": "Subtema" }, + "toggleWideScreen": { + "off": "Desactivar modo de pantalla ancha", + "on": "Activar modo de pantalla ancha" + }, "tokenDetails": { "chats": "Mensajes de chat", "historySummary": "Resumen histórico", diff --git a/locales/es-ES/editor.json b/locales/es-ES/editor.json new file mode 100644 index 0000000000..07ed2ba8ea --- /dev/null +++ b/locales/es-ES/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Contraer", + "on": "Expandir" + }, + "typobar": { + "off": "Ocultar barra de herramientas de formato", + "on": "Mostrar barra de herramientas de formato" + } + }, + "file": { + "error": "Error: {{message}}", + "uploading": "Subiendo archivo..." + }, + "image": { + "broken": "Imagen dañada" + }, + "link": { + "edit": "Editar enlace", + "open": "Abrir enlace", + "placeholder": "Introduce la URL del enlace", + "unlink": "Quitar enlace" + }, + "table": { + "delete": "Eliminar tabla", + "deleteColumn": "Eliminar columna", + "deleteRow": "Eliminar fila", + "insertColumnLeft": "Insertar {{count}} columna(s) a la izquierda", + "insertColumnRight": "Insertar {{count}} columna(s) a la derecha", + "insertRowAbove": "Insertar {{count}} fila(s) encima", + "insertRowBelow": "Insertar {{count}} fila(s) debajo" + }, + "typobar": { + "blockquote": "Cita", + "bold": "Negrita", + "bulletList": "Lista desordenada", + "code": "Código en línea", + "codeblock": "Bloque de código", + "italic": "Cursiva", + "link": "Enlace", + "numberList": "Lista ordenada", + "strikethrough": "Tachado", + "table": "Insertar tabla", + "underline": "Subrayado" + } +} diff --git a/locales/es-ES/models.json b/locales/es-ES/models.json index 3753631872..88e26be34d 100644 --- a/locales/es-ES/models.json +++ b/locales/es-ES/models.json @@ -1505,7 +1505,9 @@ "gpt-4.1-nano": { "description": "GPT-4.1 mini ofrece un equilibrio entre inteligencia, velocidad y costo, lo que lo convierte en un modelo atractivo para muchos casos de uso." }, - "gpt-4.5-preview": "GPT-4.5-preview es el modelo de propósito general más reciente, con un profundo conocimiento del mundo y una mejor comprensión de las intenciones de los usuarios; destaca en tareas creativas y en la planificación de agentes. El conocimiento de este modelo está actualizado hasta octubre de 2023.", + "gpt-4.5-preview": { + "description": "GPT-4.5-preview es el modelo de propósito general más reciente, con un profundo conocimiento del mundo y una mejor comprensión de las intenciones de los usuarios; destaca en tareas creativas y en la planificación de agentes. El conocimiento de este modelo está actualizado hasta octubre de 2023." + }, "gpt-4o": { "description": "ChatGPT-4o es un modelo dinámico que se actualiza en tiempo real para mantener la versión más actual. Combina una poderosa comprensión y generación de lenguaje, adecuado para aplicaciones a gran escala, incluyendo servicio al cliente, educación y soporte técnico." }, diff --git a/locales/fa-IR/chat.json b/locales/fa-IR/chat.json index c8950a236b..3e4cdf8127 100644 --- a/locales/fa-IR/chat.json +++ b/locales/fa-IR/chat.json @@ -72,8 +72,8 @@ "addUser": "افزودن یک پیام کاربر", "more": "بیشتر", "send": "ارسال", - "sendWithCmdEnter": "فشار دهید {{meta}} + Enter برای ارسال", - "sendWithEnter": "فشار دهید Enter برای ارسال", + "sendWithCmdEnter": "برای ارسال، کلید را فشار دهید", + "sendWithEnter": "برای ارسال، کلید را فشار دهید", "stop": "توقف", "warp": "خط جدید" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} پیام", "title": "زیرموضوع" }, + "toggleWideScreen": { + "off": "حالت صفحه‌نمایش عریض را غیرفعال کنید", + "on": "حالت صفحه‌نمایش عریض را فعال کنید" + }, "tokenDetails": { "chats": "پیام‌های گفتگو", "historySummary": "خلاصه تاریخ", diff --git a/locales/fa-IR/editor.json b/locales/fa-IR/editor.json new file mode 100644 index 0000000000..6ffbd7cd69 --- /dev/null +++ b/locales/fa-IR/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "جمع کردن", + "on": "باز کردن" + }, + "typobar": { + "off": "مخفی کردن نوار ابزار قالب‌بندی", + "on": "نمایش نوار ابزار قالب‌بندی" + } + }, + "file": { + "error": "خطا: {{message}}", + "uploading": "در حال بارگذاری فایل..." + }, + "image": { + "broken": "تصویر خراب است" + }, + "link": { + "edit": "ویرایش پیوند", + "open": "باز کردن پیوند", + "placeholder": "آدرس URL پیوند را وارد کنید", + "unlink": "حذف پیوند" + }, + "table": { + "delete": "حذف جدول", + "deleteColumn": "حذف ستون", + "deleteRow": "حذف ردیف", + "insertColumnLeft": "درج {{count}} ستون در سمت چپ", + "insertColumnRight": "درج {{count}} ستون در سمت راست", + "insertRowAbove": "درج {{count}} ردیف در بالا", + "insertRowBelow": "درج {{count}} ردیف در پایین" + }, + "typobar": { + "blockquote": "نقل قول", + "bold": "پررنگ", + "bulletList": "فهرست نشانه‌دار", + "code": "کد درون‌خطی", + "codeblock": "بلوک کد", + "italic": "ایتالیک", + "link": "پیوند", + "numberList": "فهرست شماره‌دار", + "strikethrough": "خط خورده", + "table": "درج جدول", + "underline": "زیرخط" + } +} diff --git a/locales/fr-FR/chat.json b/locales/fr-FR/chat.json index f9000eb21f..65f2174ead 100644 --- a/locales/fr-FR/chat.json +++ b/locales/fr-FR/chat.json @@ -72,8 +72,8 @@ "addUser": "Ajouter un message utilisateur", "more": "Plus", "send": "Envoyer", - "sendWithCmdEnter": "Envoyer avec {{meta}} + Entrée", - "sendWithEnter": "Envoyer avec Entrée", + "sendWithCmdEnter": "Appuyez sur pour envoyer", + "sendWithEnter": "Appuyez sur pour envoyer", "stop": "Arrêter", "warp": "Saut de ligne" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} messages", "title": "Sous-sujet" }, + "toggleWideScreen": { + "off": "Désactiver le mode écran large", + "on": "Activer le mode écran large" + }, "tokenDetails": { "chats": "Messages de discussion", "historySummary": "Résumé historique", diff --git a/locales/fr-FR/editor.json b/locales/fr-FR/editor.json new file mode 100644 index 0000000000..dda80e9553 --- /dev/null +++ b/locales/fr-FR/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Réduire", + "on": "Développer" + }, + "typobar": { + "off": "Masquer la barre d'outils de formatage", + "on": "Afficher la barre d'outils de formatage" + } + }, + "file": { + "error": "Erreur : {{message}}", + "uploading": "Téléversement du fichier en cours..." + }, + "image": { + "broken": "Image endommagée" + }, + "link": { + "edit": "Modifier le lien", + "open": "Ouvrir le lien", + "placeholder": "Entrez l'URL du lien", + "unlink": "Supprimer le lien" + }, + "table": { + "delete": "Supprimer le tableau", + "deleteColumn": "Supprimer la colonne", + "deleteRow": "Supprimer la ligne", + "insertColumnLeft": "Insérer {{count}} colonne(s) à gauche", + "insertColumnRight": "Insérer {{count}} colonne(s) à droite", + "insertRowAbove": "Insérer {{count}} ligne(s) au-dessus", + "insertRowBelow": "Insérer {{count}} ligne(s) en dessous" + }, + "typobar": { + "blockquote": "Citation", + "bold": "Gras", + "bulletList": "Liste à puces", + "code": "Code en ligne", + "codeblock": "Bloc de code", + "italic": "Italique", + "link": "Lien", + "numberList": "Liste numérotée", + "strikethrough": "Barré", + "table": "Insérer un tableau", + "underline": "Souligné" + } +} diff --git a/locales/it-IT/chat.json b/locales/it-IT/chat.json index 469de83b3d..baae9a2e6d 100644 --- a/locales/it-IT/chat.json +++ b/locales/it-IT/chat.json @@ -72,8 +72,8 @@ "addUser": "Aggiungi un messaggio utente", "more": "Ulteriori", "send": "Invia", - "sendWithCmdEnter": "Invia premendo {{meta}} + Invio", - "sendWithEnter": "Invia premendo Invio", + "sendWithCmdEnter": "Premi per inviare", + "sendWithEnter": "Premi per inviare", "stop": "Ferma", "warp": "A capo" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} messaggi", "title": "Sottoargomento" }, + "toggleWideScreen": { + "off": "Disattiva modalità schermo ampio", + "on": "Attiva modalità schermo ampio" + }, "tokenDetails": { "chats": "Chat", "historySummary": "Riepilogo storico", diff --git a/locales/it-IT/editor.json b/locales/it-IT/editor.json new file mode 100644 index 0000000000..7ca65dd2b4 --- /dev/null +++ b/locales/it-IT/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Comprimi", + "on": "Espandi" + }, + "typobar": { + "off": "Nascondi barra degli strumenti di formattazione", + "on": "Mostra barra degli strumenti di formattazione" + } + }, + "file": { + "error": "Errore: {{message}}", + "uploading": "Caricamento file in corso..." + }, + "image": { + "broken": "Immagine danneggiata" + }, + "link": { + "edit": "Modifica link", + "open": "Apri link", + "placeholder": "Inserisci URL del link", + "unlink": "Rimuovi link" + }, + "table": { + "delete": "Elimina tabella", + "deleteColumn": "Elimina colonna", + "deleteRow": "Elimina riga", + "insertColumnLeft": "Inserisci {{count}} colonne a sinistra", + "insertColumnRight": "Inserisci {{count}} colonne a destra", + "insertRowAbove": "Inserisci {{count}} righe sopra", + "insertRowBelow": "Inserisci {{count}} righe sotto" + }, + "typobar": { + "blockquote": "Citazione", + "bold": "Grassetto", + "bulletList": "Elenco puntato", + "code": "Codice in linea", + "codeblock": "Blocco di codice", + "italic": "Corsivo", + "link": "Collegamento", + "numberList": "Elenco numerato", + "strikethrough": "Testo barrato", + "table": "Inserisci tabella", + "underline": "Sottolineato" + } +} diff --git a/locales/ja-JP/chat.json b/locales/ja-JP/chat.json index d44f9dbcf0..83693010b4 100644 --- a/locales/ja-JP/chat.json +++ b/locales/ja-JP/chat.json @@ -72,8 +72,8 @@ "addUser": "ユーザーメッセージを追加", "more": "もっと", "send": "送信", - "sendWithCmdEnter": "{{meta}} + Enter キーで送信", - "sendWithEnter": "Enter キーで送信", + "sendWithCmdEnter": " キーを押して送信", + "sendWithEnter": " キーを押して送信", "stop": "停止", "warp": "改行" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} 件のメッセージ", "title": "サブトピック" }, + "toggleWideScreen": { + "off": "ワイドスクリーンモードをオフにする", + "on": "ワイドスクリーンモードをオンにする" + }, "tokenDetails": { "chats": "チャットメッセージ", "historySummary": "履歴の要約", diff --git a/locales/ja-JP/editor.json b/locales/ja-JP/editor.json new file mode 100644 index 0000000000..445c34a1cb --- /dev/null +++ b/locales/ja-JP/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "折りたたむ", + "on": "展開する" + }, + "typobar": { + "off": "書式ツールバーを非表示", + "on": "書式ツールバーを表示" + } + }, + "file": { + "error": "エラー:{{message}}", + "uploading": "ファイルをアップロードしています..." + }, + "image": { + "broken": "画像が破損しています" + }, + "link": { + "edit": "リンクを編集", + "open": "リンクを開く", + "placeholder": "リンクの URL を入力", + "unlink": "リンクを解除" + }, + "table": { + "delete": "表を削除", + "deleteColumn": "列を削除", + "deleteRow": "行を削除", + "insertColumnLeft": "左側に {{count}} 列を挿入", + "insertColumnRight": "右側に {{count}} 列を挿入", + "insertRowAbove": "上に {{count}} 行を挿入", + "insertRowBelow": "下に {{count}} 行を挿入" + }, + "typobar": { + "blockquote": "引用", + "bold": "太字", + "bulletList": "番号なしリスト", + "code": "インラインコード", + "codeblock": "コードブロック", + "italic": "斜体", + "link": "リンク", + "numberList": "番号付きリスト", + "strikethrough": "取り消し線", + "table": "表を挿入", + "underline": "下線" + } +} diff --git a/locales/ko-KR/chat.json b/locales/ko-KR/chat.json index 47086fd945..a1ff8b46cd 100644 --- a/locales/ko-KR/chat.json +++ b/locales/ko-KR/chat.json @@ -72,8 +72,8 @@ "addUser": "사용자 메시지 추가", "more": "더 많은", "send": "전송", - "sendWithCmdEnter": "{{meta}} + Enter 키로 전송", - "sendWithEnter": "Enter 키로 전송", + "sendWithCmdEnter": " 키를 눌러 전송", + "sendWithEnter": " 키를 눌러 전송", "stop": "중지", "warp": "줄바꿈" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}}개의 메시지", "title": "하위 주제" }, + "toggleWideScreen": { + "off": "와이드 스크린 모드 끄기", + "on": "와이드 스크린 모드 켜기" + }, "tokenDetails": { "chats": "채팅 메시지", "historySummary": "역사 요약", diff --git a/locales/ko-KR/editor.json b/locales/ko-KR/editor.json new file mode 100644 index 0000000000..9ef4a0abdb --- /dev/null +++ b/locales/ko-KR/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "접기", + "on": "펼치기" + }, + "typobar": { + "off": "서식 도구 모음 숨기기", + "on": "서식 도구 모음 표시" + } + }, + "file": { + "error": "오류: {{message}}", + "uploading": "파일 업로드 중..." + }, + "image": { + "broken": "이미지가 손상되었습니다" + }, + "link": { + "edit": "링크 편집", + "open": "링크 열기", + "placeholder": "링크 URL 입력", + "unlink": "링크 제거" + }, + "table": { + "delete": "테이블 삭제", + "deleteColumn": "열 삭제", + "deleteRow": "행 삭제", + "insertColumnLeft": "왼쪽에 {{count}}개의 열 삽입", + "insertColumnRight": "오른쪽에 {{count}}개의 열 삽입", + "insertRowAbove": "위에 {{count}}개의 행 삽입", + "insertRowBelow": "아래에 {{count}}개의 행 삽입" + }, + "typobar": { + "blockquote": "인용", + "bold": "굵게", + "bulletList": "순서 없는 목록", + "code": "인라인 코드", + "codeblock": "코드 블록", + "italic": "기울임꼴", + "link": "링크", + "numberList": "번호 매긴 목록", + "strikethrough": "취소선", + "table": "표 삽입", + "underline": "밑줄" + } +} diff --git a/locales/ko-KR/models.json b/locales/ko-KR/models.json index 65dea6dde9..50e04235c3 100644 --- a/locales/ko-KR/models.json +++ b/locales/ko-KR/models.json @@ -1505,7 +1505,9 @@ "gpt-4.1-nano": { "description": "GPT-4.1 mini는 지능, 속도 및 비용 간의 균형을 제공하여 많은 사용 사례에서 매력적인 모델이 됩니다." }, - "gpt-4.5-preview": "GPT-4.5-preview는 최신 범용 모델로, 폭넓은 세계 지식과 사용자 의도에 대한 향상된 이해를 갖추고 있어 창의적 과제와 에이전트 계획에 능숙합니다. 이 모델의 지식은 2023년 10월까지입니다.", + "gpt-4.5-preview": { + "description": "GPT-4.5-preview는 최신 범용 모델로, 폭넓은 세계 지식과 사용자 의도에 대한 향상된 이해를 갖추고 있어 창의적 과제와 에이전트 계획에 능숙합니다. 이 모델의 지식은 2023년 10월까지입니다." + }, "gpt-4o": { "description": "ChatGPT-4o는 동적 모델로, 최신 버전을 유지하기 위해 실시간으로 업데이트됩니다. 강력한 언어 이해 및 생성 능력을 결합하여 고객 서비스, 교육 및 기술 지원을 포함한 대규모 응용 프로그램에 적합합니다." }, diff --git a/locales/nl-NL/chat.json b/locales/nl-NL/chat.json index b75603d51c..8d909af9b9 100644 --- a/locales/nl-NL/chat.json +++ b/locales/nl-NL/chat.json @@ -72,8 +72,8 @@ "addUser": "Voeg een gebruikersbericht toe", "more": "Meer", "send": "Verzenden", - "sendWithCmdEnter": "Verzenden met {{meta}} + Enter", - "sendWithEnter": "Verzenden met Enter", + "sendWithCmdEnter": "Druk op om te verzenden", + "sendWithEnter": "Druk op om te verzenden", "stop": "Stoppen", "warp": "Nieuwe regel" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} berichten", "title": "Subonderwerp" }, + "toggleWideScreen": { + "off": "Schakel breedbeeldmodus uit", + "on": "Schakel breedbeeldmodus in" + }, "tokenDetails": { "chats": "Chats", "historySummary": "Geschiedenis samenvatting", diff --git a/locales/nl-NL/editor.json b/locales/nl-NL/editor.json new file mode 100644 index 0000000000..9068c949e1 --- /dev/null +++ b/locales/nl-NL/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Inklappen", + "on": "Uitklappen" + }, + "typobar": { + "off": "Verberg opmaakwerkbalk", + "on": "Toon opmaakwerkbalk" + } + }, + "file": { + "error": "Fout: {{message}}", + "uploading": "Bestand wordt geüpload..." + }, + "image": { + "broken": "Afbeelding beschadigd" + }, + "link": { + "edit": "Link bewerken", + "open": "Link openen", + "placeholder": "Voer de URL van de link in", + "unlink": "Link verwijderen" + }, + "table": { + "delete": "Tabel verwijderen", + "deleteColumn": "Kolom verwijderen", + "deleteRow": "Rij verwijderen", + "insertColumnLeft": "Voeg {{count}} kolommen links in", + "insertColumnRight": "Voeg {{count}} kolommen rechts in", + "insertRowAbove": "Voeg {{count}} rijen hierboven in", + "insertRowBelow": "Voeg {{count}} rijen hieronder in" + }, + "typobar": { + "blockquote": "Citaat", + "bold": "Vet", + "bulletList": "Opsomming", + "code": "Inlinecode", + "codeblock": "Codeblok", + "italic": "Cursief", + "link": "Link", + "numberList": "Genummerde lijst", + "strikethrough": "Doorhalen", + "table": "Tabel invoegen", + "underline": "Onderstrepen" + } +} diff --git a/locales/nl-NL/models.json b/locales/nl-NL/models.json index 401f39d752..c15f8ae6fd 100644 --- a/locales/nl-NL/models.json +++ b/locales/nl-NL/models.json @@ -2663,7 +2663,9 @@ "step-r1-v-mini": { "description": "Dit model is een krachtig redeneringsmodel met sterke beeldbegripcapaciteiten, in staat om beeld- en tekstinformatie te verwerken en tekstinhoud te genereren na diep nadenken. Dit model presteert uitstekend in visuele redenering en heeft eersteklas wiskundige, code- en tekstredeneringscapaciteiten. De contextlengte is 100k." }, - "stepfun-ai/step3": "Step3 is een geavanceerd multimodaal redeneermodel uitgebracht door StepFun (阶跃星辰). Het is gebouwd op een Mixture-of-Experts (MoE)-architectuur met in totaal 321 miljard (321B) parameters en 38 miljard (38B) actieve parameters. Het model heeft een end-to-end ontwerp dat gericht is op het minimaliseren van decodeerkosten, terwijl het topniveau-prestaties levert bij vision-language redenering. Dankzij de synergie tussen Multi-Matrix Factorized Attention (MFA) en Attention-FFN Decoupling (AFD) behoudt Step3 uitstekende efficiëntie zowel op high-end als low-end accelerators. Tijdens de voortraining verwerkte Step3 meer dan 20 biljoen (20T) teksttokens en 4 biljoen (4T) gecombineerde beeld-tekst-tokens, en bestrijkt daarmee meer dan tien talen. Het model behaalt leidende resultaten onder open-sourcemodellen op verschillende benchmarks, waaronder wiskunde, code en multimodaal.", + "stepfun-ai/step3": { + "description": "Step3 is een geavanceerd multimodaal redeneermodel uitgebracht door StepFun (阶跃星辰). Het is gebouwd op een Mixture-of-Experts (MoE)-architectuur met in totaal 321 miljard (321B) parameters en 38 miljard (38B) actieve parameters. Het model heeft een end-to-end ontwerp dat gericht is op het minimaliseren van decodeerkosten, terwijl het topniveau-prestaties levert bij vision-language redenering. Dankzij de synergie tussen Multi-Matrix Factorized Attention (MFA) en Attention-FFN Decoupling (AFD) behoudt Step3 uitstekende efficiëntie zowel op high-end als low-end accelerators. Tijdens de voortraining verwerkte Step3 meer dan 20 biljoen (20T) teksttokens en 4 biljoen (4T) gecombineerde beeld-tekst-tokens, en bestrijkt daarmee meer dan tien talen. Het model behaalt leidende resultaten onder open-sourcemodellen op verschillende benchmarks, waaronder wiskunde, code en multimodaal." + }, "taichu_llm": { "description": "Het Zido Tai Chu-taalmodel heeft een sterke taalbegripcapaciteit en kan tekstcreatie, kennisvragen, codeprogrammering, wiskundige berekeningen, logische redenering, sentimentanalyse, tekstsamenvattingen en meer aan. Het combineert innovatief grote data voortraining met rijke kennis uit meerdere bronnen, door algoritmische technologie continu te verfijnen en voortdurend nieuwe kennis op te nemen uit enorme tekstdata op het gebied van vocabulaire, structuur, grammatica en semantiek, waardoor de modelprestaties voortdurend evolueren. Het biedt gebruikers gemakkelijkere informatie en diensten en een meer intelligente ervaring." }, diff --git a/locales/pl-PL/chat.json b/locales/pl-PL/chat.json index 77ba984fb7..d02f683ff4 100644 --- a/locales/pl-PL/chat.json +++ b/locales/pl-PL/chat.json @@ -72,8 +72,8 @@ "addUser": "Dodaj wiadomość użytkownika", "more": "więcej", "send": "Wyślij", - "sendWithCmdEnter": "Wyślij za pomocą klawisza {{meta}} + Enter", - "sendWithEnter": "Wyślij za pomocą klawisza Enter", + "sendWithCmdEnter": "Naciśnij , aby wysłać", + "sendWithEnter": "Naciśnij , aby wysłać", "stop": "Zatrzymaj", "warp": "Złamanie wiersza" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} wiadomości", "title": "Podwątek" }, + "toggleWideScreen": { + "off": "Wyłącz tryb szerokiego ekranu", + "on": "Włącz tryb szerokiego ekranu" + }, "tokenDetails": { "chats": "Rozmowy", "historySummary": "Podsumowanie historii", diff --git a/locales/pl-PL/editor.json b/locales/pl-PL/editor.json new file mode 100644 index 0000000000..18d4f41dde --- /dev/null +++ b/locales/pl-PL/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Zwiń", + "on": "Rozwiń" + }, + "typobar": { + "off": "Ukryj pasek narzędzi formatowania", + "on": "Pokaż pasek narzędzi formatowania" + } + }, + "file": { + "error": "Błąd: {{message}}", + "uploading": "Przesyłanie pliku..." + }, + "image": { + "broken": "Obraz uszkodzony" + }, + "link": { + "edit": "Edytuj link", + "open": "Otwórz link", + "placeholder": "Wpisz adres URL linku", + "unlink": "Usuń link" + }, + "table": { + "delete": "Usuń tabelę", + "deleteColumn": "Usuń kolumnę", + "deleteRow": "Usuń wiersz", + "insertColumnLeft": "Wstaw {{count}} kolumn po lewej", + "insertColumnRight": "Wstaw {{count}} kolumn po prawej", + "insertRowAbove": "Wstaw {{count}} wierszy powyżej", + "insertRowBelow": "Wstaw {{count}} wierszy poniżej" + }, + "typobar": { + "blockquote": "Cytat", + "bold": "Pogrubienie", + "bulletList": "Lista punktowana", + "code": "Kod w linii", + "codeblock": "Blok kodu", + "italic": "Kursywa", + "link": "Link", + "numberList": "Lista numerowana", + "strikethrough": "Przekreślenie", + "table": "Wstaw tabelę", + "underline": "Podkreślenie" + } +} diff --git a/locales/pt-BR/chat.json b/locales/pt-BR/chat.json index 7e5e9d09ef..15d5ad69b0 100644 --- a/locales/pt-BR/chat.json +++ b/locales/pt-BR/chat.json @@ -72,8 +72,8 @@ "addUser": "Adicionar uma mensagem de usuário", "more": "mais", "send": "Enviar", - "sendWithCmdEnter": "Pressione {{meta}} + Enter para enviar", - "sendWithEnter": "Pressione Enter para enviar", + "sendWithCmdEnter": "Pressione para enviar", + "sendWithEnter": "Pressione para enviar", "stop": "Parar", "warp": "Quebrar linha" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} mensagens", "title": "Subtópico" }, + "toggleWideScreen": { + "off": "Desativar modo de tela ampla", + "on": "Ativar modo de tela ampla" + }, "tokenDetails": { "chats": "Mensagens de bate-papo", "historySummary": "Resumo Histórico", diff --git a/locales/pt-BR/editor.json b/locales/pt-BR/editor.json new file mode 100644 index 0000000000..1ef95f79c4 --- /dev/null +++ b/locales/pt-BR/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Recolher", + "on": "Expandir" + }, + "typobar": { + "off": "Ocultar barra de formatação", + "on": "Mostrar barra de formatação" + } + }, + "file": { + "error": "Erro: {{message}}", + "uploading": "Enviando arquivo..." + }, + "image": { + "broken": "Imagem corrompida" + }, + "link": { + "edit": "Editar link", + "open": "Abrir link", + "placeholder": "Digite a URL do link", + "unlink": "Remover link" + }, + "table": { + "delete": "Excluir tabela", + "deleteColumn": "Excluir coluna", + "deleteRow": "Excluir linha", + "insertColumnLeft": "Inserir {{count}} coluna(s) à esquerda", + "insertColumnRight": "Inserir {{count}} coluna(s) à direita", + "insertRowAbove": "Inserir {{count}} linha(s) acima", + "insertRowBelow": "Inserir {{count}} linha(s) abaixo" + }, + "typobar": { + "blockquote": "Citação", + "bold": "Negrito", + "bulletList": "Lista não ordenada", + "code": "Código inline", + "codeblock": "Bloco de código", + "italic": "Itálico", + "link": "Link", + "numberList": "Lista numerada", + "strikethrough": "Tachado", + "table": "Inserir tabela", + "underline": "Sublinhado" + } +} diff --git a/locales/ru-RU/chat.json b/locales/ru-RU/chat.json index acf593931b..60f53cfa37 100644 --- a/locales/ru-RU/chat.json +++ b/locales/ru-RU/chat.json @@ -72,8 +72,8 @@ "addUser": "Добавить сообщение пользователя", "more": "больше", "send": "Отправить", - "sendWithCmdEnter": "Отправить с помощью {{meta}} + Enter", - "sendWithEnter": "Отправить с помощью Enter", + "sendWithCmdEnter": "Нажмите для отправки", + "sendWithEnter": "Нажмите для отправки", "stop": "Остановить", "warp": "Перенос строки" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} сообщений", "title": "Подтема" }, + "toggleWideScreen": { + "off": "Отключить режим широкого экрана", + "on": "Включить режим широкого экрана" + }, "tokenDetails": { "chats": "Чаты", "historySummary": "Историческое резюме", diff --git a/locales/ru-RU/editor.json b/locales/ru-RU/editor.json new file mode 100644 index 0000000000..7e86c3d07e --- /dev/null +++ b/locales/ru-RU/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Свернуть", + "on": "Развернуть" + }, + "typobar": { + "off": "Скрыть панель форматирования", + "on": "Показать панель форматирования" + } + }, + "file": { + "error": "Ошибка: {{message}}", + "uploading": "Загрузка файла..." + }, + "image": { + "broken": "Изображение повреждено" + }, + "link": { + "edit": "Редактировать ссылку", + "open": "Открыть ссылку", + "placeholder": "Введите URL ссылки", + "unlink": "Удалить ссылку" + }, + "table": { + "delete": "Удалить таблицу", + "deleteColumn": "Удалить столбец", + "deleteRow": "Удалить строку", + "insertColumnLeft": "{count, plural, one {Вставить # столбец слева} few {Вставить # столбца слева} many {Вставить # столбцов слева} other {Вставить # столбца слева}}", + "insertColumnRight": "{count, plural, one {Вставить # столбец справа} few {Вставить # столбца справа} many {Вставить # столбцов справа} other {Вставить # столбца справа}}", + "insertRowAbove": "{count, plural, one {Вставить # строку сверху} few {Вставить # строки сверху} many {Вставить # строк сверху} other {Вставить # строки сверху}}", + "insertRowBelow": "{count, plural, one {Вставить # строку снизу} few {Вставить # строки снизу} many {Вставить # строк снизу} other {Вставить # строки снизу}}" + }, + "typobar": { + "blockquote": "Цитата", + "bold": "Жирный", + "bulletList": "Маркированный список", + "code": "Встроенный код", + "codeblock": "Блок кода", + "italic": "Курсив", + "link": "Ссылка", + "numberList": "Нумерованный список", + "strikethrough": "Зачёркнутый", + "table": "Вставить таблицу", + "underline": "Подчёркнутый" + } +} diff --git a/locales/tr-TR/chat.json b/locales/tr-TR/chat.json index 6c4701fedf..bd6c05a092 100644 --- a/locales/tr-TR/chat.json +++ b/locales/tr-TR/chat.json @@ -72,8 +72,8 @@ "addUser": "Bir kullanıcı mesajı ekleyin", "more": "Daha fazla", "send": "Gönder", - "sendWithCmdEnter": "{{meta}} + Enter tuşuna basarak gönder", - "sendWithEnter": "Enter tuşuna basarak gönder", + "sendWithCmdEnter": " tuşuna basarak gönder", + "sendWithEnter": " tuşuna basarak gönder", "stop": "Dur", "warp": "Satır atla" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} mesaj", "title": "Alt konu" }, + "toggleWideScreen": { + "off": "Geniş ekran modunu kapat", + "on": "Geniş ekran modunu aç" + }, "tokenDetails": { "chats": "Sohbetler", "historySummary": "Tarih Özeti", diff --git a/locales/tr-TR/editor.json b/locales/tr-TR/editor.json new file mode 100644 index 0000000000..5a6f96656d --- /dev/null +++ b/locales/tr-TR/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Daralt", + "on": "Genişlet" + }, + "typobar": { + "off": "Biçim araç çubuğunu gizle", + "on": "Biçim araç çubuğunu göster" + } + }, + "file": { + "error": "Hata: {{message}}", + "uploading": "Dosya yükleniyor..." + }, + "image": { + "broken": "Resim bozuk" + }, + "link": { + "edit": "Bağlantıyı düzenle", + "open": "Bağlantıyı aç", + "placeholder": "Bağlantı URL'si girin", + "unlink": "Bağlantıyı kaldır" + }, + "table": { + "delete": "Tabloyu sil", + "deleteColumn": "Sütunu sil", + "deleteRow": "Satırı sil", + "insertColumnLeft": "Sol tarafa {{count}} sütun ekle", + "insertColumnRight": "Sağ tarafa {{count}} sütun ekle", + "insertRowAbove": "Üst tarafa {{count}} satır ekle", + "insertRowBelow": "Alt tarafa {{count}} satır ekle" + }, + "typobar": { + "blockquote": "Alıntı", + "bold": "Kalın", + "bulletList": "Sırasız liste", + "code": "Satır içi kod", + "codeblock": "Kod bloğu", + "italic": "İtalik", + "link": "Bağlantı", + "numberList": "Numaralı liste", + "strikethrough": "Üstü çizili", + "table": "Tablo ekle", + "underline": "Altı çizili" + } +} diff --git a/locales/vi-VN/chat.json b/locales/vi-VN/chat.json index 0f2737f2d2..8e2e2823e2 100644 --- a/locales/vi-VN/chat.json +++ b/locales/vi-VN/chat.json @@ -72,8 +72,8 @@ "addUser": "Thêm một tin nhắn người dùng", "more": "Thêm", "send": "Gửi", - "sendWithCmdEnter": "Nhấn {{meta}} + Enter để gửi", - "sendWithEnter": "Nhấn Enter để gửi", + "sendWithCmdEnter": "Nhấn để gửi", + "sendWithEnter": "Nhấn để gửi", "stop": "Dừng", "warp": "Xuống dòng" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} tin nhắn", "title": "Chủ đề con" }, + "toggleWideScreen": { + "off": "Tắt chế độ màn hình rộng", + "on": "Bật chế độ màn hình rộng" + }, "tokenDetails": { "chats": "Tin nhắn trò chuyện", "historySummary": "Tóm tắt lịch sử", diff --git a/locales/vi-VN/editor.json b/locales/vi-VN/editor.json new file mode 100644 index 0000000000..4d586ae770 --- /dev/null +++ b/locales/vi-VN/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "Thu gọn", + "on": "Mở rộng" + }, + "typobar": { + "off": "Ẩn thanh công cụ định dạng", + "on": "Hiển thị thanh công cụ định dạng" + } + }, + "file": { + "error": "Lỗi: {{message}}", + "uploading": "Đang tải tệp lên..." + }, + "image": { + "broken": "Hình ảnh bị hỏng" + }, + "link": { + "edit": "Chỉnh sửa liên kết", + "open": "Mở liên kết", + "placeholder": "Nhập URL liên kết", + "unlink": "Gỡ liên kết" + }, + "table": { + "delete": "Xóa bảng", + "deleteColumn": "Xóa cột", + "deleteRow": "Xóa hàng", + "insertColumnLeft": "Chèn {{count}} cột vào bên trái", + "insertColumnRight": "Chèn {{count}} cột vào bên phải", + "insertRowAbove": "Chèn {{count}} hàng vào phía trên", + "insertRowBelow": "Chèn {{count}} hàng vào phía dưới" + }, + "typobar": { + "blockquote": "Trích dẫn", + "bold": "Đậm", + "bulletList": "Danh sách gạch đầu dòng", + "code": "Mã nội tuyến", + "codeblock": "Khối mã", + "italic": "Nghiêng", + "link": "Liên kết", + "numberList": "Danh sách có thứ tự", + "strikethrough": "Gạch ngang", + "table": "Chèn bảng", + "underline": "Gạch chân" + } +} diff --git a/locales/zh-CN/chat.json b/locales/zh-CN/chat.json index a1fc8cedc9..bfef1b28da 100644 --- a/locales/zh-CN/chat.json +++ b/locales/zh-CN/chat.json @@ -72,8 +72,8 @@ "addUser": "添加一条用户消息", "more": "更多", "send": "发送", - "sendWithCmdEnter": "按 {{meta}} + Enter 键发送", - "sendWithEnter": "按 Enter 键发送", + "sendWithCmdEnter": "按 键发送", + "sendWithEnter": "按 键发送", "stop": "停止", "warp": "换行" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} 条消息", "title": "子话题" }, + "toggleWideScreen": { + "off": "关闭宽屏模式", + "on": "开启宽屏模式" + }, "tokenDetails": { "chats": "会话消息", "historySummary": "历史总结", diff --git a/locales/zh-CN/editor.json b/locales/zh-CN/editor.json new file mode 100644 index 0000000000..0c1b155eeb --- /dev/null +++ b/locales/zh-CN/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "收起", + "on": "展开" + }, + "typobar": { + "off": "隐藏格式工具栏", + "on": "显示格式工具栏" + } + }, + "file": { + "error": "错误:{{message}}", + "uploading": "正在上传文件..." + }, + "image": { + "broken": "图片损坏" + }, + "link": { + "edit": "编辑链接", + "open": "打开链接", + "placeholder": "输入链接 URL", + "unlink": "取消链接" + }, + "table": { + "delete": "删除表格", + "deleteColumn": "删除列", + "deleteRow": "删除行", + "insertColumnLeft": "在左侧插入 {{count}} 列", + "insertColumnRight": "在右侧插入 {{count}} 列", + "insertRowAbove": "在上方插入 {{count}} 行", + "insertRowBelow": "在下方插入 {{count}} 行" + }, + "typobar": { + "blockquote": "引用", + "bold": "加粗", + "bulletList": "无序列表", + "code": "行内代码", + "codeblock": "代码块", + "italic": "斜体", + "link": "链接", + "numberList": "有序列表", + "strikethrough": "删除线", + "table": "插入表格", + "underline": "下划线" + } +} diff --git a/locales/zh-TW/chat.json b/locales/zh-TW/chat.json index c2d55763dd..a0aae24d2c 100644 --- a/locales/zh-TW/chat.json +++ b/locales/zh-TW/chat.json @@ -72,8 +72,8 @@ "addUser": "新增一條使用者訊息", "more": "更多", "send": "發送", - "sendWithCmdEnter": "按 {{meta}} + Enter 鍵發送", - "sendWithEnter": "按 Enter 鍵發送", + "sendWithCmdEnter": "按 鍵發送", + "sendWithEnter": "按 鍵發送", "stop": "停止", "warp": "換行" }, @@ -232,6 +232,10 @@ "threadMessageCount": "{{messageCount}} 條消息", "title": "子話題" }, + "toggleWideScreen": { + "off": "關閉寬螢幕模式", + "on": "開啟寬螢幕模式" + }, "tokenDetails": { "chats": "聊天訊息", "historySummary": "歷史總結", diff --git a/locales/zh-TW/editor.json b/locales/zh-TW/editor.json new file mode 100644 index 0000000000..0200caf26b --- /dev/null +++ b/locales/zh-TW/editor.json @@ -0,0 +1,47 @@ +{ + "actions": { + "expand": { + "off": "收合", + "on": "展開" + }, + "typobar": { + "off": "隱藏格式工具列", + "on": "顯示格式工具列" + } + }, + "file": { + "error": "錯誤:{{message}}", + "uploading": "正在上傳檔案..." + }, + "image": { + "broken": "圖片損毀" + }, + "link": { + "edit": "編輯連結", + "open": "開啟連結", + "placeholder": "輸入連結 URL", + "unlink": "取消連結" + }, + "table": { + "delete": "刪除表格", + "deleteColumn": "刪除列", + "deleteRow": "刪除行", + "insertColumnLeft": "在左側插入 {{count}} 列", + "insertColumnRight": "在右側插入 {{count}} 列", + "insertRowAbove": "在上方插入 {{count}} 行", + "insertRowBelow": "在下方插入 {{count}} 行" + }, + "typobar": { + "blockquote": "引用", + "bold": "粗體", + "bulletList": "無序清單", + "code": "行內程式碼", + "codeblock": "程式碼區塊", + "italic": "斜體", + "link": "連結", + "numberList": "有序清單", + "strikethrough": "刪除線", + "table": "插入表格", + "underline": "底線" + } +} diff --git a/locales/zh-TW/models.json b/locales/zh-TW/models.json index 5cb756de41..3c5f66d892 100644 --- a/locales/zh-TW/models.json +++ b/locales/zh-TW/models.json @@ -1505,7 +1505,9 @@ "gpt-4.1-nano": { "description": "GPT-4.1 mini 提供了智能、速度和成本之間的平衡,使其成為許多用例中具吸引力的模型。" }, - "gpt-4.5-preview": "GPT-4.5-preview 是最新的通用模型,具備深厚的世界知識並能更好地理解使用者意圖,擅長創意任務與代理規劃。此模型的知識截至 2023 年 10 月。", + "gpt-4.5-preview": { + "description": "GPT-4.5-preview 是最新的通用模型,擁有深厚的世界知識和對使用者意圖的更佳理解,擅長創意任務與代理規劃。該模型的知識截止至2023年10月。" + }, "gpt-4o": { "description": "ChatGPT-4o是一款動態模型,實時更新以保持當前最新版本。它結合了強大的語言理解與生成能力,適合於大規模應用場景,包括客戶服務、教育和技術支持。" }, diff --git a/next.config.ts b/next.config.ts index 1939e0ea10..a84209c27d 100644 --- a/next.config.ts +++ b/next.config.ts @@ -22,6 +22,9 @@ const standaloneConfig: NextConfig = { const nextConfig: NextConfig = { ...(isStandaloneMode ? standaloneConfig : {}), + compiler: { + emotion: true, + }, compress: isProd, experimental: { optimizePackageImports: [ @@ -30,6 +33,7 @@ const nextConfig: NextConfig = { '@emoji-mart/data', '@icons-pack/react-simple-icons', '@lobehub/ui', + '@lobehub/icons', 'gpt-tokenizer', ], // oidc provider depend on constructor.name diff --git a/package.json b/package.json index 2768d6c37a..1ce9990028 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,7 @@ "@codesandbox/sandpack-react": "^2.20.0", "@cyntler/react-doc-viewer": "^1.17.0", "@electric-sql/pglite": "0.2.17", + "@emotion/react": "^11.14.0", "@fal-ai/client": "^1.6.1", "@formkit/auto-animate": "^0.8.2", "@google/genai": "^1.13.0", @@ -154,10 +155,11 @@ "@lobehub/charts": "^2.0.0", "@lobehub/chat-plugin-sdk": "^1.32.4", "@lobehub/chat-plugins-gateway": "^1.9.0", + "@lobehub/editor": "^1.4.4", "@lobehub/icons": "^2.31.0", "@lobehub/market-sdk": "^0.22.7", "@lobehub/tts": "^2.0.1", - "@lobehub/ui": "^2.8.3", + "@lobehub/ui": "^2.11.9", "@modelcontextprotocol/sdk": "^1.17.1", "@neondatabase/serverless": "^1.0.1", "@next/third-parties": "^15.4.6", diff --git a/packages/const/src/layoutTokens.ts b/packages/const/src/layoutTokens.ts index e9e4d8ea93..8040073ac1 100644 --- a/packages/const/src/layoutTokens.ts +++ b/packages/const/src/layoutTokens.ts @@ -7,6 +7,7 @@ export const CHAT_TEXTAREA_MAX_HEIGHT = 800; export const CHAT_TEXTAREA_HEIGHT = 160; export const CHAT_TEXTAREA_HEIGHT_MOBILE = 108; export const CHAT_SIDEBAR_WIDTH = 280; +export const CONVERSATION_MIN_WIDTH = 850; export const CHAT_PORTAL_WIDTH = 400; export const CHAT_PORTAL_MAX_WIDTH = 1280; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index b865e30255..b7af26e8e2 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -8,6 +8,7 @@ export * from './chunk'; export * from './clientDB'; export * from './eval'; export * from './fetch'; +export * from './hotkey'; export * from './knowledgeBase'; export * from './llm'; export * from './message'; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index fac243a40e..55f645bac3 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -2,6 +2,7 @@ export * from './client/cookie'; export * from './detectChinese'; export * from './format'; export * from './imageToBase64'; +export * from './keyboard'; export * from './object'; export * from './parseModels'; export * from './pricing'; diff --git a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx b/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx deleted file mode 100644 index 92f99b08d3..0000000000 --- a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { Button, Dropdown, Hotkey, Icon } from '@lobehub/ui'; -import { createStyles } from 'antd-style'; -import { BotMessageSquare, LucideCheck, LucideChevronDown, MessageSquarePlus } from 'lucide-react'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Flexbox } from 'react-layout-kit'; - -import { useSendMessage } from '@/features/ChatInput/useSend'; -import { useChatStore } from '@/store/chat'; -import { useUserStore } from '@/store/user'; -import { preferenceSelectors, settingsSelectors } from '@/store/user/selectors'; -import { HotkeyEnum } from '@/types/hotkey'; - -const useStyles = createStyles(({ css, prefixCls }) => { - return { - arrow: css` - &.${prefixCls}-btn.${prefixCls}-btn-icon-only { - width: 28px; - } - `, - }; -}); - -interface SendMoreProps { - disabled?: boolean; - isMac?: boolean; -} - -const SendMore = memo(({ disabled, isMac }) => { - const { t } = useTranslation('chat'); - const hotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.AddUserMessage)); - const { styles } = useStyles(); - - const [useCmdEnterToSend, updatePreference] = useUserStore((s) => [ - preferenceSelectors.useCmdEnterToSend(s), - s.updatePreference, - ]); - const addAIMessage = useChatStore((s) => s.addAIMessage); - - const { send: sendMessage } = useSendMessage(); - - return ( - :
, - key: 'sendWithEnter', - label: t('input.sendWithEnter'), - onClick: () => { - updatePreference({ useCmdEnterToSend: false }); - }, - }, - { - icon: useCmdEnterToSend ? :
, - key: 'sendWithCmdEnter', - label: t('input.sendWithCmdEnter', { - meta: typeof isMac === 'boolean' ? (isMac ? '⌘' : 'Ctrl') : '…', - }), - onClick: () => { - updatePreference({ useCmdEnterToSend: true }); - }, - }, - { type: 'divider' }, - { - icon: , - key: 'addAi', - label: t('input.addAi'), - onClick: () => { - addAIMessage(); - }, - }, - { - icon: , - key: 'addUser', - label: ( - - {t('input.addUser')} - - - ), - onClick: () => { - sendMessage({ onlyAddUserMessage: true }); - }, - }, - ], - }} - placement={'topRight'} - trigger={['hover']} - > - - ) : ( - - - - - )} - - - - - ); -}); - -Footer.displayName = 'Footer'; - -export default Footer; diff --git a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/MessageFromUrl.tsx b/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/MessageFromUrl.tsx similarity index 87% rename from src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/MessageFromUrl.tsx rename to src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/MessageFromUrl.tsx index f46a2f57d3..a8110acb37 100644 --- a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/MessageFromUrl.tsx +++ b/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/MessageFromUrl.tsx @@ -3,12 +3,13 @@ import { useSearchParams } from 'next/navigation'; import { useEffect } from 'react'; -import { useSendMessage } from '@/features/ChatInput/useSend'; import { useChatStore } from '@/store/chat'; +import { useSend } from '../useSend'; + const MessageFromUrl = () => { const updateInputMessage = useChatStore((s) => s.updateInputMessage); - const { send: sendMessage } = useSendMessage(); + const { send: sendMessage } = useSend(); const searchParams = useSearchParams(); useEffect(() => { diff --git a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx b/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx deleted file mode 100644 index 2bc8762f8e..0000000000 --- a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx +++ /dev/null @@ -1,332 +0,0 @@ -import { act, fireEvent, render, screen } from '@testing-library/react'; -import React from 'react'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -import { useChatStore } from '@/store/chat'; -import { useUserStore } from '@/store/user'; - -import InputArea from './TextArea'; - -let sendMessageMock: () => Promise; - -// Mock the useSendMessage hook to return our mock function -vi.mock('@/features/ChatInput/useSend', () => ({ - useSendMessage: () => ({ - send: sendMessageMock, - canSend: true, - }), -})); - -// Mock the Chinese warning hook to always allow sending -vi.mock('@/hooks/useGeminiChineseWarning', () => ({ - useGeminiChineseWarning: () => () => Promise.resolve(true), -})); - -let onSendMock: () => void; - -beforeEach(() => { - onSendMock = vi.fn(); - sendMessageMock = vi.fn().mockResolvedValue(undefined); - vi.clearAllMocks(); -}); - -describe('', () => { - it('renders the TextArea component correctly', () => { - render(); - const textArea = screen.getByRole('textbox'); - expect(textArea).toBeInTheDocument(); - }); - - it('auto-focuses the TextArea component on mount', () => { - render(); - const textArea = screen.getByRole('textbox'); - - // The document's active element should be the textarea if it was auto-focused - expect(document.activeElement).toBe(textArea); - }); - - it('renders with correct placeholder text', () => { - render(); - const textArea = screen.getByPlaceholderText('sendPlaceholder'); - expect(textArea).toBeInTheDocument(); - }); - - it('has the correct initial value', () => { - render(); - const textArea = screen.getByRole('textbox'); - expect(textArea).toHaveValue(''); - }); - - describe('input behavior', () => { - it('calls updateInputMessage on input change', async () => { - const updateInputMessageMock = vi.fn(); - act(() => { - useChatStore.setState({ updateInputMessage: updateInputMessageMock }); - }); - - render(); - - const textArea = screen.getByRole('textbox'); - const newValue = 'New message'; - fireEvent.change(textArea, { target: { value: newValue } }); - - expect(updateInputMessageMock).toHaveBeenCalledWith(newValue); - }); - - it('handles composition events for IME input correctly', () => { - const updateInputMessageMock = vi.fn(); - act(() => { - useChatStore.setState({ updateInputMessage: updateInputMessageMock }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - - // Start composition (IME input starts) - fireEvent.compositionStart(textArea); - fireEvent.change(textArea, { target: { value: '正在' } }); - expect(updateInputMessageMock).toHaveBeenCalledWith('正在'); - - // End composition (IME input ends) - fireEvent.compositionEnd(textArea); - fireEvent.change(textArea, { target: { value: '正在输入' } }); - expect(updateInputMessageMock).toHaveBeenCalledWith('正在输入'); - }); - - it('does not send a message when Enter is pressed during IME composition', () => { - const updateInputMessageMock = vi.fn(); - act(() => { - useChatStore.setState({ updateInputMessage: updateInputMessageMock }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - - // Start composition (IME input starts) - fireEvent.compositionStart(textArea); - - // Simulate pressing Enter during IME composition - fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter' }); - - // Since we are in the middle of IME composition, the message should not be sent - expect(onSendMock).not.toHaveBeenCalled(); - expect(updateInputMessageMock).not.toHaveBeenCalled(); - - // End composition (IME input ends) - fireEvent.compositionEnd(textArea); - - // Now simulate pressing Enter after IME composition - fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter' }); - - // Since IME composition has ended, now the message should be sent - expect(onSendMock).toHaveBeenCalled(); - expect(updateInputMessageMock).toHaveBeenCalled(); - }); - - it('updates the input message when TextArea loses focus', () => { - const updateInputMessageMock = vi.fn(); - act(() => { - useChatStore.setState({ updateInputMessage: updateInputMessageMock }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - const newText = 'New input text'; - - fireEvent.change(textArea, { target: { value: newText } }); - fireEvent.blur(textArea); - - expect(updateInputMessageMock).toHaveBeenCalledWith(newText); - }); - }); - - describe('leaving protect', () => { - it('triggers a warning when trying to leave the page with unsaved input', () => { - const beforeUnloadEvent = new Event('beforeunload', { cancelable: true }); - - act(() => { - useChatStore.setState({ inputMessage: 'Unsaved input' }); - }); - - render(); - - // trigger beforeunload - window.dispatchEvent(beforeUnloadEvent); - - // 检查 returnValue 是否被设置为 true,这是触发警告的标识 - expect(beforeUnloadEvent.returnValue).toBeTruthy(); - }); - - it('does not trigger a warning when trying to leave the page with empty input', () => { - act(() => { - useChatStore.setState({ inputMessage: '' }); - }); - - // 模拟 window.addEventListener 来捕获 beforeunload 事件处理程序 - const addEventListenerSpy = vi.spyOn(window, 'addEventListener'); - const beforeUnloadHandler = vi.fn(); - - addEventListenerSpy.mockImplementation((event, handler) => { - // @ts-ignore - if (event === 'beforeunload') { - beforeUnloadHandler.mockImplementation(handler as any); - } - }); - - // 渲染组件 - render(); - - // 触发 beforeunload 事件 - const event = new Event('beforeunload', { cancelable: true }); - window.dispatchEvent(event); - - // 检查 beforeunload 事件的处理程序是否没有被调用 - expect(beforeUnloadHandler).not.toHaveBeenCalled(); - - // 清理模拟 - addEventListenerSpy.mockRestore(); - }); - - describe('cleanup', () => { - it('removes beforeunload listener on unmount', () => { - const addEventListenerSpy = vi.spyOn(window, 'addEventListener'); - const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener'); - - // 渲染并立即卸载组件 - const { unmount } = render(); - unmount(); - - // 检查是否为 beforeunload 事件添加了监听器 - expect(addEventListenerSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function)); - - // 检查是否移除了对应的监听器 - expect(removeEventListenerSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function)); - - // 清理 spy - addEventListenerSpy.mockRestore(); - removeEventListenerSpy.mockRestore(); - }); - }); - }); - - describe('message sending behavior', () => { - it('does not send message when loading or shift key is pressed', () => { - act(() => { - useChatStore.setState({ chatLoadingIds: ['123'] }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - - fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter', shiftKey: true }); - expect(sendMessageMock).not.toHaveBeenCalled(); - }); - - it('sends message on Enter press when not loading and no shift key', async () => { - act(() => { - useChatStore.setState({ - chatLoadingIds: [], - inputMessage: 'abc', - }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - fireEvent.change(textArea, { target: { value: 'Test message' } }); - - fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter' }); - - await vi.waitFor(() => { - expect(sendMessageMock).toHaveBeenCalled(); - }); - }); - - describe('metaKey behavior for sending messages', () => { - it('windows: sends message on ctrl + enter when useCmdEnterToSend is true', async () => { - act(() => { - useChatStore.setState({ - chatLoadingIds: [], - inputMessage: '123', - }); - useUserStore.getState().updatePreference({ useCmdEnterToSend: true }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - - fireEvent.keyDown(textArea, { code: 'Enter', ctrlKey: true, key: 'Enter' }); - - await vi.waitFor(() => { - expect(sendMessageMock).toHaveBeenCalled(); - }); - }); - - it('windows: inserts a new line on ctrl + enter when useCmdEnterToSend is false', () => { - const updateInputMessageMock = vi.fn(); - act(() => { - useChatStore.setState({ - chatLoadingIds: [], - inputMessage: 'Test', - updateInputMessage: updateInputMessageMock, - }); - useUserStore.getState().updatePreference({ useCmdEnterToSend: false }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - - fireEvent.keyDown(textArea, { code: 'Enter', ctrlKey: true, key: 'Enter' }); - expect(updateInputMessageMock).toHaveBeenCalledWith('Test\n'); - expect(sendMessageMock).not.toHaveBeenCalled(); // sendMessage should not be called - }); - - it('macOS: sends message on cmd + enter when useCmdEnterToSend is true', async () => { - vi.stubGlobal('navigator', { - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', - }); - act(() => { - useChatStore.setState({ - chatLoadingIds: [], - inputMessage: '123', - }); - useUserStore.getState().updatePreference({ useCmdEnterToSend: true }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - - fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter', metaKey: true }); - - await vi.waitFor(() => { - expect(sendMessageMock).toHaveBeenCalled(); - }); - vi.restoreAllMocks(); - }); - - it('macOS: inserts a new line on cmd + enter when useCmdEnterToSend is false', () => { - vi.stubGlobal('navigator', { - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', - }); - const updateInputMessageMock = vi.fn(); - act(() => { - useChatStore.setState({ - chatLoadingIds: [], - inputMessage: 'Test', - updateInputMessage: updateInputMessageMock, - }); - useUserStore.getState().updatePreference({ useCmdEnterToSend: false }); - }); - - render(); - const textArea = screen.getByRole('textbox'); - - fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter', metaKey: true }); - expect(updateInputMessageMock).toHaveBeenCalledWith('Test\n'); - expect(sendMessageMock).not.toHaveBeenCalled(); // sendMessage should not be called - vi.restoreAllMocks(); - }); - }); - }); -}); diff --git a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx b/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx deleted file mode 100644 index d87624c9e5..0000000000 --- a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { memo } from 'react'; - -import InputArea from '@/features/ChatInput/Desktop/InputArea'; -import { useSendMessage } from '@/features/ChatInput/useSend'; -import { useChatStore } from '@/store/chat'; -import { chatSelectors } from '@/store/chat/slices/message/selectors'; - -const TextArea = memo<{ onSend?: () => void }>(({ onSend }) => { - const [loading, value, updateInputMessage] = useChatStore((s) => [ - chatSelectors.isAIGenerating(s), - s.inputMessage, - s.updateInputMessage, - ]); - const { send: sendMessage } = useSendMessage(); - - return ( - { - sendMessage(); - onSend?.(); - }} - value={value} - /> - ); -}); - -export default TextArea; diff --git a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx b/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx index 95bfce02c0..c2d802e60b 100644 --- a/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +++ b/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx @@ -1,51 +1,152 @@ 'use client'; -import { memo } from 'react'; +import { Alert, Hotkey, Icon } from '@lobehub/ui'; +import { BotMessageSquare, LucideCheck, MessageSquarePlus } from 'lucide-react'; +import { Suspense, memo } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { Flexbox } from 'react-layout-kit'; -import { ActionKeys } from '@/features/ChatInput/ActionBar/config'; -import DesktopChatInput, { FooterRender } from '@/features/ChatInput/Desktop'; -import { useGlobalStore } from '@/store/global'; -import { systemStatusSelectors } from '@/store/global/selectors'; +import { type ActionKeys, ChatInputProvider, DesktopChatInput } from '@/features/ChatInput'; +import WideScreenContainer from '@/features/Conversation/components/WideScreenContainer'; +import { useChatStore } from '@/store/chat'; +import { aiChatSelectors } from '@/store/chat/selectors'; +import { useUserStore } from '@/store/user'; +import { preferenceSelectors, settingsSelectors } from '@/store/user/selectors'; +import { HotkeyEnum, KeyEnum } from '@/types/hotkey'; -import Footer from './Footer'; -import TextArea from './TextArea'; +import { useSend } from '../useSend'; +import MessageFromUrl from './MessageFromUrl'; -const leftActions = [ +const leftActions: ActionKeys[] = [ 'model', 'search', + 'typo', 'fileUpload', 'knowledgeBase', - 'params', - 'history', - 'stt', 'tools', + '---', + ['params', 'history', 'stt', 'clear'], 'mainToken', -] as ActionKeys[]; +]; -const rightActions = ['clear'] as ActionKeys[]; - -const renderTextArea = (onSend: () => void) =>