@lobehub/chat 1.103.2 → 1.104.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 (83) hide show
  1. package/.cursor/rules/code-review.mdc +2 -0
  2. package/.cursor/rules/typescript.mdc +3 -1
  3. package/CHANGELOG.md +50 -0
  4. package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +1 -1
  5. package/apps/desktop/src/main/controllers/ShortcutCtr.ts +9 -1
  6. package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +1 -5
  7. package/apps/desktop/src/main/controllers/__tests__/ShortcutCtr.test.ts +14 -11
  8. package/apps/desktop/src/main/core/ui/ShortcutManager.ts +71 -5
  9. package/apps/desktop/src/main/shortcuts/config.ts +4 -2
  10. package/changelog/v1.json +18 -0
  11. package/locales/ar/hotkey.json +10 -4
  12. package/locales/ar/setting.json +12 -1
  13. package/locales/bg-BG/hotkey.json +10 -4
  14. package/locales/bg-BG/setting.json +12 -1
  15. package/locales/de-DE/hotkey.json +10 -4
  16. package/locales/de-DE/setting.json +12 -1
  17. package/locales/en-US/hotkey.json +10 -4
  18. package/locales/en-US/setting.json +12 -1
  19. package/locales/es-ES/hotkey.json +10 -4
  20. package/locales/es-ES/setting.json +12 -1
  21. package/locales/fa-IR/hotkey.json +10 -4
  22. package/locales/fa-IR/setting.json +12 -1
  23. package/locales/fr-FR/hotkey.json +10 -4
  24. package/locales/fr-FR/setting.json +12 -1
  25. package/locales/it-IT/hotkey.json +10 -4
  26. package/locales/it-IT/setting.json +12 -1
  27. package/locales/ja-JP/hotkey.json +10 -4
  28. package/locales/ja-JP/setting.json +12 -1
  29. package/locales/ko-KR/hotkey.json +10 -4
  30. package/locales/ko-KR/setting.json +12 -1
  31. package/locales/nl-NL/hotkey.json +10 -4
  32. package/locales/nl-NL/setting.json +12 -1
  33. package/locales/pl-PL/hotkey.json +10 -4
  34. package/locales/pl-PL/setting.json +12 -1
  35. package/locales/pt-BR/hotkey.json +10 -4
  36. package/locales/pt-BR/setting.json +12 -1
  37. package/locales/ru-RU/hotkey.json +10 -4
  38. package/locales/ru-RU/setting.json +12 -1
  39. package/locales/tr-TR/hotkey.json +10 -4
  40. package/locales/tr-TR/setting.json +12 -1
  41. package/locales/vi-VN/hotkey.json +10 -4
  42. package/locales/vi-VN/setting.json +12 -1
  43. package/locales/zh-CN/hotkey.json +10 -4
  44. package/locales/zh-CN/setting.json +12 -1
  45. package/locales/zh-TW/hotkey.json +10 -4
  46. package/locales/zh-TW/setting.json +12 -1
  47. package/package.json +1 -1
  48. package/packages/electron-client-ipc/src/events/shortcut.ts +3 -1
  49. package/packages/electron-client-ipc/src/types/shortcut.ts +11 -0
  50. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +6 -1
  51. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ErrorState.tsx +3 -2
  52. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/LoadingState.tsx +27 -24
  53. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/SuccessState.tsx +14 -3
  54. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/index.tsx +4 -7
  55. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/types.ts +3 -0
  56. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/utils.test.ts +600 -0
  57. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/utils.ts +126 -7
  58. package/src/app/[variants]/(main)/settings/hotkey/features/Conversation.tsx +3 -11
  59. package/src/app/[variants]/(main)/settings/hotkey/features/Desktop.tsx +92 -0
  60. package/src/app/[variants]/(main)/settings/hotkey/features/Essential.tsx +3 -11
  61. package/src/app/[variants]/(main)/settings/hotkey/page.tsx +3 -0
  62. package/src/const/desktop.ts +9 -0
  63. package/src/const/hotkeys.ts +20 -16
  64. package/src/const/imageGeneration.ts +18 -0
  65. package/src/features/User/UserPanel/useMenu.tsx +2 -2
  66. package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.test.ts +3 -0
  67. package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +7 -5
  68. package/src/libs/model-runtime/utils/streams/openai/openai.ts +8 -4
  69. package/src/libs/model-runtime/utils/usageConverter.test.ts +45 -1
  70. package/src/libs/model-runtime/utils/usageConverter.ts +6 -2
  71. package/src/locales/default/hotkey.ts +13 -5
  72. package/src/locales/default/setting.ts +11 -0
  73. package/src/server/services/generation/index.test.ts +848 -0
  74. package/src/server/services/generation/index.ts +90 -69
  75. package/src/services/electron/settings.ts +19 -1
  76. package/src/store/electron/actions/settings.ts +42 -1
  77. package/src/store/electron/initialState.ts +9 -1
  78. package/src/store/electron/selectors/__tests__/desktopState.test.ts +6 -17
  79. package/src/store/electron/selectors/hotkey.ts +11 -0
  80. package/src/store/electron/selectors/index.ts +1 -0
  81. package/src/types/hotkey.ts +18 -4
  82. package/src/utils/number.test.ts +101 -1
  83. package/src/utils/number.ts +42 -0
@@ -45,14 +45,25 @@
45
45
  },
46
46
  "hotkey": {
47
47
  "conflicts": "与现有快捷键冲突",
48
+ "errors": {
49
+ "CONFLICT": "快捷键冲突:该快捷键已被其他功能占用",
50
+ "INVALID_FORMAT": "快捷键格式无效:请使用正确的格式(如 CommandOrControl+E)",
51
+ "INVALID_ID": "无效的快捷键ID",
52
+ "NO_MODIFIER": "快捷键必须包含修饰键(Ctrl、Alt、Shift等)",
53
+ "SYSTEM_OCCUPIED": "快捷键已被系统或其他应用程序占用",
54
+ "UNKNOWN": "更新失败:未知错误"
55
+ },
48
56
  "group": {
49
57
  "conversation": "会话",
58
+ "desktop": "桌面端",
50
59
  "essential": "基础"
51
60
  },
52
61
  "invalidCombination": "快捷键需要至少包含一个修饰键 (Ctrl, Alt, Shift) 和一个常规键",
53
62
  "record": "按下按键以录制快捷键",
54
63
  "reset": "重置为默认快捷键",
55
- "title": "快捷键"
64
+ "title": "快捷键",
65
+ "updateError": "快捷键更新失败:网络或系统错误",
66
+ "updateSuccess": "快捷键更新成功"
56
67
  },
57
68
  "llm": {
58
69
  "aesGcm": "您的秘钥与代理地址等将使用 <1>AES-GCM</1> 加密算法进行加密",
@@ -7,6 +7,16 @@
7
7
  "desc": "清空當前會話的消息和上傳的檔案",
8
8
  "title": "清空會話消息"
9
9
  },
10
+ "desktop": {
11
+ "openSettings": {
12
+ "desc": "打開應用設定頁面",
13
+ "title": "應用設定"
14
+ },
15
+ "showApp": {
16
+ "desc": "全域快速鍵顯示或隱藏主視窗",
17
+ "title": "顯示/隱藏主視窗"
18
+ }
19
+ },
10
20
  "editMessage": {
11
21
  "desc": "通過按住 Alt 並雙擊消息進入編輯模式",
12
22
  "title": "編輯消息"
@@ -19,10 +29,6 @@
19
29
  "desc": "查看所有快捷鍵的使用說明",
20
30
  "title": "打開快捷鍵幫助"
21
31
  },
22
- "openSettings": {
23
- "desc": "打開應用設定頁面",
24
- "title": "應用設定"
25
- },
26
32
  "regenerateMessage": {
27
33
  "desc": "重新生成最後一條消息",
28
34
  "title": "重新生成消息"
@@ -45,14 +45,25 @@
45
45
  },
46
46
  "hotkey": {
47
47
  "conflicts": "與現有快捷鍵衝突",
48
+ "errors": {
49
+ "CONFLICT": "快速鍵衝突:該快速鍵已被其他功能佔用",
50
+ "INVALID_FORMAT": "快速鍵格式無效:請使用正確的格式(如 CommandOrControl+E)",
51
+ "INVALID_ID": "無效的快速鍵ID",
52
+ "NO_MODIFIER": "快速鍵必須包含修飾鍵(Ctrl、Alt、Shift等)",
53
+ "SYSTEM_OCCUPIED": "快速鍵已被系統或其他應用程式佔用",
54
+ "UNKNOWN": "更新失敗:未知錯誤"
55
+ },
48
56
  "group": {
49
57
  "conversation": "對話",
58
+ "desktop": "桌面端",
50
59
  "essential": "基本"
51
60
  },
52
61
  "invalidCombination": "快捷鍵需要至少包含一個修飾鍵 (Ctrl, Alt, Shift) 和一個常規鍵",
53
62
  "record": "按下按鍵以錄製快捷鍵",
54
63
  "reset": "重置為預設快捷鍵",
55
- "title": "快速鍵"
64
+ "title": "快速鍵",
65
+ "updateError": "快速鍵更新失敗:網路或系統錯誤",
66
+ "updateSuccess": "快速鍵更新成功"
56
67
  },
57
68
  "llm": {
58
69
  "aesGcm": "您的金鑰與代理地址等將使用 <1>AES-GCM</1> 加密演算法進行加密",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.103.2",
3
+ "version": "1.104.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",
@@ -1,4 +1,6 @@
1
+ import { ShortcutUpdateResult } from '../types';
2
+
1
3
  export interface ShortcutDispatchEvents {
2
4
  getShortcutsConfig: () => Record<string, string>;
3
- updateShortcutConfig: (id: string, accelerator: string) => boolean;
5
+ updateShortcutConfig: (params: { accelerator: string; id: string }) => ShortcutUpdateResult;
4
6
  }
@@ -9,3 +9,14 @@ export interface ShortcutConfig {
9
9
  id: string;
10
10
  }
11
11
  export type ShortcutActionType = Record<string, any>;
12
+
13
+ export interface ShortcutUpdateResult {
14
+ errorType?:
15
+ | 'INVALID_ID'
16
+ | 'INVALID_FORMAT'
17
+ | 'NO_MODIFIER'
18
+ | 'CONFLICT'
19
+ | 'SYSTEM_OCCUPIED'
20
+ | 'UNKNOWN';
21
+ success: boolean;
22
+ }
@@ -20,6 +20,7 @@ import { AsyncTaskErrorType } from '@/types/asyncTask';
20
20
  import { GenerationBatch } from '@/types/generation';
21
21
 
22
22
  import { GenerationItem } from './GenerationItem';
23
+ import { DEFAULT_MAX_ITEM_WIDTH } from './GenerationItem/utils';
23
24
  import { ReferenceImages } from './ReferenceImages';
24
25
 
25
26
  const useStyles = createStyles(({ cx, css, token }) => ({
@@ -182,7 +183,11 @@ export const GenerationBatchItem = memo<GenerationBatchItemProps>(({ batch }) =>
182
183
  {promptAndMetadata}
183
184
  </>
184
185
  )}
185
- <Grid maxItemWidth={200} ref={imageGridRef} rows={batch.generations.length || 4}>
186
+ <Grid
187
+ maxItemWidth={DEFAULT_MAX_ITEM_WIDTH}
188
+ ref={imageGridRef}
189
+ rows={batch.generations.length}
190
+ >
186
191
  {batch.generations.map((generation) => (
187
192
  <GenerationItem
188
193
  generation={generation}
@@ -9,10 +9,11 @@ import { Center } from 'react-layout-kit';
9
9
  import { ActionButtons } from './ActionButtons';
10
10
  import { useStyles } from './styles';
11
11
  import { ErrorStateProps } from './types';
12
+ import { getThumbnailMaxWidth } from './utils';
12
13
 
13
14
  // 错误状态组件
14
15
  export const ErrorState = memo<ErrorStateProps>(
15
- ({ generation, aspectRatio, onDelete, onCopyError }) => {
16
+ ({ generation, generationBatch, aspectRatio, onDelete, onCopyError }) => {
16
17
  const { styles, theme } = useStyles();
17
18
  const { t } = useTranslation('image');
18
19
 
@@ -32,7 +33,7 @@ export const ErrorState = memo<ErrorStateProps>(
32
33
  style={{
33
34
  aspectRatio,
34
35
  cursor: 'pointer',
35
- maxWidth: generation.asset?.width ? generation.asset.width / 2 : 'unset',
36
+ maxWidth: getThumbnailMaxWidth(generation, generationBatch),
36
37
  }}
37
38
  variant={'filled'}
38
39
  >
@@ -12,33 +12,36 @@ import { ActionButtons } from './ActionButtons';
12
12
  import { ElapsedTime } from './ElapsedTime';
13
13
  import { useStyles } from './styles';
14
14
  import { LoadingStateProps } from './types';
15
+ import { getThumbnailMaxWidth } from './utils';
15
16
 
16
17
  // 加载状态组件
17
- export const LoadingState = memo<LoadingStateProps>(({ generation, aspectRatio, onDelete }) => {
18
- const { styles } = useStyles();
18
+ export const LoadingState = memo<LoadingStateProps>(
19
+ ({ generation, generationBatch, aspectRatio, onDelete }) => {
20
+ const { styles } = useStyles();
19
21
 
20
- const isGenerating =
21
- generation.task.status === AsyncTaskStatus.Processing ||
22
- generation.task.status === AsyncTaskStatus.Pending;
22
+ const isGenerating =
23
+ generation.task.status === AsyncTaskStatus.Processing ||
24
+ generation.task.status === AsyncTaskStatus.Pending;
23
25
 
24
- return (
25
- <Block
26
- align={'center'}
27
- className={styles.placeholderContainer}
28
- justify={'center'}
29
- style={{
30
- aspectRatio,
31
- maxWidth: generation.asset?.width ? generation.asset.width / 2 : 'unset',
32
- }}
33
- variant={'filled'}
34
- >
35
- <Center gap={8}>
36
- <Spin indicator={<LoadingOutlined spin />} />
37
- <ElapsedTime generationId={generation.id} isActive={isGenerating} />
38
- </Center>
39
- <ActionButtons onDelete={onDelete} />
40
- </Block>
41
- );
42
- });
26
+ return (
27
+ <Block
28
+ align={'center'}
29
+ className={styles.placeholderContainer}
30
+ justify={'center'}
31
+ style={{
32
+ aspectRatio,
33
+ maxWidth: getThumbnailMaxWidth(generation, generationBatch),
34
+ }}
35
+ variant={'filled'}
36
+ >
37
+ <Center gap={8}>
38
+ <Spin indicator={<LoadingOutlined spin />} />
39
+ <ElapsedTime generationId={generation.id} isActive={isGenerating} />
40
+ </Center>
41
+ <ActionButtons onDelete={onDelete} />
42
+ </Block>
43
+ );
44
+ },
45
+ );
43
46
 
44
47
  LoadingState.displayName = 'LoadingState';
@@ -8,10 +8,20 @@ import ImageItem from '@/components/ImageItem';
8
8
  import { ActionButtons } from './ActionButtons';
9
9
  import { useStyles } from './styles';
10
10
  import { SuccessStateProps } from './types';
11
+ import { getThumbnailMaxWidth } from './utils';
11
12
 
12
13
  // 成功状态组件
13
14
  export const SuccessState = memo<SuccessStateProps>(
14
- ({ generation, prompt, aspectRatio, onDelete, onDownload, onCopySeed, seedTooltip }) => {
15
+ ({
16
+ generation,
17
+ generationBatch,
18
+ prompt,
19
+ aspectRatio,
20
+ onDelete,
21
+ onDownload,
22
+ onCopySeed,
23
+ seedTooltip,
24
+ }) => {
15
25
  const { styles } = useStyles();
16
26
 
17
27
  return (
@@ -21,7 +31,7 @@ export const SuccessState = memo<SuccessStateProps>(
21
31
  justify={'center'}
22
32
  style={{
23
33
  aspectRatio,
24
- maxWidth: generation.asset?.width ? generation.asset.width / 2 : 'unset',
34
+ maxWidth: getThumbnailMaxWidth(generation, generationBatch),
25
35
  }}
26
36
  variant={'filled'}
27
37
  >
@@ -31,7 +41,8 @@ export const SuccessState = memo<SuccessStateProps>(
31
41
  src: generation.asset!.url,
32
42
  }}
33
43
  style={{ height: '100%', width: '100%' }}
34
- url={generation.asset!.thumbnailUrl}
44
+ // Thumbnail quality is too bad
45
+ url={generation.asset!.url}
35
46
  />
36
47
  <ActionButtons
37
48
  onCopySeed={onCopySeed}
@@ -37,13 +37,7 @@ export const GenerationItem = memo<GenerationItemProps>(
37
37
  const shouldPoll = !isFinalized;
38
38
  useCheckGenerationStatus(generation.id, generation.task.id, activeTopicId!, shouldPoll);
39
39
 
40
- const aspectRatio = getAspectRatio(
41
- generation.asset ?? {
42
- height: generationBatch.config?.height,
43
- type: 'image',
44
- width: generationBatch.config?.width,
45
- },
46
- );
40
+ const aspectRatio = getAspectRatio(generation, generationBatch);
47
41
 
48
42
  // 事件处理函数
49
43
  const handleDeleteGeneration = async () => {
@@ -120,6 +114,7 @@ export const GenerationItem = memo<GenerationItemProps>(
120
114
  <SuccessState
121
115
  aspectRatio={aspectRatio}
122
116
  generation={generation}
117
+ generationBatch={generationBatch}
123
118
  onCopySeed={handleCopySeed}
124
119
  onDelete={handleDeleteGeneration}
125
120
  onDownload={handleDownloadImage}
@@ -134,6 +129,7 @@ export const GenerationItem = memo<GenerationItemProps>(
134
129
  <ErrorState
135
130
  aspectRatio={aspectRatio}
136
131
  generation={generation}
132
+ generationBatch={generationBatch}
137
133
  onCopyError={handleCopyError}
138
134
  onDelete={handleDeleteGeneration}
139
135
  />
@@ -145,6 +141,7 @@ export const GenerationItem = memo<GenerationItemProps>(
145
141
  <LoadingState
146
142
  aspectRatio={aspectRatio}
147
143
  generation={generation}
144
+ generationBatch={generationBatch}
148
145
  onDelete={handleDeleteGeneration}
149
146
  />
150
147
  );
@@ -18,6 +18,7 @@ export interface ActionButtonsProps {
18
18
  export interface SuccessStateProps {
19
19
  aspectRatio: string;
20
20
  generation: Generation;
21
+ generationBatch: GenerationBatch;
21
22
  onCopySeed?: () => void;
22
23
  onDelete: () => void;
23
24
  onDownload: () => void;
@@ -28,6 +29,7 @@ export interface SuccessStateProps {
28
29
  export interface ErrorStateProps {
29
30
  aspectRatio: string;
30
31
  generation: Generation;
32
+ generationBatch: GenerationBatch;
31
33
  onCopyError: () => void;
32
34
  onDelete: () => void;
33
35
  }
@@ -35,5 +37,6 @@ export interface ErrorStateProps {
35
37
  export interface LoadingStateProps {
36
38
  aspectRatio: string;
37
39
  generation: Generation;
40
+ generationBatch: GenerationBatch;
38
41
  onDelete: () => void;
39
42
  }