@lobehub/chat 1.15.18 → 1.15.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/locales/ar/chat.json +3 -0
  3. package/locales/ar/portal.json +1 -0
  4. package/locales/bg-BG/chat.json +3 -0
  5. package/locales/bg-BG/portal.json +1 -0
  6. package/locales/de-DE/chat.json +3 -0
  7. package/locales/de-DE/portal.json +1 -0
  8. package/locales/en-US/chat.json +3 -0
  9. package/locales/en-US/portal.json +1 -0
  10. package/locales/es-ES/chat.json +3 -0
  11. package/locales/es-ES/portal.json +1 -0
  12. package/locales/fr-FR/chat.json +3 -0
  13. package/locales/fr-FR/portal.json +1 -0
  14. package/locales/it-IT/chat.json +3 -0
  15. package/locales/it-IT/portal.json +1 -0
  16. package/locales/ja-JP/chat.json +3 -0
  17. package/locales/ja-JP/portal.json +1 -0
  18. package/locales/ko-KR/chat.json +3 -0
  19. package/locales/ko-KR/portal.json +1 -0
  20. package/locales/nl-NL/chat.json +3 -0
  21. package/locales/nl-NL/portal.json +1 -0
  22. package/locales/pl-PL/chat.json +3 -0
  23. package/locales/pl-PL/portal.json +1 -0
  24. package/locales/pt-BR/chat.json +3 -0
  25. package/locales/pt-BR/portal.json +1 -0
  26. package/locales/ru-RU/chat.json +3 -0
  27. package/locales/ru-RU/portal.json +1 -0
  28. package/locales/tr-TR/chat.json +3 -0
  29. package/locales/tr-TR/portal.json +1 -0
  30. package/locales/vi-VN/chat.json +3 -0
  31. package/locales/vi-VN/portal.json +1 -0
  32. package/locales/zh-CN/chat.json +3 -0
  33. package/locales/zh-CN/portal.json +1 -0
  34. package/locales/zh-TW/chat.json +3 -0
  35. package/locales/zh-TW/portal.json +1 -0
  36. package/package.json +3 -3
  37. package/src/app/(main)/chat/(workspace)/@portal/MessageDetail/Body/index.tsx +45 -0
  38. package/src/app/(main)/chat/(workspace)/@portal/MessageDetail/Header.tsx +19 -0
  39. package/src/app/(main)/chat/(workspace)/@portal/MessageDetail/index.ts +9 -0
  40. package/src/app/(main)/chat/(workspace)/@portal/MessageDetail/useEnable.ts +4 -0
  41. package/src/app/(main)/chat/(workspace)/@portal/router.tsx +2 -1
  42. package/src/features/Conversation/Messages/User/MarkdownRender/ContentPreview.tsx +58 -0
  43. package/src/features/Conversation/Messages/User/MarkdownRender/index.tsx +15 -0
  44. package/src/features/Conversation/Messages/User/index.tsx +1 -0
  45. package/src/features/Conversation/Messages/index.ts +6 -2
  46. package/src/features/Conversation/components/ChatItem/index.tsx +24 -2
  47. package/src/features/Conversation/types/index.tsx +6 -0
  48. package/src/locales/default/chat.ts +3 -0
  49. package/src/locales/default/portal.ts +1 -0
  50. package/src/services/chat.ts +3 -1
  51. package/src/store/chat/slices/portal/action.ts +15 -7
  52. package/src/store/chat/slices/portal/initialState.ts +1 -0
  53. package/src/store/chat/slices/portal/selectors.ts +4 -0
  54. package/src/utils/tokenizer/tokenizer.worker.ts +0 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.15.19](https://github.com/lobehub/lobe-chat/compare/v1.15.18...v1.15.19)
6
+
7
+ <sup>Released on **2024-09-08**</sup>
8
+
9
+ <br/>
10
+
11
+ <details>
12
+ <summary><kbd>Improvements and Fixes</kbd></summary>
13
+
14
+ </details>
15
+
16
+ <div align="right">
17
+
18
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
19
+
20
+ </div>
21
+
5
22
  ### [Version 1.15.18](https://github.com/lobehub/lobe-chat/compare/v1.15.17...v1.15.18)
6
23
 
7
24
  <sup>Released on **2024-09-06**</sup>
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "مرحبًا، أنا **{{name}}**، دعنا نبدأ المحادثة!",
8
8
  "agentsAndConversations": "الوكلاء والمحادثات",
9
9
  "backToBottom": "العودة إلى الأسفل",
10
+ "chatList": {
11
+ "longMessageDetail": "عرض التفاصيل"
12
+ },
10
13
  "clearCurrentMessages": "مسح رسائل الجلسة الحالية",
11
14
  "confirmClearCurrentMessages": "سيتم مسح رسائل الجلسة الحالية قريبًا، وبمجرد المسح لن يمكن استعادتها، يرجى تأكيد الإجراء الخاص بك",
12
15
  "confirmRemoveSessionItemAlert": "سيتم حذف هذا المساعد قريبًا، وبمجرد الحذف لن يمكن استعادته، يرجى تأكيد الإجراء الخاص بك",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "قائمة القطع الأثرية الحالية فارغة، يرجى استخدام الإضافات في الجلسة ومن ثم التحقق مرة أخرى",
15
15
  "emptyKnowledgeList": "قائمة المعرفة الحالية فارغة، يرجى فتح قاعدة المعرفة حسب الحاجة في المحادثة قبل العرض",
16
16
  "files": "ملفات",
17
+ "messageDetail": "تفاصيل الرسالة",
17
18
  "title": "نافذة موسعة"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Здравей, аз съм **{{name}}** и нека започнем разговора!",
8
8
  "agentsAndConversations": "агенти и разговори",
9
9
  "backToBottom": "Върни се в началото",
10
+ "chatList": {
11
+ "longMessageDetail": "Вижте детайлите"
12
+ },
10
13
  "clearCurrentMessages": "Изчисти съобщенията от текущата сесия",
11
14
  "confirmClearCurrentMessages": "На път си да изчистиш съобщенията от текущата сесия. След като бъдат изчистени, те не могат да бъдат възстановени. Моля, потвърди действието си.",
12
15
  "confirmRemoveSessionItemAlert": "На път си да изтриеш този агент. След като бъде изтрит, той не може да бъде възстановен. Моля, потвърди действието си.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "Списъкът с текущите артефакти е празен. Моля, използвайте добавки в разговора и след това проверете отново.",
15
15
  "emptyKnowledgeList": "Текущият списък с познания е празен. Моля, активирайте базата данни на познанията по време на сесията, за да я прегледате.",
16
16
  "files": "файлове",
17
+ "messageDetail": "Детайли на съобщението",
17
18
  "title": "Разширено прозорец"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Hallo, ich bin **{{name}}**. Lassen Sie uns ins Gespräch kommen!",
8
8
  "agentsAndConversations": "Agenten und Unterhaltungen",
9
9
  "backToBottom": "Zurück zum Ende",
10
+ "chatList": {
11
+ "longMessageDetail": "Details anzeigen"
12
+ },
10
13
  "clearCurrentMessages": "Aktuelle Nachrichten löschen",
11
14
  "confirmClearCurrentMessages": "Möchtest du wirklich die aktuellen Nachrichten löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
12
15
  "confirmRemoveSessionItemAlert": "Möchtest du diesen Assistenten wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "Die Liste der Artefakte ist derzeit leer. Bitte verwenden Sie Plugins in der Sitzung und überprüfen Sie sie erneut.",
15
15
  "emptyKnowledgeList": "Die aktuelle Wissensliste ist leer. Bitte aktivieren Sie die Wissensdatenbank nach Bedarf in der Sitzung, um sie anzuzeigen.",
16
16
  "files": "Dateien",
17
+ "messageDetail": "Nachrichtendetails",
17
18
  "title": "Erweiterungsfenster"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Hello, I'm **{{name}}**, let's start chatting!",
8
8
  "agentsAndConversations": "Assistants and Conversations",
9
9
  "backToBottom": "Back to bottom",
10
+ "chatList": {
11
+ "longMessageDetail": "View Details"
12
+ },
10
13
  "clearCurrentMessages": "Clear current session messages",
11
14
  "confirmClearCurrentMessages": "You are about to clear the current session messages. Once cleared, they cannot be retrieved. Please confirm your action.",
12
15
  "confirmRemoveSessionItemAlert": "You are about to delete this assistant. Once deleted, it cannot be retrieved. Please confirm your action.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "The current Artifacts list is empty. Please use plugins in the session as needed before viewing.",
15
15
  "emptyKnowledgeList": "The current knowledge list is empty. Please enable the knowledge base as needed during the conversation before viewing.",
16
16
  "files": "Files",
17
+ "messageDetail": "Message Details",
17
18
  "title": "Portal View"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "¡Hola, soy **{{name}}**! Comencemos nuestra conversación.",
8
8
  "agentsAndConversations": "agentesYConversaciones",
9
9
  "backToBottom": "Volver al fondo",
10
+ "chatList": {
11
+ "longMessageDetail": "Ver detalles"
12
+ },
10
13
  "clearCurrentMessages": "Borrar mensajes actuales",
11
14
  "confirmClearCurrentMessages": "Estás a punto de borrar los mensajes de esta sesión. Una vez borrados, no se podrán recuperar. Por favor, confirma tu acción.",
12
15
  "confirmRemoveSessionItemAlert": "Estás a punto de eliminar este asistente. Una vez eliminado, no se podrá recuperar. Por favor, confirma tu acción.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "La lista de Artefactos actual está vacía. Por favor, utilice los complementos en la conversación y vuelva a intentarlo.",
15
15
  "emptyKnowledgeList": "La lista de conocimientos actual está vacía. Por favor, activa la base de conocimientos según sea necesario en la conversación antes de volver a revisar.",
16
16
  "files": "archivos",
17
+ "messageDetail": "Detalles del mensaje",
17
18
  "title": "Ventana de expansión"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Bonjour, je suis **{{name}}**. Commençons notre conversation !",
8
8
  "agentsAndConversations": "Agents et conversations",
9
9
  "backToBottom": "Retour en bas",
10
+ "chatList": {
11
+ "longMessageDetail": "Voir les détails"
12
+ },
10
13
  "clearCurrentMessages": "Effacer les messages actuels",
11
14
  "confirmClearCurrentMessages": "Vous êtes sur le point d'effacer les messages de cette session. Cette action est irréversible. Veuillez confirmer.",
12
15
  "confirmRemoveSessionItemAlert": "Vous êtes sur le point de supprimer cet agent. Cette action est irréversible. Veuillez confirmer.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "La liste des Artifacts est actuellement vide. Veuillez utiliser les plugins dans la conversation avant de consulter à nouveau.",
15
15
  "emptyKnowledgeList": "La liste des connaissances est actuellement vide. Veuillez activer la base de connaissances selon vos besoins dans la conversation avant de consulter.",
16
16
  "files": "Fichiers",
17
+ "messageDetail": "Détails du message",
17
18
  "title": "Fenêtre d'extension"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Ciao, sono **{{name}}**. Cominciamo a chiacchierare!",
8
8
  "agentsAndConversations": "Assistenti e Conversazioni",
9
9
  "backToBottom": "Torna in fondo",
10
+ "chatList": {
11
+ "longMessageDetail": "Visualizza dettagli"
12
+ },
10
13
  "clearCurrentMessages": "Cancella messaggi attuali",
11
14
  "confirmClearCurrentMessages": "Stai per cancellare i messaggi attuali, questa operazione non potrà essere annullata. Confermi?",
12
15
  "confirmRemoveSessionItemAlert": "Stai per rimuovere questo assistente, l'operazione non potrà essere annullata. Confermi?",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "La lista degli Artefatti attuale è vuota, si prega di utilizzare i plugin necessari durante la sessione e poi controllare di nuovo",
15
15
  "emptyKnowledgeList": "L'elenco delle conoscenze attuale è vuoto. Si prega di attivare il database delle conoscenze durante la conversazione per visualizzarlo.",
16
16
  "files": "File",
17
+ "messageDetail": "Dettagli del messaggio",
17
18
  "title": "Finestra di espansione"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "こんにちは、私は**{{name}}**です。会話しましょう!",
8
8
  "agentsAndConversations": "エージェントと会話",
9
9
  "backToBottom": "現在に戻る",
10
+ "chatList": {
11
+ "longMessageDetail": "詳細を見る"
12
+ },
10
13
  "clearCurrentMessages": "現在の会話をクリア",
11
14
  "confirmClearCurrentMessages": "現在の会話をクリアします。クリアした後は元に戻すことはできません。操作を確認してください。",
12
15
  "confirmRemoveSessionItemAlert": "このエージェントを削除します。削除した後は元に戻すことはできません。操作を確認してください。",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "現在、アーティファクトリストは空です。プラグインを使用してセッション中に追加してください。",
15
15
  "emptyKnowledgeList": "現在の知識リストは空です。会話中に必要に応じて知識ベースを開いてからご覧ください。",
16
16
  "files": "ファイル",
17
+ "messageDetail": "メッセージの詳細",
17
18
  "title": "拡張ウィンドウ"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "안녕하세요, 저는 **{{name}}**입니다. 대화를 시작해보세요!",
8
8
  "agentsAndConversations": "에이전트 및 대화",
9
9
  "backToBottom": "하단으로 이동",
10
+ "chatList": {
11
+ "longMessageDetail": "자세히 보기"
12
+ },
10
13
  "clearCurrentMessages": "현재 대화 지우기",
11
14
  "confirmClearCurrentMessages": "현재 대화를 지우시면 되돌릴 수 없습니다. 작업을 확인하시겠습니까?",
12
15
  "confirmRemoveSessionItemAlert": "이 도우미를 삭제하시면 되돌릴 수 없습니다. 작업을 확인하시겠습니까?",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "현재 아티팩트 목록이 비어 있습니다. 플러그인을 사용한 후에 다시 확인해주세요.",
15
15
  "emptyKnowledgeList": "현재 지식 목록이 비어 있습니다. 대화 중에 필요에 따라 지식 베이스를 활성화한 후 다시 확인해 주세요.",
16
16
  "files": "파일",
17
+ "messageDetail": "메시지 세부정보",
17
18
  "title": "확장 창"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Hallo, ik ben **{{name}}**. Laten we beginnen met een gesprek!",
8
8
  "agentsAndConversations": "agenten en gesprekken",
9
9
  "backToBottom": "Terug naar onderen",
10
+ "chatList": {
11
+ "longMessageDetail": "Bekijk details"
12
+ },
10
13
  "clearCurrentMessages": "Huidige berichten wissen",
11
14
  "confirmClearCurrentMessages": "Huidige berichten worden gewist en kunnen niet worden hersteld. Bevestig je actie.",
12
15
  "confirmRemoveSessionItemAlert": "Deze assistent wordt verwijderd en kan niet worden hersteld. Bevestig je actie.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "De huidige lijst met Artifacts is leeg. Gebruik plugins in de sessie en bekijk deze later opnieuw.",
15
15
  "emptyKnowledgeList": "De huidige kennislijst is leeg. Gelieve de kennisbank in de sessie te openen voordat u deze bekijkt.",
16
16
  "files": "Bestanden",
17
+ "messageDetail": "Berichtdetails",
17
18
  "title": "Uitbreidingsvenster"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Cześć, jestem **{{name}}**. Zacznijmy rozmowę!",
8
8
  "agentsAndConversations": "Agenci i rozmowy",
9
9
  "backToBottom": "Przewiń na dół",
10
+ "chatList": {
11
+ "longMessageDetail": "Zobacz szczegóły"
12
+ },
10
13
  "clearCurrentMessages": "Wyczyść bieżącą rozmowę",
11
14
  "confirmClearCurrentMessages": "Czy na pewno chcesz wyczyścić bieżącą rozmowę? Tej operacji nie można cofnąć.",
12
15
  "confirmRemoveSessionItemAlert": "Czy na pewno chcesz usunąć tego asystenta? Tej operacji nie można cofnąć.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "Obecna lista Artefaktów jest pusta. Proszę użyć wtyczek w trakcie sesji, a następnie sprawdzić ponownie.",
15
15
  "emptyKnowledgeList": "Aktualna lista wiedzy jest pusta. Proszę otworzyć bazę wiedzy w trakcie rozmowy, aby ją przeglądać.",
16
16
  "files": "Pliki",
17
+ "messageDetail": "Szczegóły wiadomości",
17
18
  "title": "Okno rozszerzenia"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Olá, sou o **{{name}}**, vamos começar a conversa!",
8
8
  "agentsAndConversations": "Agentes e Conversas",
9
9
  "backToBottom": "Voltar para o início",
10
+ "chatList": {
11
+ "longMessageDetail": "Ver detalhes"
12
+ },
10
13
  "clearCurrentMessages": "Limpar mensagens atuais",
11
14
  "confirmClearCurrentMessages": "Você está prestes a limpar as mensagens desta sessão. Depois de limpar, não será possível recuperá-las. Por favor, confirme sua ação.",
12
15
  "confirmRemoveSessionItemAlert": "Você está prestes a remover este assistente. Depois de remover, não será possível recuperá-lo. Por favor, confirme sua ação.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "A lista de Artefatos atual está vazia. Por favor, use os plugins conforme necessário durante a sessão e depois verifique novamente.",
15
15
  "emptyKnowledgeList": "A lista de conhecimentos atual está vazia. Por favor, ative o repositório de conhecimentos conforme necessário durante a conversa antes de visualizar.",
16
16
  "files": "Arquivos",
17
+ "messageDetail": "Detalhes da mensagem",
17
18
  "title": "Janela de Expansão"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Привет, я **{{name}}**, давай начнём разговор!",
8
8
  "agentsAndConversations": "Агенты и беседы",
9
9
  "backToBottom": "Вернуться вниз",
10
+ "chatList": {
11
+ "longMessageDetail": "Посмотреть детали"
12
+ },
10
13
  "clearCurrentMessages": "Очистить текущий разговор",
11
14
  "confirmClearCurrentMessages": "Вы уверены, что хотите очистить текущий разговор? После этого его нельзя будет восстановить.",
12
15
  "confirmRemoveSessionItemAlert": "Вы уверены, что хотите удалить этого помощника? После этого его нельзя будет восстановить.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "Список текущих артефактов пуст. Пожалуйста, используйте плагины во время сеанса и затем просмотрите.",
15
15
  "emptyKnowledgeList": "Текущий список знаний пуст. Пожалуйста, откройте базу знаний по мере необходимости в разговоре, прежде чем просматривать.",
16
16
  "files": "файлы",
17
+ "messageDetail": "Детали сообщения",
17
18
  "title": "Расширенное окно"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Merhaba, ben **{{name}}**. Konuşmaya başlayalım!",
8
8
  "agentsAndConversations": "Ajanlar ve Konuşmalar",
9
9
  "backToBottom": "En alta git",
10
+ "chatList": {
11
+ "longMessageDetail": "Detayları görüntüle"
12
+ },
10
13
  "clearCurrentMessages": "Mevcut oturum mesajlarını temizle",
11
14
  "confirmClearCurrentMessages": "Mevcut oturum mesajlarını temizlemek üzeresiniz. Temizlendikten sonra geri alınamazlar. Lütfen eyleminizi onaylayın.",
12
15
  "confirmRemoveSessionItemAlert": "Bu asistanı silmek üzeresiniz. Silindikten sonra geri alınamaz. Lütfen eyleminizi onaylayın.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "Mevcut Artefakt listesi boş, lütfen eklentileri kullanarak oturumda gerektiğinde göz atın",
15
15
  "emptyKnowledgeList": "Mevcut bilgi listesi boş. Lütfen sohbet sırasında ihtiyaç duyduğunuz bilgi havuzunu açtıktan sonra tekrar kontrol edin.",
16
16
  "files": "Dosyalar",
17
+ "messageDetail": "Mesaj Detayı",
17
18
  "title": "Genişletme Penceresi"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "Xin chào, tôi là **{{name}}**, chúng ta hãy bắt đầu trò chuyện nào!",
8
8
  "agentsAndConversations": "Người hỗ trợ và cuộc trò chuyện",
9
9
  "backToBottom": "Quay về dưới cùng",
10
+ "chatList": {
11
+ "longMessageDetail": "Xem chi tiết"
12
+ },
10
13
  "clearCurrentMessages": "Xóa tin nhắn hiện tại",
11
14
  "confirmClearCurrentMessages": "Bạn sắp xóa tin nhắn hiện tại. Hành động này không thể hoàn tác, vui lòng xác nhận.",
12
15
  "confirmRemoveSessionItemAlert": "Bạn sắp xóa trợ lý này. Hành động này không thể hoàn tác, vui lòng xác nhận.",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "Danh sách Tác Phẩm hiện tại đang trống, vui lòng sử dụng các plugin trong cuộc trò chuyện trước khi xem lại",
15
15
  "emptyKnowledgeList": "Danh sách kiến thức hiện tại trống, vui lòng mở kho kiến thức khi cần trong cuộc trò chuyện trước khi xem",
16
16
  "files": "Tập tin",
17
+ "messageDetail": "Chi tiết tin nhắn",
17
18
  "title": "Cửa sổ mở rộng"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "你好,我是 **{{name}}**,让我们开始对话吧!",
8
8
  "agentsAndConversations": "助手与会话",
9
9
  "backToBottom": "跳转至当前",
10
+ "chatList": {
11
+ "longMessageDetail": "查看详情"
12
+ },
10
13
  "clearCurrentMessages": "清空当前会话消息",
11
14
  "confirmClearCurrentMessages": "即将清空当前会话消息,清空后将无法找回,请确认你的操作",
12
15
  "confirmRemoveSessionItemAlert": "即将删除该助手,删除后该将无法找回,请确认你的操作",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "当前 Artifacts 列表为空,请在会话中按需使用插件后再查看",
15
15
  "emptyKnowledgeList": "当前知识列表为空,请在会话中按需开启知识库后再查看",
16
16
  "files": "文件",
17
+ "messageDetail": "消息详情",
17
18
  "title": "工作区"
18
19
  }
@@ -7,6 +7,9 @@
7
7
  "agentDefaultMessageWithoutEdit": "你好,我是 **{{name}}**,讓我們開始對話吧!",
8
8
  "agentsAndConversations": "助理與對話",
9
9
  "backToBottom": "返回底部",
10
+ "chatList": {
11
+ "longMessageDetail": "查看詳情"
12
+ },
10
13
  "clearCurrentMessages": "清空當前對話",
11
14
  "confirmClearCurrentMessages": "即將清空當前對話,清空後將無法找回,請確認你的操作",
12
15
  "confirmRemoveSessionItemAlert": "即將刪除該助手,刪除後將無法找回,請確認你的操作",
@@ -14,5 +14,6 @@
14
14
  "emptyArtifactList": "當前文物列表為空,請在會話中按需使用插件後再查看",
15
15
  "emptyKnowledgeList": "當前知識列表為空,請在會話中按需開啟知識庫後再查看",
16
16
  "files": "檔案",
17
+ "messageDetail": "消息詳情",
17
18
  "title": "擴展視窗"
18
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.15.18",
3
+ "version": "1.15.19",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -121,7 +121,7 @@
121
121
  "@lobehub/chat-plugins-gateway": "^1.9.0",
122
122
  "@lobehub/icons": "^1.30.0",
123
123
  "@lobehub/tts": "^1.24.3",
124
- "@lobehub/ui": "^1.149.2",
124
+ "@lobehub/ui": "^1.150.2",
125
125
  "@neondatabase/serverless": "^0.9.4",
126
126
  "@next/third-parties": "^14.2.6",
127
127
  "@react-spring/web": "^9.7.3",
@@ -168,7 +168,7 @@
168
168
  "mammoth": "^1.8.0",
169
169
  "modern-screenshot": "^4.4.39",
170
170
  "nanoid": "^5.0.7",
171
- "next": "^14.2.6",
171
+ "next": "^14.2.8",
172
172
  "next-auth": "5.0.0-beta.15",
173
173
  "next-sitemap": "^4.2.3",
174
174
  "numeral": "^2.0.6",
@@ -0,0 +1,45 @@
1
+ import { Markdown } from '@lobehub/ui';
2
+ import { css, cx } from 'antd-style';
3
+ import isEqual from 'fast-deep-equal';
4
+ import { useEffect } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { useChatStore } from '@/store/chat';
8
+ import { chatPortalSelectors, chatSelectors } from '@/store/chat/selectors';
9
+
10
+ const md = css`
11
+ overflow: scroll;
12
+
13
+ > div {
14
+ padding-block-end: 40px;
15
+ }
16
+ `;
17
+
18
+ const MessageDetailBody = () => {
19
+ const [messageDetailId, togglePortal] = useChatStore((s) => [
20
+ chatPortalSelectors.messageDetailId(s),
21
+ s.togglePortal,
22
+ ]);
23
+
24
+ const message = useChatStore(chatSelectors.getMessageById(messageDetailId || ''), isEqual);
25
+
26
+ const content = message?.content || '';
27
+
28
+ useEffect(() => {
29
+ if (!message) {
30
+ togglePortal(false);
31
+ }
32
+ }, [message]);
33
+
34
+ return (
35
+ <Flexbox height={'100%'} paddingBlock={'0 12px'} paddingInline={8}>
36
+ {!!content && (
37
+ <Markdown className={cx(md)} variant={'chat'}>
38
+ {content}
39
+ </Markdown>
40
+ )}
41
+ </Flexbox>
42
+ );
43
+ };
44
+
45
+ export default MessageDetailBody;
@@ -0,0 +1,19 @@
1
+ import { Typography } from 'antd';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import { oneLineEllipsis } from '@/styles';
6
+
7
+ const Header = () => {
8
+ const { t } = useTranslation('portal');
9
+
10
+ return (
11
+ <Flexbox align={'center'} gap={4} horizontal>
12
+ <Typography.Text className={oneLineEllipsis} style={{ fontSize: 16 }} type={'secondary'}>
13
+ {t('messageDetail')}
14
+ </Typography.Text>
15
+ </Flexbox>
16
+ );
17
+ };
18
+
19
+ export default Header;
@@ -0,0 +1,9 @@
1
+ import Body from './Body';
2
+ import Header from './Header';
3
+ import { useEnable } from './useEnable';
4
+
5
+ export const MessageDetail = {
6
+ Body,
7
+ Header,
8
+ useEnable,
9
+ };
@@ -0,0 +1,4 @@
1
+ import { useChatStore } from '@/store/chat';
2
+ import { chatPortalSelectors } from '@/store/chat/selectors';
3
+
4
+ export const useEnable = () => useChatStore(chatPortalSelectors.showMessageDetail);
@@ -5,8 +5,9 @@ import { memo } from 'react';
5
5
  import { Artifacts } from './Artifacts';
6
6
  import { FilePreview } from './FilePreview';
7
7
  import { HomeBody, HomeHeader } from './Home';
8
+ import { MessageDetail } from './MessageDetail';
8
9
 
9
- const items = [Artifacts, FilePreview];
10
+ const items = [MessageDetail, Artifacts, FilePreview];
10
11
 
11
12
  export const PortalHeader = memo(() => {
12
13
  const enabledList: boolean[] = [];
@@ -0,0 +1,58 @@
1
+ import { Markdown } from '@lobehub/ui';
2
+ import { Button } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { useChatStore } from '@/store/chat';
8
+ import { convertAlphaToSolid } from '@/utils/colorUtils';
9
+
10
+ const useStyles = createStyles(({ css, token, isDarkMode }, displayMode: 'chat' | 'docs') => {
11
+ const darkBg = convertAlphaToSolid(token.colorFillSecondary, token.colorBgContainer);
12
+
13
+ const maskBgColor =
14
+ displayMode === 'docs' ? token.colorBgLayout : isDarkMode ? darkBg : token.colorBgContainer;
15
+
16
+ return {
17
+ mask: css`
18
+ position: absolute;
19
+ inset-block: 0 0;
20
+ width: 100%;
21
+ background: linear-gradient(0deg, ${maskBgColor} 0%, transparent 50%);
22
+ `,
23
+ };
24
+ });
25
+
26
+ interface ContentPreviewProps {
27
+ content: string;
28
+ displayMode: 'chat' | 'docs';
29
+ id: string;
30
+ }
31
+
32
+ const ContentPreview = ({ content, id, displayMode }: ContentPreviewProps) => {
33
+ const { t } = useTranslation('chat');
34
+
35
+ const [openMessageDetail] = useChatStore((s) => [s.openMessageDetail]);
36
+
37
+ const { styles } = useStyles(displayMode);
38
+ return (
39
+ <Flexbox>
40
+ <Flexbox style={{ position: 'relative' }}>
41
+ <Markdown variant={'chat'}>{content.slice(0, 1000)}</Markdown>
42
+ <div className={styles.mask} />
43
+ </Flexbox>
44
+ <Flexbox padding={4}>
45
+ <Button
46
+ block
47
+ onClick={() => {
48
+ openMessageDetail(id);
49
+ }}
50
+ size={'small'}
51
+ >
52
+ {t('chatList.longMessageDetail')}
53
+ </Button>
54
+ </Flexbox>
55
+ </Flexbox>
56
+ );
57
+ };
58
+ export default ContentPreview;
@@ -0,0 +1,15 @@
1
+ import { Flexbox } from 'react-layout-kit';
2
+
3
+ import { MarkdownCustomRender } from '../../../types';
4
+ import ContentPreview from './ContentPreview';
5
+
6
+ export const MarkdownRender: MarkdownCustomRender = ({ text, dom, id, displayMode }) => {
7
+ if (text.length > 30_000)
8
+ return (
9
+ <Flexbox>
10
+ <ContentPreview content={text} displayMode={displayMode} id={id} />
11
+ </Flexbox>
12
+ );
13
+
14
+ return dom;
15
+ };
@@ -29,3 +29,4 @@ export const UserMessage = memo<
29
29
  });
30
30
 
31
31
  export * from './BelowMessage';
32
+ export { MarkdownRender as UserMarkdownRender } from './MarkdownRender';
@@ -3,11 +3,11 @@ import { useGlobalStore } from '@/store/global';
3
3
  import { useSessionStore } from '@/store/session';
4
4
  import { sessionSelectors } from '@/store/session/selectors';
5
5
 
6
- import { OnAvatarsClick, RenderBelowMessage, RenderMessage } from '../types';
6
+ import { MarkdownCustomRender, OnAvatarsClick, RenderBelowMessage, RenderMessage } from '../types';
7
7
  import { AssistantMessage } from './Assistant';
8
8
  import { DefaultBelowMessage, DefaultMessage } from './Default';
9
9
  import { ToolMessage } from './Tool';
10
- import { UserBelowMessage, UserMessage } from './User';
10
+ import { UserBelowMessage, UserMarkdownRender, UserMessage } from './User';
11
11
 
12
12
  export const renderMessages: Record<string, RenderMessage> = {
13
13
  assistant: AssistantMessage,
@@ -22,6 +22,10 @@ export const renderBelowMessages: Record<string, RenderBelowMessage> = {
22
22
  user: UserBelowMessage,
23
23
  };
24
24
 
25
+ export const markdownCustomRenders: Record<string, MarkdownCustomRender> = {
26
+ user: UserMarkdownRender,
27
+ };
28
+
25
29
  export const useAvatarsClick = (): OnAvatarsClick => {
26
30
  const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
27
31
  const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
@@ -16,7 +16,12 @@ import { ChatMessage } from '@/types/message';
16
16
 
17
17
  import ErrorMessageExtra, { useErrorContent } from '../../Error';
18
18
  import { renderMessagesExtra } from '../../Extras';
19
- import { renderBelowMessages, renderMessages, useAvatarsClick } from '../../Messages';
19
+ import {
20
+ markdownCustomRenders,
21
+ renderBelowMessages,
22
+ renderMessages,
23
+ useAvatarsClick,
24
+ } from '../../Messages';
20
25
  import ActionsBar from './ActionsBar';
21
26
  import HistoryDivider from './HistoryDivider';
22
27
 
@@ -104,7 +109,7 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
104
109
 
105
110
  const MessageExtra = useCallback(
106
111
  ({ data }: { data: ChatMessage }) => {
107
- if (!renderMessagesExtra || !item?.role) return;
112
+ if (!item?.role) return;
108
113
  let RenderFunction;
109
114
  if (renderMessagesExtra?.[item.role]) RenderFunction = renderMessagesExtra[item.role];
110
115
 
@@ -113,6 +118,20 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
113
118
  },
114
119
  [item?.role],
115
120
  );
121
+
122
+ const markdownCustomRender = useCallback(
123
+ (dom: ReactNode, { text }: { text: string }) => {
124
+ if (!item?.role) return dom;
125
+ let RenderFunction;
126
+
127
+ if (renderMessagesExtra?.[item.role]) RenderFunction = markdownCustomRenders[item.role];
128
+ if (!RenderFunction) return dom;
129
+
130
+ return <RenderFunction displayMode={type} dom={dom} id={id} text={text} />;
131
+ },
132
+ [item?.role, type],
133
+ );
134
+
116
135
  const error = useErrorContent(item?.error);
117
136
 
118
137
  const enableHistoryDivider = useAgentStore((s) => {
@@ -145,6 +164,9 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
145
164
  errorMessage={<ErrorMessageExtra data={item} />}
146
165
  fontSize={fontSize}
147
166
  loading={isProcessing}
167
+ markdownProps={{
168
+ customRender: markdownCustomRender,
169
+ }}
148
170
  message={item.content}
149
171
  messageExtra={<MessageExtra data={item} />}
150
172
  onAvatarClick={onAvatarsClick?.(item.role)}
@@ -13,6 +13,12 @@ export type RenderRole = LLMRoleType | 'default' | string;
13
13
  export type RenderMessage = FC<ChatMessage & { editableContent: ReactNode }>;
14
14
  export type RenderBelowMessage = FC<ChatMessage>;
15
15
  export type RenderMessageExtra = FC<ChatMessage>;
16
+ export type MarkdownCustomRender = (props: {
17
+ displayMode: 'chat' | 'docs';
18
+ dom: ReactNode;
19
+ id: string;
20
+ text: string;
21
+ }) => ReactNode;
16
22
 
17
23
  export type RenderAction = FC<ActionsBarProps & ChatMessage>;
18
24
 
@@ -8,6 +8,9 @@ export default {
8
8
  agentDefaultMessageWithoutEdit: '你好,我是 **{{name}}**,让我们开始对话吧!',
9
9
  agentsAndConversations: '助手与会话',
10
10
  backToBottom: '跳转至当前',
11
+ chatList: {
12
+ longMessageDetail: '查看详情',
13
+ },
11
14
  clearCurrentMessages: '清空当前会话消息',
12
15
  confirmClearCurrentMessages: '即将清空当前会话消息,清空后将无法找回,请确认你的操作',
13
16
  confirmRemoveSessionItemAlert: '即将删除该助手,删除后该将无法找回,请确认你的操作',
@@ -14,5 +14,6 @@ export default {
14
14
  emptyArtifactList: '当前 Artifacts 列表为空,请在会话中按需使用插件后再查看',
15
15
  emptyKnowledgeList: '当前知识列表为空,请在会话中按需开启知识库后再查看',
16
16
  files: '文件',
17
+ messageDetail: '消息详情',
17
18
  title: '工作区',
18
19
  };
@@ -319,7 +319,9 @@ class ChatService {
319
319
  onFinish: options?.onFinish,
320
320
  onMessageHandle: options?.onMessageHandle,
321
321
  signal,
322
- smoothing: providerConfig?.smoothing,
322
+ // use smoothing when enable client fetch
323
+ // https://github.com/lobehub/lobe-chat/issues/3800
324
+ smoothing: providerConfig?.smoothing || enableFetchOnClient,
323
325
  });
324
326
  };
325
327
 
@@ -1,13 +1,17 @@
1
1
  import { StateCreator } from 'zustand/vanilla';
2
2
 
3
- import { PortalFile } from '@/store/chat/slices/portal/initialState';
4
3
  import { ChatStore } from '@/store/chat/store';
5
4
 
5
+ import { PortalFile } from './initialState';
6
+
6
7
  export interface ChatPortalAction {
7
8
  closeFilePreview: () => void;
9
+ closeMessageDetail: () => void;
8
10
  closeToolUI: () => void;
9
11
  openFilePreview: (portal: PortalFile) => void;
12
+ openMessageDetail: (messageId: string) => void;
10
13
  openToolUI: (messageId: string, identifier: string) => void;
14
+
11
15
  togglePortal: (open?: boolean) => void;
12
16
  }
13
17
 
@@ -20,20 +24,24 @@ export const chatPortalSlice: StateCreator<
20
24
  closeFilePreview: () => {
21
25
  set({ portalFile: undefined }, false, 'closeFilePreview');
22
26
  },
27
+ closeMessageDetail: () => {
28
+ set({ portalMessageDetail: undefined }, false, 'openMessageDetail');
29
+ },
23
30
  closeToolUI: () => {
24
31
  set({ portalToolMessage: undefined }, false, 'closeToolUI');
25
32
  },
26
33
  openFilePreview: (portal) => {
27
- if (!get().showPortal) {
28
- get().togglePortal(true);
29
- }
34
+ get().togglePortal(true);
30
35
 
31
36
  set({ portalFile: portal }, false, 'openFilePreview');
32
37
  },
38
+ openMessageDetail: (messageId) => {
39
+ get().togglePortal(true);
40
+
41
+ set({ portalMessageDetail: messageId }, false, 'openMessageDetail');
42
+ },
33
43
  openToolUI: (id, identifier) => {
34
- if (!get().showPortal) {
35
- get().togglePortal(true);
36
- }
44
+ get().togglePortal(true);
37
45
 
38
46
  set({ portalToolMessage: { id, identifier } }, false, 'openToolUI');
39
47
  },
@@ -6,6 +6,7 @@ export interface PortalFile {
6
6
 
7
7
  export interface ChatPortalState {
8
8
  portalFile?: PortalFile;
9
+ portalMessageDetail?: string;
9
10
  portalToolMessage?: { id: string; identifier: string };
10
11
  showPortal: boolean;
11
12
  }
@@ -8,16 +8,20 @@ const isArtifactMessageUIOpen = (id: string) => (s: ChatStoreState) =>
8
8
 
9
9
  const showArtifactUI = (s: ChatStoreState) => !!s.portalToolMessage;
10
10
  const showFilePreview = (s: ChatStoreState) => !!s.portalFile;
11
+ const showMessageDetail = (s: ChatStoreState) => !!s.portalMessageDetail;
11
12
  const previewFileId = (s: ChatStoreState) => s.portalFile?.fileId;
13
+ const messageDetailId = (s: ChatStoreState) => s.portalMessageDetail;
12
14
  const chunkText = (s: ChatStoreState) => s.portalFile?.chunkText;
13
15
 
14
16
  export const chatPortalSelectors = {
15
17
  artifactMessageId,
16
18
  chunkText,
17
19
  isArtifactMessageUIOpen,
20
+ messageDetailId,
18
21
  previewFileId,
19
22
  showArtifactUI,
20
23
  showFilePreview,
24
+ showMessageDetail,
21
25
  showPortal,
22
26
  toolUIIdentifier: (s: ChatStoreState) => s.portalToolMessage?.identifier,
23
27
  };
@@ -3,9 +3,7 @@ addEventListener('message', async (event) => {
3
3
  try {
4
4
  const { encode } = await import('gpt-tokenizer');
5
5
 
6
- console.time('client tokenizer');
7
6
  const tokenCount = encode(str).length;
8
- console.timeEnd('client tokenizer');
9
7
 
10
8
  postMessage({ id, result: tokenCount });
11
9
  } catch (error) {