@lobehub/chat 1.75.5 → 1.76.1

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 (123) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/Dockerfile +3 -2
  3. package/Dockerfile.database +3 -1
  4. package/Dockerfile.pglite +3 -1
  5. package/changelog/v1.json +18 -0
  6. package/docs/developer/database-schema.dbml +1 -0
  7. package/locales/ar/hotkey.json +46 -0
  8. package/locales/ar/models.json +3 -0
  9. package/locales/ar/setting.json +12 -0
  10. package/locales/bg-BG/hotkey.json +46 -0
  11. package/locales/bg-BG/models.json +3 -0
  12. package/locales/bg-BG/setting.json +12 -0
  13. package/locales/de-DE/hotkey.json +46 -0
  14. package/locales/de-DE/models.json +3 -0
  15. package/locales/de-DE/setting.json +12 -0
  16. package/locales/en-US/hotkey.json +46 -0
  17. package/locales/en-US/models.json +3 -0
  18. package/locales/en-US/setting.json +12 -0
  19. package/locales/es-ES/hotkey.json +46 -0
  20. package/locales/es-ES/models.json +3 -0
  21. package/locales/es-ES/setting.json +12 -0
  22. package/locales/fa-IR/hotkey.json +46 -0
  23. package/locales/fa-IR/models.json +3 -0
  24. package/locales/fa-IR/setting.json +12 -0
  25. package/locales/fr-FR/hotkey.json +46 -0
  26. package/locales/fr-FR/models.json +3 -0
  27. package/locales/fr-FR/setting.json +12 -0
  28. package/locales/it-IT/hotkey.json +46 -0
  29. package/locales/it-IT/models.json +3 -0
  30. package/locales/it-IT/setting.json +12 -0
  31. package/locales/ja-JP/hotkey.json +46 -0
  32. package/locales/ja-JP/models.json +3 -0
  33. package/locales/ja-JP/setting.json +12 -0
  34. package/locales/ko-KR/hotkey.json +46 -0
  35. package/locales/ko-KR/models.json +3 -0
  36. package/locales/ko-KR/setting.json +12 -0
  37. package/locales/nl-NL/hotkey.json +46 -0
  38. package/locales/nl-NL/models.json +3 -0
  39. package/locales/nl-NL/setting.json +12 -0
  40. package/locales/pl-PL/hotkey.json +46 -0
  41. package/locales/pl-PL/models.json +3 -0
  42. package/locales/pl-PL/setting.json +12 -0
  43. package/locales/pt-BR/hotkey.json +46 -0
  44. package/locales/pt-BR/models.json +3 -0
  45. package/locales/pt-BR/setting.json +12 -0
  46. package/locales/ru-RU/hotkey.json +46 -0
  47. package/locales/ru-RU/models.json +3 -0
  48. package/locales/ru-RU/setting.json +12 -0
  49. package/locales/tr-TR/hotkey.json +46 -0
  50. package/locales/tr-TR/models.json +3 -0
  51. package/locales/tr-TR/setting.json +12 -0
  52. package/locales/vi-VN/hotkey.json +46 -0
  53. package/locales/vi-VN/models.json +3 -0
  54. package/locales/vi-VN/setting.json +12 -0
  55. package/locales/zh-CN/hotkey.json +46 -0
  56. package/locales/zh-CN/models.json +3 -0
  57. package/locales/zh-CN/setting.json +12 -0
  58. package/locales/zh-TW/hotkey.json +46 -0
  59. package/locales/zh-TW/models.json +3 -0
  60. package/locales/zh-TW/setting.json +12 -0
  61. package/package.json +3 -3
  62. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/Category.tsx +1 -1
  63. package/src/app/[variants]/(main)/(mobile)/me/(home)/layout.tsx +3 -2
  64. package/src/app/[variants]/(main)/(mobile)/me/data/features/Category.tsx +1 -1
  65. package/src/app/[variants]/(main)/(mobile)/me/profile/features/Category.tsx +1 -1
  66. package/src/app/[variants]/(main)/(mobile)/me/settings/features/Category.tsx +1 -1
  67. package/src/app/[variants]/(main)/_layout/Desktop/RegisterHotkeys.tsx +11 -0
  68. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/PinList/index.tsx +6 -23
  69. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.test.tsx +2 -0
  70. package/src/app/[variants]/(main)/_layout/Desktop/index.tsx +11 -4
  71. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx +6 -21
  72. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/ShortcutHint.tsx +13 -34
  73. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +1 -1
  74. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ZenModeToast/Toast.tsx +7 -4
  75. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx +12 -8
  76. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +24 -30
  77. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/index.tsx +0 -2
  78. package/src/app/[variants]/(main)/chat/(workspace)/features/SettingButton.tsx +12 -7
  79. package/src/app/[variants]/(main)/chat/@session/features/SessionSearchBar.tsx +5 -1
  80. package/src/app/[variants]/(main)/chat/_layout/Desktop/RegisterHotkeys.tsx +10 -0
  81. package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +5 -0
  82. package/src/app/[variants]/(main)/chat/_layout/Mobile.tsx +1 -1
  83. package/src/app/[variants]/(main)/discover/features/StoreSearchBar.tsx +5 -1
  84. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +31 -21
  85. package/src/app/[variants]/(main)/settings/hotkey/features/HotkeySetting.tsx +80 -0
  86. package/src/app/[variants]/(main)/settings/hotkey/index.tsx +9 -0
  87. package/src/app/[variants]/(main)/settings/hotkey/page.tsx +15 -0
  88. package/src/app/[variants]/(main)/settings/tts/features/const.tsx +4 -0
  89. package/src/app/[variants]/layout.tsx +16 -13
  90. package/src/config/aiModels/openai.ts +10 -0
  91. package/src/const/hotkeys.ts +80 -10
  92. package/src/const/settings/hotkey.ts +10 -0
  93. package/src/const/settings/index.ts +3 -0
  94. package/src/database/client/migrations.json +46 -32
  95. package/src/database/migrations/0019_add_hotkey_user_settings.sql +2 -0
  96. package/src/database/migrations/meta/0019_snapshot.json +4218 -0
  97. package/src/database/migrations/meta/_journal.json +7 -0
  98. package/src/database/schemas/user.ts +1 -0
  99. package/src/database/server/models/user.ts +2 -0
  100. package/src/features/ChatInput/Desktop/InputArea/index.tsx +8 -0
  101. package/src/features/ChatInput/Desktop/index.tsx +0 -1
  102. package/src/features/ChatInput/Topic/index.tsx +10 -15
  103. package/src/features/FileManager/Header/FilesSearchBar.tsx +6 -2
  104. package/src/features/HotkeyHelperPanel/HotkeyContent.tsx +62 -0
  105. package/src/features/HotkeyHelperPanel/index.tsx +59 -0
  106. package/src/hooks/useHotkeys/chatScope.ts +105 -0
  107. package/src/hooks/useHotkeys/globalScope.ts +69 -0
  108. package/src/hooks/useHotkeys/index.ts +2 -0
  109. package/src/hooks/useHotkeys/useHotkeyById.test.ts +194 -0
  110. package/src/hooks/useHotkeys/useHotkeyById.ts +57 -0
  111. package/src/locales/default/hotkey.ts +50 -0
  112. package/src/locales/default/index.ts +2 -0
  113. package/src/locales/default/setting.ts +12 -0
  114. package/src/store/global/initialState.ts +3 -0
  115. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +79 -0
  116. package/src/store/user/slices/settings/selectors/settings.test.ts +131 -0
  117. package/src/store/user/slices/settings/selectors/settings.ts +6 -0
  118. package/src/types/hotkey.ts +59 -0
  119. package/src/types/user/settings/hotkey.ts +3 -0
  120. package/src/types/user/settings/index.ts +3 -0
  121. package/src/types/user/settings/tts.ts +1 -1
  122. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/HotKeys.tsx +0 -44
  123. package/src/components/HotKeys/index.tsx +0 -77
@@ -42,6 +42,17 @@
42
42
  "sessionWithName": "Настройки сеанса · {{name}}",
43
43
  "title": "Настройки"
44
44
  },
45
+ "hotkey": {
46
+ "conflicts": "Конфликт с существующими горячими клавишами",
47
+ "group": {
48
+ "conversation": "Беседа",
49
+ "essential": "Основной"
50
+ },
51
+ "invalidCombination": "Горячая клавиша должна содержать как минимум одну модификаторную клавишу (Ctrl, Alt, Shift) и одну обычную клавишу",
52
+ "record": "Нажмите клавишу для записи горячей клавиши",
53
+ "reset": "Сбросить на стандартные горячие клавиши",
54
+ "title": "Горячие клавиши"
55
+ },
45
56
  "llm": {
46
57
  "aesGcm": "Ваши ключи и адреса агентов будут зашифрованы с использованием алгоритма шифрования <1>AES-GCM</1>",
47
58
  "apiKey": {
@@ -425,6 +436,7 @@
425
436
  "agent": "Помощник по умолчанию",
426
437
  "common": "Общие настройки",
427
438
  "experiment": "Эксперимент",
439
+ "hotkey": "Горячие клавиши",
428
440
  "llm": "Языковая модель",
429
441
  "provider": "Поставщик ИИ услуг",
430
442
  "sync": "Синхронизация с облаком",
@@ -0,0 +1,46 @@
1
+ {
2
+ "addUserMessage": {
3
+ "desc": "Mevcut girdi içeriğini kullanıcı mesajı olarak ekleyin, ancak oluşturmayı tetiklemeyin",
4
+ "title": "Bir kullanıcı mesajı ekle"
5
+ },
6
+ "editMessage": {
7
+ "desc": "Mesaja çift tıklayıp Alt tuşuna basarak düzenleme moduna geçin",
8
+ "title": "Mesajı düzenle"
9
+ },
10
+ "openChatSettings": {
11
+ "desc": "Mevcut oturumun ayarlarını görüntüle ve değiştir",
12
+ "title": "Sohbet ayarlarını aç"
13
+ },
14
+ "openHotkeyHelper": {
15
+ "desc": "Tüm kısayol tuşlarının kullanım talimatlarını görüntüle",
16
+ "title": "Kısayol Yardımını Aç"
17
+ },
18
+ "regenerateMessage": {
19
+ "desc": "Son mesajı yeniden oluştur",
20
+ "title": "Mesajı yeniden oluştur"
21
+ },
22
+ "saveTopic": {
23
+ "desc": "Mevcut konuyu kaydedin ve yeni bir konu açın",
24
+ "title": "Yeni konu aç"
25
+ },
26
+ "search": {
27
+ "desc": "Mevcut sayfanın ana arama kutusunu aç",
28
+ "title": "Ara"
29
+ },
30
+ "switchAgent": {
31
+ "desc": "Yan panelde sabitlenmiş asistanı değiştirmek için Ctrl tuşuna basılı tutarak 0~9 sayısına basın",
32
+ "title": "Asistanı hızlıca değiştir"
33
+ },
34
+ "toggleLeftPanel": {
35
+ "desc": "Sol yan paneli göster veya gizle",
36
+ "title": "Asistan panelini göster/gizle"
37
+ },
38
+ "toggleRightPanel": {
39
+ "desc": "Sağ konu panelini göster veya gizle",
40
+ "title": "Konu panelini göster/gizle"
41
+ },
42
+ "toggleZenMode": {
43
+ "desc": "Odak modu, yalnızca mevcut oturumu gösterir, diğer UI'ları gizler",
44
+ "title": "Odak modunu değiştir"
45
+ }
46
+ }
@@ -1082,6 +1082,9 @@
1082
1082
  "gpt-4o-mini-realtime-preview": {
1083
1083
  "description": "GPT-4o-mini gerçek zamanlı versiyonu, ses ve metin için gerçek zamanlı giriş ve çıkış desteği sunar."
1084
1084
  },
1085
+ "gpt-4o-mini-tts": {
1086
+ "description": "GPT-4o mini TTS, GPT-4o mini'ye dayalı bir metin-ses modeldir ve yüksek kaliteli ses üretimi, düşük maliyetli oluşturma sunar."
1087
+ },
1085
1088
  "gpt-4o-realtime-preview": {
1086
1089
  "description": "GPT-4o gerçek zamanlı versiyonu, ses ve metin için gerçek zamanlı giriş ve çıkış desteği sunar."
1087
1090
  },
@@ -42,6 +42,17 @@
42
42
  "sessionWithName": "Oturum Ayarları · {{name}}",
43
43
  "title": "Ayarlar"
44
44
  },
45
+ "hotkey": {
46
+ "conflicts": "Mevcut kısayol tuşlarıyla çakışıyor",
47
+ "group": {
48
+ "conversation": "Sohbet",
49
+ "essential": "Temel"
50
+ },
51
+ "invalidCombination": "Kısayol tuşu en az bir modifiye tuşu (Ctrl, Alt, Shift) ve bir normal tuş içermelidir",
52
+ "record": "Kısayol tuşunu kaydetmek için tuşa basın",
53
+ "reset": "Varsayılan kısayol tuşlarına sıfırla",
54
+ "title": "Kısayollar"
55
+ },
45
56
  "llm": {
46
57
  "aesGcm": "Anahtarınız ve vekil adresiniz <1>AES-GCM</1> şifreleme algoritması kullanılarak şifrelenecektir",
47
58
  "apiKey": {
@@ -425,6 +436,7 @@
425
436
  "agent": "Varsayılan Asistan",
426
437
  "common": "Genel Ayarlar",
427
438
  "experiment": "Deney",
439
+ "hotkey": "Kısayollar",
428
440
  "llm": "Modeller",
429
441
  "provider": "Yapay Zeka Hizmet Sağlayıcısı",
430
442
  "sync": "Bulut Senkronizasyonu",
@@ -0,0 +1,46 @@
1
+ {
2
+ "addUserMessage": {
3
+ "desc": "Thêm nội dung hiện tại vào tin nhắn của người dùng mà không kích hoạt việc tạo mới",
4
+ "title": "Thêm một tin nhắn người dùng"
5
+ },
6
+ "editMessage": {
7
+ "desc": "Vào chế độ chỉnh sửa bằng cách giữ phím Alt và nhấp đúp vào tin nhắn",
8
+ "title": "Chỉnh sửa tin nhắn"
9
+ },
10
+ "openChatSettings": {
11
+ "desc": "Xem và chỉnh sửa cài đặt của cuộc trò chuyện hiện tại",
12
+ "title": "Mở cài đặt cuộc trò chuyện"
13
+ },
14
+ "openHotkeyHelper": {
15
+ "desc": "Xem hướng dẫn sử dụng tất cả các phím tắt",
16
+ "title": "Mở trợ giúp phím tắt"
17
+ },
18
+ "regenerateMessage": {
19
+ "desc": "Tạo lại tin nhắn cuối cùng",
20
+ "title": "Tạo lại tin nhắn"
21
+ },
22
+ "saveTopic": {
23
+ "desc": "Lưu chủ đề hiện tại và mở chủ đề mới",
24
+ "title": "Mở chủ đề mới"
25
+ },
26
+ "search": {
27
+ "desc": "Kích hoạt hộp tìm kiếm chính của trang hiện tại",
28
+ "title": "Tìm kiếm"
29
+ },
30
+ "switchAgent": {
31
+ "desc": "Chuyển đổi giữa các trợ lý cố định ở thanh bên bằng cách giữ phím Ctrl và nhấn số 0~9",
32
+ "title": "Chuyển đổi nhanh trợ lý"
33
+ },
34
+ "toggleLeftPanel": {
35
+ "desc": "Hiện hoặc ẩn bảng trợ giúp bên trái",
36
+ "title": "Hiện/Ẩn bảng trợ lý"
37
+ },
38
+ "toggleRightPanel": {
39
+ "desc": "Hiện hoặc ẩn bảng chủ đề bên phải",
40
+ "title": "Hiện/Ẩn bảng chủ đề"
41
+ },
42
+ "toggleZenMode": {
43
+ "desc": "Trong chế độ tập trung, chỉ hiển thị cuộc trò chuyện hiện tại, ẩn các giao diện người dùng khác",
44
+ "title": "Chuyển đổi chế độ tập trung"
45
+ }
46
+ }
@@ -1082,6 +1082,9 @@
1082
1082
  "gpt-4o-mini-realtime-preview": {
1083
1083
  "description": "Phiên bản thời gian thực của GPT-4o-mini, hỗ trợ đầu vào và đầu ra âm thanh và văn bản theo thời gian thực."
1084
1084
  },
1085
+ "gpt-4o-mini-tts": {
1086
+ "description": "GPT-4o mini TTS là mô hình chuyển văn bản thành giọng nói dựa trên GPT-4o mini, cung cấp sinh âm thanh cao cấp với chi phí thấp hơn."
1087
+ },
1085
1088
  "gpt-4o-realtime-preview": {
1086
1089
  "description": "Phiên bản thời gian thực của GPT-4o, hỗ trợ đầu vào và đầu ra âm thanh và văn bản theo thời gian thực."
1087
1090
  },
@@ -42,6 +42,17 @@
42
42
  "sessionWithName": "Cài đặt cuộc trò chuyện · {{name}}",
43
43
  "title": "Cài đặt"
44
44
  },
45
+ "hotkey": {
46
+ "conflicts": "Xung đột với phím tắt hiện có",
47
+ "group": {
48
+ "conversation": "Cuộc trò chuyện",
49
+ "essential": "Cơ bản"
50
+ },
51
+ "invalidCombination": "Phím tắt cần ít nhất một phím sửa đổi (Ctrl, Alt, Shift) và một phím thông thường",
52
+ "record": "Nhấn phím để ghi lại phím tắt",
53
+ "reset": "Đặt lại thành phím tắt mặc định",
54
+ "title": "Phím tắt"
55
+ },
45
56
  "llm": {
46
57
  "aesGcm": "Khóa và địa chỉ proxy của bạn sẽ được mã hóa bằng thuật toán mã hóa <1>AES-GCM</1>",
47
58
  "apiKey": {
@@ -425,6 +436,7 @@
425
436
  "agent": "Trợ lý mặc định",
426
437
  "common": "Cài đặt chung",
427
438
  "experiment": "Thử nghiệm",
439
+ "hotkey": "Phím tắt",
428
440
  "llm": "Mô hình ngôn ngữ",
429
441
  "provider": "Nhà cung cấp AI",
430
442
  "sync": "Đồng bộ trên đám mây",
@@ -0,0 +1,46 @@
1
+ {
2
+ "addUserMessage": {
3
+ "desc": "将当前输入内容添加为用户消息,但不触发生成",
4
+ "title": "添加一条用户消息"
5
+ },
6
+ "editMessage": {
7
+ "desc": "通过按住 Alt 并双击消息进入编辑模式",
8
+ "title": "编辑消息"
9
+ },
10
+ "openChatSettings": {
11
+ "desc": "查看和修改当前会话的设置",
12
+ "title": "打开会话设置"
13
+ },
14
+ "openHotkeyHelper": {
15
+ "desc": "查看所有快捷键的使用说明",
16
+ "title": "打开快捷键帮助"
17
+ },
18
+ "regenerateMessage": {
19
+ "desc": "重新生成最后一条消息",
20
+ "title": "重新生成消息"
21
+ },
22
+ "saveTopic": {
23
+ "desc": "保存当前话题并打开新话题",
24
+ "title": "开启新话题"
25
+ },
26
+ "search": {
27
+ "desc": "唤起当前页面主要搜索框",
28
+ "title": "搜索"
29
+ },
30
+ "switchAgent": {
31
+ "desc": "通过按住 Ctrl 加数字 0~9 切换固定在侧边栏的助手",
32
+ "title": "快捷切换助手"
33
+ },
34
+ "toggleLeftPanel": {
35
+ "desc": "显示或隐藏左侧助手面板",
36
+ "title": "显示/隐藏助手面板"
37
+ },
38
+ "toggleRightPanel": {
39
+ "desc": "显示或隐藏右侧话题面板",
40
+ "title": "显示/隐藏话题面板"
41
+ },
42
+ "toggleZenMode": {
43
+ "desc": "专注模式下,只显示当前会话,隐藏其他 UI",
44
+ "title": "切换专注模式"
45
+ }
46
+ }
@@ -1082,6 +1082,9 @@
1082
1082
  "gpt-4o-mini-realtime-preview": {
1083
1083
  "description": "GPT-4o-mini 实时版本,支持音频和文本实时输入输出"
1084
1084
  },
1085
+ "gpt-4o-mini-tts": {
1086
+ "description": "GPT-4o mini TTS 是基于 GPT-4o mini 的文本转语音模型,提供高品质的语音生成,同时降低成本。"
1087
+ },
1085
1088
  "gpt-4o-realtime-preview": {
1086
1089
  "description": "GPT-4o 实时版本,支持音频和文本实时输入输出"
1087
1090
  },
@@ -42,6 +42,17 @@
42
42
  "sessionWithName": "会话设置 · {{name}}",
43
43
  "title": "设置"
44
44
  },
45
+ "hotkey": {
46
+ "conflicts": "与现有快捷键冲突",
47
+ "group": {
48
+ "conversation": "会话",
49
+ "essential": "基础"
50
+ },
51
+ "invalidCombination": "快捷键需要至少包含一个修饰键 (Ctrl, Alt, Shift) 和一个常规键",
52
+ "record": "按下按键以录制快捷键",
53
+ "reset": "重置为默认快捷键",
54
+ "title": "快捷键"
55
+ },
45
56
  "llm": {
46
57
  "aesGcm": "您的秘钥与代理地址等将使用 <1>AES-GCM</1> 加密算法进行加密",
47
58
  "apiKey": {
@@ -425,6 +436,7 @@
425
436
  "agent": "默认助手",
426
437
  "common": "通用设置",
427
438
  "experiment": "实验",
439
+ "hotkey": "快捷键",
428
440
  "llm": "语言模型",
429
441
  "provider": "AI 服务商",
430
442
  "sync": "云端同步",
@@ -0,0 +1,46 @@
1
+ {
2
+ "addUserMessage": {
3
+ "desc": "將當前輸入內容添加為使用者消息,但不觸發生成",
4
+ "title": "添加一條使用者消息"
5
+ },
6
+ "editMessage": {
7
+ "desc": "通過按住 Alt 並雙擊消息進入編輯模式",
8
+ "title": "編輯消息"
9
+ },
10
+ "openChatSettings": {
11
+ "desc": "查看和修改當前會話的設定",
12
+ "title": "打開會話設定"
13
+ },
14
+ "openHotkeyHelper": {
15
+ "desc": "查看所有快捷鍵的使用說明",
16
+ "title": "打開快捷鍵幫助"
17
+ },
18
+ "regenerateMessage": {
19
+ "desc": "重新生成最後一條消息",
20
+ "title": "重新生成消息"
21
+ },
22
+ "saveTopic": {
23
+ "desc": "保存當前話題並打開新話題",
24
+ "title": "開啟新話題"
25
+ },
26
+ "search": {
27
+ "desc": "喚起當前頁面主要搜尋框",
28
+ "title": "搜尋"
29
+ },
30
+ "switchAgent": {
31
+ "desc": "通過按住 Ctrl 加數字 0~9 切換固定在側邊欄的助手",
32
+ "title": "快捷切換助手"
33
+ },
34
+ "toggleLeftPanel": {
35
+ "desc": "顯示或隱藏左側助手面板",
36
+ "title": "顯示/隱藏助手面板"
37
+ },
38
+ "toggleRightPanel": {
39
+ "desc": "顯示或隱藏右側話題面板",
40
+ "title": "顯示/隱藏話題面板"
41
+ },
42
+ "toggleZenMode": {
43
+ "desc": "專注模式下,只顯示當前會話,隱藏其他 UI",
44
+ "title": "切換專注模式"
45
+ }
46
+ }
@@ -1082,6 +1082,9 @@
1082
1082
  "gpt-4o-mini-realtime-preview": {
1083
1083
  "description": "GPT-4o-mini 實時版本,支持音頻和文本實時輸入輸出"
1084
1084
  },
1085
+ "gpt-4o-mini-tts": {
1086
+ "description": "GPT-4o mini TTS 是基於 GPT-4o mini 的文本轉語音模型,提供高品質的語音生成,同時降低成本。"
1087
+ },
1085
1088
  "gpt-4o-realtime-preview": {
1086
1089
  "description": "GPT-4o 實時版本,支持音頻和文本實時輸入輸出"
1087
1090
  },
@@ -42,6 +42,17 @@
42
42
  "sessionWithName": "對話設定 · {{name}}",
43
43
  "title": "設定"
44
44
  },
45
+ "hotkey": {
46
+ "conflicts": "與現有快捷鍵衝突",
47
+ "group": {
48
+ "conversation": "對話",
49
+ "essential": "基本"
50
+ },
51
+ "invalidCombination": "快捷鍵需要至少包含一個修飾鍵 (Ctrl, Alt, Shift) 和一個常規鍵",
52
+ "record": "按下按鍵以錄製快捷鍵",
53
+ "reset": "重置為預設快捷鍵",
54
+ "title": "快速鍵"
55
+ },
45
56
  "llm": {
46
57
  "aesGcm": "您的金鑰與代理地址等將使用 <1>AES-GCM</1> 加密演算法進行加密",
47
58
  "apiKey": {
@@ -425,6 +436,7 @@
425
436
  "agent": "默認助手",
426
437
  "common": "通用設置",
427
438
  "experiment": "實驗",
439
+ "hotkey": "快速鍵",
428
440
  "llm": "語言模型",
429
441
  "provider": "AI 服務商",
430
442
  "sync": "雲端同步",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.75.5",
3
+ "version": "1.76.1",
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",
@@ -135,7 +135,7 @@
135
135
  "@lobehub/chat-plugins-gateway": "^1.9.0",
136
136
  "@lobehub/icons": "^1.73.1",
137
137
  "@lobehub/tts": "^1.28.0",
138
- "@lobehub/ui": "^1.165.8",
138
+ "@lobehub/ui": "^1.169.2",
139
139
  "@neondatabase/serverless": "^0.10.4",
140
140
  "@next/third-parties": "15.2.3",
141
141
  "@react-spring/web": "^9.7.5",
@@ -192,7 +192,7 @@
192
192
  "next-mdx-remote": "^5.0.0",
193
193
  "nextjs-toploader": "^3.7.15",
194
194
  "numeral": "^2.0.6",
195
- "nuqs": "^1.20.0",
195
+ "nuqs": "^2.4.1",
196
196
  "officeparser": "^5.1.1",
197
197
  "ollama": "^0.5.11",
198
198
  "openai": "^4.77.3",
@@ -9,7 +9,7 @@ import { useCategory } from './useCategory';
9
9
  const Category = memo(() => {
10
10
  const items = useCategory();
11
11
 
12
- return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
12
+ return items?.map(({ key, ...item }, index) => <Cell key={key || index} {...item} />);
13
13
  });
14
14
 
15
15
  export default Category;
@@ -1,5 +1,6 @@
1
- import { PropsWithChildren } from 'react';
1
+ import { PropsWithChildren, Suspense } from 'react';
2
2
 
3
+ import Loading from '@/components/Loading/BrandTextLoading';
3
4
  import MobileContentLayout from '@/components/server/MobileNavLayout';
4
5
  import InitClientDB from '@/features/InitClientDB';
5
6
 
@@ -8,7 +9,7 @@ import Header from './features/Header';
8
9
  const Layout = ({ children }: PropsWithChildren) => {
9
10
  return (
10
11
  <MobileContentLayout header={<Header />} withNav>
11
- {children}
12
+ <Suspense fallback={<Loading />}>{children}</Suspense>
12
13
  <InitClientDB />
13
14
  </MobileContentLayout>
14
15
  );
@@ -42,7 +42,7 @@ const Category = memo(() => {
42
42
  },
43
43
  ];
44
44
 
45
- return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
45
+ return items?.map(({ key, ...item }, index) => <Cell key={key || index} {...item} />);
46
46
  });
47
47
 
48
48
  export default Category;
@@ -56,7 +56,7 @@ const Category = memo(() => {
56
56
  },
57
57
  ].filter(Boolean) as CellProps[];
58
58
 
59
- return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
59
+ return items?.map(({ key, ...item }, index) => <Cell key={key || index} {...item} />);
60
60
  });
61
61
 
62
62
  export default Category;
@@ -9,7 +9,7 @@ import { useCategory } from './useCategory';
9
9
  const Category = memo(() => {
10
10
  const items = useCategory();
11
11
 
12
- return items?.map((item, index) => <Cell {...item} key={item.key || index} />);
12
+ return items?.map(({ key, ...item }, index) => <Cell key={key || index} {...item} />);
13
13
  });
14
14
 
15
15
  export default Category;
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+
3
+ import { useRegisterGlobalHotkeys } from '@/hooks/useHotkeys';
4
+
5
+ const RegisterHotkeys = () => {
6
+ useRegisterGlobalHotkeys();
7
+
8
+ return null;
9
+ };
10
+
11
+ export default RegisterHotkeys;
@@ -3,14 +3,15 @@ import { Divider } from 'antd';
3
3
  import { createStyles } from 'antd-style';
4
4
  import isEqual from 'fast-deep-equal';
5
5
  import { parseAsBoolean, useQueryState } from 'nuqs';
6
- import { useHotkeys } from 'react-hotkeys-hook';
7
6
  import { Flexbox } from 'react-layout-kit';
8
7
 
9
- import HotKeys from '@/components/HotKeys';
10
8
  import { useSwitchSession } from '@/hooks/useSwitchSession';
11
9
  import { useSessionStore } from '@/store/session';
12
10
  import { sessionHelpers } from '@/store/session/helpers';
13
11
  import { sessionSelectors } from '@/store/session/selectors';
12
+ import { useUserStore } from '@/store/user';
13
+ import { settingsSelectors } from '@/store/user/selectors';
14
+ import { HotkeyEnum, KeyEnum } from '@/types/hotkey';
14
15
 
15
16
  const useStyles = createStyles(({ css, token }) => ({
16
17
  avatar: css`
@@ -52,7 +53,7 @@ const PinList = () => {
52
53
  const list = useSessionStore(sessionSelectors.pinnedSessions, isEqual);
53
54
  const [activeId] = useSessionStore((s) => [s.activeId]);
54
55
  const switchSession = useSwitchSession();
55
-
56
+ const hotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.SwitchAgent));
56
57
  const hasList = list.length > 0;
57
58
  const [isPinned, setPinned] = useQueryState('pinned', parseAsBoolean);
58
59
 
@@ -61,20 +62,6 @@ const PinList = () => {
61
62
  setPinned(true);
62
63
  };
63
64
 
64
- useHotkeys(
65
- list.slice(0, 9).map((e, i) => `ctrl+${i + 1}`),
66
- (keyboardEvent, hotkeysEvent) => {
67
- if (!hotkeysEvent.keys?.[0]) return;
68
-
69
- const index = parseInt(hotkeysEvent.keys?.[0]) - 1;
70
- const item = list[index];
71
- if (!item) return;
72
-
73
- switchAgent(item.id);
74
- },
75
- { enableOnFormTags: true, preventDefault: true },
76
- );
77
-
78
65
  return (
79
66
  hasList && (
80
67
  <>
@@ -83,13 +70,9 @@ const PinList = () => {
83
70
  {list.slice(0, 9).map((item, index) => (
84
71
  <Flexbox key={item.id} style={{ position: 'relative' }}>
85
72
  <Tooltip
73
+ hotkey={hotkey.replaceAll(KeyEnum.Number, String(index + 1))}
86
74
  placement={'right'}
87
- title={
88
- <Flexbox gap={8} horizontal>
89
- {sessionHelpers.getTitle(item.meta)}
90
- <HotKeys inverseTheme keys={`ctrl+${index + 1}`} />
91
- </Flexbox>
92
- }
75
+ title={sessionHelpers.getTitle(item.meta)}
93
76
  >
94
77
  <Flexbox
95
78
  className={cx(
@@ -36,6 +36,8 @@ vi.mock('next/link', () => ({
36
36
 
37
37
  vi.mock('@lobehub/ui', () => ({
38
38
  ActionIcon: vi.fn(({ title }) => <div>{title}</div>),
39
+ combineKeys: vi.fn((keys) => keys.join('+')),
40
+ KeyMapEnum: { Alt: 'alt', Ctrl: 'ctrl', Shift: 'shift' },
39
41
  }));
40
42
 
41
43
  vi.mock('react-i18next', () => ({
@@ -2,13 +2,17 @@
2
2
 
3
3
  import { useTheme } from 'antd-style';
4
4
  import dynamic from 'next/dynamic';
5
- import { PropsWithChildren, memo } from 'react';
5
+ import { PropsWithChildren, Suspense, memo } from 'react';
6
+ import { HotkeysProvider } from 'react-hotkeys-hook';
6
7
  import { Flexbox } from 'react-layout-kit';
7
8
 
8
9
  import { BANNER_HEIGHT } from '@/features/AlertBanner/CloudBanner';
10
+ import HotkeyHelperPanel from '@/features/HotkeyHelperPanel';
9
11
  import { usePlatform } from '@/hooks/usePlatform';
10
12
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
13
+ import { HotkeyScopeEnum } from '@/types/hotkey';
11
14
 
15
+ import RegisterHotkeys from './RegisterHotkeys';
12
16
  import SideBar from './SideBar';
13
17
 
14
18
  const CloudBanner = dynamic(() => import('@/features/AlertBanner/CloudBanner'));
@@ -16,11 +20,10 @@ const CloudBanner = dynamic(() => import('@/features/AlertBanner/CloudBanner'));
16
20
  const Layout = memo<PropsWithChildren>(({ children }) => {
17
21
  const { isPWA } = usePlatform();
18
22
  const theme = useTheme();
19
-
20
23
  const { showCloudPromotion } = useServerConfigStore(featureFlagsSelectors);
21
24
 
22
25
  return (
23
- <>
26
+ <HotkeysProvider initiallyActiveScopes={[HotkeyScopeEnum.Global]}>
24
27
  {showCloudPromotion && <CloudBanner />}
25
28
  <Flexbox
26
29
  height={showCloudPromotion ? `calc(100% - ${BANNER_HEIGHT}px)` : '100%'}
@@ -34,7 +37,11 @@ const Layout = memo<PropsWithChildren>(({ children }) => {
34
37
  <SideBar />
35
38
  {children}
36
39
  </Flexbox>
37
- </>
40
+ <HotkeyHelperPanel />
41
+ <Suspense>
42
+ <RegisterHotkeys />
43
+ </Suspense>
44
+ </HotkeysProvider>
38
45
  );
39
46
  });
40
47
 
@@ -1,18 +1,16 @@
1
- import { Icon } from '@lobehub/ui';
1
+ import { Hotkey, Icon } from '@lobehub/ui';
2
2
  import { Button, Dropdown } from 'antd';
3
3
  import { createStyles } from 'antd-style';
4
4
  import { BotMessageSquare, LucideCheck, LucideChevronDown, MessageSquarePlus } from 'lucide-react';
5
5
  import { memo } from 'react';
6
- import { useHotkeys } from 'react-hotkeys-hook';
7
6
  import { useTranslation } from 'react-i18next';
8
7
  import { Flexbox } from 'react-layout-kit';
9
8
 
10
- import HotKeys from '@/components/HotKeys';
11
- import { ALT_KEY } from '@/const/hotkeys';
12
9
  import { useSendMessage } from '@/features/ChatInput/useSend';
13
10
  import { useChatStore } from '@/store/chat';
14
11
  import { useUserStore } from '@/store/user';
15
- import { preferenceSelectors } from '@/store/user/selectors';
12
+ import { preferenceSelectors, settingsSelectors } from '@/store/user/selectors';
13
+ import { HotkeyEnum } from '@/types/hotkey';
16
14
 
17
15
  const useStyles = createStyles(({ css, prefixCls }) => {
18
16
  return {
@@ -31,7 +29,7 @@ interface SendMoreProps {
31
29
 
32
30
  const SendMore = memo<SendMoreProps>(({ disabled, isMac }) => {
33
31
  const { t } = useTranslation('chat');
34
-
32
+ const hotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.AddUserMessage));
35
33
  const { styles } = useStyles();
36
34
 
37
35
  const [useCmdEnterToSend, updatePreference] = useUserStore((s) => [
@@ -42,19 +40,6 @@ const SendMore = memo<SendMoreProps>(({ disabled, isMac }) => {
42
40
 
43
41
  const { send: sendMessage } = useSendMessage();
44
42
 
45
- const hotKey = [ALT_KEY, 'enter'].join('+');
46
- useHotkeys(
47
- hotKey,
48
- (keyboardEvent, hotkeysEvent) => {
49
- console.log(keyboardEvent, hotkeysEvent);
50
- sendMessage({ onlyAddUserMessage: true });
51
- },
52
- {
53
- enableOnFormTags: true,
54
- preventDefault: true,
55
- },
56
- );
57
-
58
43
  return (
59
44
  <Dropdown
60
45
  disabled={disabled}
@@ -91,9 +76,9 @@ const SendMore = memo<SendMoreProps>(({ disabled, isMac }) => {
91
76
  icon: <Icon icon={MessageSquarePlus} />,
92
77
  key: 'addUser',
93
78
  label: (
94
- <Flexbox gap={24} horizontal>
79
+ <Flexbox align={'center'} gap={24} horizontal>
95
80
  {t('input.addUser')}
96
- <HotKeys keys={hotKey} />
81
+ <Hotkey keys={hotkey} />
97
82
  </Flexbox>
98
83
  ),
99
84
  onClick: () => {