@lobehub/chat 1.103.1 → 1.104.0

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 (135) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/apps/desktop/build/icon-beta.ico +0 -0
  3. package/apps/desktop/build/icon-dev.ico +0 -0
  4. package/apps/desktop/build/icon-nightly.ico +0 -0
  5. package/apps/desktop/build/icon.ico +0 -0
  6. package/apps/desktop/electron.vite.config.ts +4 -2
  7. package/apps/desktop/package.json +1 -0
  8. package/apps/desktop/src/main/appBrowsers.ts +2 -2
  9. package/apps/desktop/src/main/const/env.ts +5 -4
  10. package/apps/desktop/src/main/const/store.ts +1 -0
  11. package/apps/desktop/src/main/const/theme.ts +11 -0
  12. package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +1 -1
  13. package/apps/desktop/src/main/controllers/NotificationCtr.ts +2 -4
  14. package/apps/desktop/src/main/controllers/ShortcutCtr.ts +9 -1
  15. package/apps/desktop/src/main/controllers/SystemCtr.ts +4 -0
  16. package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -9
  17. package/apps/desktop/src/main/controllers/__tests__/ShortcutCtr.test.ts +14 -11
  18. package/apps/desktop/src/main/controllers/index.ts +1 -1
  19. package/apps/desktop/src/main/core/App.ts +9 -10
  20. package/apps/desktop/src/main/core/{Browser.ts → browser/Browser.ts} +129 -88
  21. package/apps/desktop/src/main/core/{BrowserManager.ts → browser/BrowserManager.ts} +13 -3
  22. package/apps/desktop/src/main/core/{StaticFileServerManager.ts → infrastructure/StaticFileServerManager.ts} +13 -7
  23. package/apps/desktop/src/main/core/{StoreManager.ts → infrastructure/StoreManager.ts} +1 -1
  24. package/apps/desktop/src/main/core/{UpdaterManager.ts → infrastructure/UpdaterManager.ts} +1 -1
  25. package/apps/desktop/src/main/core/{MenuManager.ts → ui/MenuManager.ts} +2 -2
  26. package/apps/desktop/src/main/core/{ShortcutManager.ts → ui/ShortcutManager.ts} +78 -6
  27. package/apps/desktop/src/main/core/{Tray.ts → ui/Tray.ts} +61 -59
  28. package/apps/desktop/src/main/core/{TrayManager.ts → ui/TrayManager.ts} +5 -5
  29. package/apps/desktop/src/main/shortcuts/config.ts +4 -2
  30. package/apps/desktop/src/main/types/store.ts +1 -0
  31. package/changelog/v1.json +21 -0
  32. package/docs/development/basic/add-new-image-model.mdx +162 -0
  33. package/docs/development/basic/add-new-image-model.zh-CN.mdx +162 -0
  34. package/docs/usage/providers/fal.mdx +1 -1
  35. package/docs/usage/providers/fal.zh-CN.mdx +1 -1
  36. package/locales/ar/hotkey.json +10 -4
  37. package/locales/ar/setting.json +12 -1
  38. package/locales/bg-BG/hotkey.json +10 -4
  39. package/locales/bg-BG/setting.json +12 -1
  40. package/locales/de-DE/hotkey.json +10 -4
  41. package/locales/de-DE/setting.json +12 -1
  42. package/locales/en-US/hotkey.json +10 -4
  43. package/locales/en-US/setting.json +12 -1
  44. package/locales/es-ES/hotkey.json +10 -4
  45. package/locales/es-ES/setting.json +12 -1
  46. package/locales/fa-IR/hotkey.json +10 -4
  47. package/locales/fa-IR/setting.json +12 -1
  48. package/locales/fr-FR/hotkey.json +10 -4
  49. package/locales/fr-FR/setting.json +12 -1
  50. package/locales/it-IT/hotkey.json +10 -4
  51. package/locales/it-IT/setting.json +12 -1
  52. package/locales/ja-JP/hotkey.json +10 -4
  53. package/locales/ja-JP/setting.json +12 -1
  54. package/locales/ko-KR/hotkey.json +10 -4
  55. package/locales/ko-KR/setting.json +12 -1
  56. package/locales/nl-NL/hotkey.json +10 -4
  57. package/locales/nl-NL/setting.json +12 -1
  58. package/locales/pl-PL/hotkey.json +10 -4
  59. package/locales/pl-PL/setting.json +12 -1
  60. package/locales/pt-BR/hotkey.json +10 -4
  61. package/locales/pt-BR/setting.json +12 -1
  62. package/locales/ru-RU/hotkey.json +10 -4
  63. package/locales/ru-RU/setting.json +12 -1
  64. package/locales/tr-TR/hotkey.json +10 -4
  65. package/locales/tr-TR/setting.json +12 -1
  66. package/locales/vi-VN/hotkey.json +10 -4
  67. package/locales/vi-VN/setting.json +12 -1
  68. package/locales/zh-CN/hotkey.json +10 -4
  69. package/locales/zh-CN/setting.json +12 -1
  70. package/locales/zh-TW/hotkey.json +10 -4
  71. package/locales/zh-TW/setting.json +12 -1
  72. package/package.json +66 -66
  73. package/packages/electron-client-ipc/src/events/shortcut.ts +3 -1
  74. package/packages/electron-client-ipc/src/types/shortcut.ts +11 -0
  75. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +3 -1
  76. package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/index.tsx +4 -2
  77. package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +1 -1
  78. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +39 -3
  79. package/src/app/[variants]/(main)/image/features/GenerationFeed/ReferenceImages.tsx +122 -0
  80. package/src/app/[variants]/(main)/settings/hotkey/features/Conversation.tsx +3 -11
  81. package/src/app/[variants]/(main)/settings/hotkey/features/Desktop.tsx +92 -0
  82. package/src/app/[variants]/(main)/settings/hotkey/features/Essential.tsx +3 -11
  83. package/src/app/[variants]/(main)/settings/hotkey/page.tsx +3 -0
  84. package/src/config/aiModels/fal.ts +31 -7
  85. package/src/config/aiModels/openai.ts +10 -1
  86. package/src/const/desktop.ts +9 -0
  87. package/src/const/hotkeys.ts +20 -16
  88. package/src/features/ElectronTitlebar/WinControl/index.tsx +85 -90
  89. package/src/features/ElectronTitlebar/hooks/useWatchThemeUpdate.ts +10 -5
  90. package/src/features/ImageTopicPanel/index.tsx +0 -1
  91. package/src/features/PluginDevModal/index.tsx +3 -1
  92. package/src/features/User/UserPanel/useMenu.tsx +2 -2
  93. package/src/features/User/__tests__/UserAvatar.test.tsx +5 -4
  94. package/src/libs/model-runtime/fal/index.ts +1 -1
  95. package/src/libs/model-runtime/types/image.ts +1 -1
  96. package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +1 -1
  97. package/src/libs/model-runtime/utils/response.ts +2 -0
  98. package/src/libs/model-runtime/utils/streams/google-ai.test.ts +46 -0
  99. package/src/libs/model-runtime/utils/streams/google-ai.ts +4 -4
  100. package/src/libs/model-runtime/utils/streams/vertex-ai.ts +6 -8
  101. package/src/libs/standard-parameters/{meta-schema.test.ts → index.test.ts} +1 -1
  102. package/src/libs/standard-parameters/index.ts +152 -1
  103. package/src/locales/default/hotkey.ts +13 -5
  104. package/src/locales/default/setting.ts +11 -0
  105. package/src/server/ld.test.ts +4 -3
  106. package/src/server/routers/async/image.ts +1 -1
  107. package/src/services/__tests__/chat.test.ts +3 -4
  108. package/src/services/electron/settings.ts +19 -1
  109. package/src/store/chat/slices/message/selectors.test.ts +2 -3
  110. package/src/store/chat/slices/plugin/action.test.ts +2 -1
  111. package/src/store/electron/actions/settings.ts +42 -1
  112. package/src/store/electron/initialState.ts +9 -1
  113. package/src/store/electron/selectors/__tests__/desktopState.test.ts +6 -17
  114. package/src/store/electron/selectors/hotkey.ts +11 -0
  115. package/src/store/electron/selectors/index.ts +1 -0
  116. package/src/store/image/slices/generationConfig/action.test.ts +2 -2
  117. package/src/store/image/slices/generationConfig/action.ts +1 -1
  118. package/src/store/image/slices/generationConfig/hooks.test.ts +2 -2
  119. package/src/store/image/slices/generationConfig/hooks.ts +1 -4
  120. package/src/store/image/slices/generationConfig/initialState.ts +2 -2
  121. package/src/store/image/slices/generationConfig/selectors.test.ts +2 -2
  122. package/src/store/image/slices/generationConfig/selectors.ts +1 -1
  123. package/src/store/user/slices/auth/selectors.test.ts +3 -2
  124. package/src/types/generation/index.ts +1 -0
  125. package/src/types/hotkey.ts +18 -4
  126. package/docs/development/basic/add-new-ai-image-model.mdx +0 -36
  127. package/docs/development/basic/add-new-ai-image-model.zh-CN.mdx +0 -0
  128. package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +0 -8
  129. package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +0 -11
  130. package/src/config/paramsSchemas/fal/flux-schnell.ts +0 -9
  131. package/src/config/paramsSchemas/fal/imagen4.ts +0 -10
  132. package/src/config/paramsSchemas/openai/gpt-image-1.ts +0 -10
  133. package/src/libs/standard-parameters/meta-schema.ts +0 -147
  134. /package/apps/desktop/src/main/core/{I18nManager.ts → infrastructure/I18nManager.ts} +0 -0
  135. /package/apps/desktop/src/main/core/{IoCContainer.ts → infrastructure/IoCContainer.ts} +0 -0
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+
3
+ import { Form, type FormGroupItemType, HotkeyInput, Icon } from '@lobehub/ui';
4
+ import { App, Skeleton } from 'antd';
5
+ import isEqual from 'fast-deep-equal';
6
+ import { Loader2Icon } from 'lucide-react';
7
+ import { memo, useState } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+
10
+ import { DESKTOP_HOTKEYS_REGISTRATION } from '@/const/hotkeys';
11
+ import { FORM_STYLE } from '@/const/layoutTokens';
12
+ import hotkeyMeta from '@/locales/default/hotkey';
13
+ import { useElectronStore } from '@/store/electron';
14
+ import { desktopHotkeysSelectors } from '@/store/electron/selectors';
15
+ import { DesktopHotkeyItem } from '@/types/hotkey';
16
+
17
+ const HotkeySetting = memo(() => {
18
+ const { t } = useTranslation(['setting', 'hotkey']);
19
+ const [form] = Form.useForm();
20
+ const { message } = App.useApp();
21
+
22
+ const hotkeys = useElectronStore(desktopHotkeysSelectors.hotkeys, isEqual);
23
+ const [isHotkeysInit, updateDesktopHotkey, useFetchDesktopHotkeys] = useElectronStore((s) => [
24
+ desktopHotkeysSelectors.isHotkeysInit(s),
25
+ s.updateDesktopHotkey,
26
+ s.useFetchDesktopHotkeys,
27
+ ]);
28
+
29
+ useFetchDesktopHotkeys();
30
+
31
+ const [loading, setLoading] = useState(false);
32
+
33
+ if (!isHotkeysInit) return <Skeleton active paragraph={{ rows: 5 }} title={false} />;
34
+
35
+ const mapHotkeyItem = (item: DesktopHotkeyItem) => ({
36
+ children: (
37
+ <HotkeyInput
38
+ disabled={item.nonEditable}
39
+ onChange={async (value) => {
40
+ setLoading(true);
41
+ try {
42
+ const result = await updateDesktopHotkey(item.id, value);
43
+ console.log(result);
44
+ if (result.success) {
45
+ message.success(t('hotkey.updateSuccess', { ns: 'setting' }));
46
+ } else {
47
+ // 根据错误类型显示相应的错误消息
48
+
49
+ message.error(t(`hotkey.errors.${result.errorType}` as any, { ns: 'setting' }));
50
+ }
51
+ } catch {
52
+ message.error(t('hotkey.updateError', { ns: 'setting' }));
53
+ } finally {
54
+ setLoading(false);
55
+ }
56
+ }}
57
+ placeholder={t('hotkey.record')}
58
+ resetValue={item.keys}
59
+ texts={{
60
+ conflicts: t('hotkey.conflicts'),
61
+ invalidCombination: t('hotkey.invalidCombination'),
62
+ reset: t('hotkey.reset'),
63
+ }}
64
+ value={hotkeys[item.id]}
65
+ />
66
+ ),
67
+ desc: hotkeyMeta.desktop[item.id]?.desc
68
+ ? t(`desktop.${item.id}.desc`, { ns: 'hotkey' })
69
+ : undefined,
70
+ label: t(`desktop.${item.id}.title`, { ns: 'hotkey' }),
71
+ name: item.id,
72
+ });
73
+
74
+ const desktop: FormGroupItemType = {
75
+ children: DESKTOP_HOTKEYS_REGISTRATION.map((item) => mapHotkeyItem(item)),
76
+ extra: loading && <Icon icon={Loader2Icon} size={16} spin style={{ opacity: 0.5 }} />,
77
+ title: t('hotkey.group.desktop'),
78
+ };
79
+
80
+ return (
81
+ <Form
82
+ form={form}
83
+ initialValues={hotkeys}
84
+ items={[desktop]}
85
+ itemsType={'group'}
86
+ variant={'borderless'}
87
+ {...FORM_STYLE}
88
+ />
89
+ );
90
+ });
91
+
92
+ export default HotkeySetting;
@@ -9,19 +9,11 @@ import { useTranslation } from 'react-i18next';
9
9
 
10
10
  import { HOTKEYS_REGISTRATION } from '@/const/hotkeys';
11
11
  import { FORM_STYLE } from '@/const/layoutTokens';
12
- import { isDesktop } from '@/const/version';
13
12
  import hotkeyMeta from '@/locales/default/hotkey';
14
13
  import { useUserStore } from '@/store/user';
15
14
  import { settingsSelectors } from '@/store/user/selectors';
16
15
  import { HotkeyGroupEnum, HotkeyItem } from '@/types/hotkey';
17
16
 
18
- const filterByDesktop = (item: HotkeyItem) => {
19
- if (isDesktop) return true;
20
-
21
- // is not desktop, filter out desktop only items
22
- if (!isDesktop) return !item.isDesktop;
23
- };
24
-
25
17
  const HotkeySetting = memo(() => {
26
18
  const { t } = useTranslation(['setting', 'hotkey']);
27
19
  const [form] = Form.useForm();
@@ -61,9 +53,9 @@ const HotkeySetting = memo(() => {
61
53
  };
62
54
 
63
55
  const essential: FormGroupItemType = {
64
- children: HOTKEYS_REGISTRATION.filter((item) => item.group === HotkeyGroupEnum.Essential)
65
- .filter((item) => filterByDesktop(item))
66
- .map((item) => mapHotkeyItem(item)),
56
+ children: HOTKEYS_REGISTRATION.filter((item) => item.group === HotkeyGroupEnum.Essential).map(
57
+ (item) => mapHotkeyItem(item),
58
+ ),
67
59
  extra: loading && <Icon icon={Loader2Icon} size={16} spin style={{ opacity: 0.5 }} />,
68
60
  title: t('hotkey.group.essential'),
69
61
  };
@@ -1,9 +1,11 @@
1
+ import { isDesktop } from '@/const/version';
1
2
  import { metadataModule } from '@/server/metadata';
2
3
  import { translation } from '@/server/translation';
3
4
  import { DynamicLayoutProps } from '@/types/next';
4
5
  import { RouteVariants } from '@/utils/server/routeVariants';
5
6
 
6
7
  import Conversation from './features/Conversation';
8
+ import Desktop from './features/Desktop';
7
9
  import Essential from './features/Essential';
8
10
 
9
11
  export const generateMetadata = async (props: DynamicLayoutProps) => {
@@ -19,6 +21,7 @@ export const generateMetadata = async (props: DynamicLayoutProps) => {
19
21
  const Page = () => {
20
22
  return (
21
23
  <>
24
+ {isDesktop && <Desktop />}
22
25
  <Essential />
23
26
  <Conversation />
24
27
  </>
@@ -1,9 +1,13 @@
1
+ import { ModelParamsSchema } from '@/libs/standard-parameters';
1
2
  import { AIImageModelCard } from '@/types/aiModel';
2
3
 
3
- import { fluxKontextDevParamsSchema } from '../paramsSchemas/fal/flux-kontext-dev';
4
- import { fluxProKontextParamsSchema } from '../paramsSchemas/fal/flux-pro-kontext';
5
- import { fluxSchnellParamsSchema } from '../paramsSchemas/fal/flux-schnell';
6
- import { imagen4ParamsSchema } from '../paramsSchemas/fal/imagen4';
4
+ export const fluxSchnellParamsSchema: ModelParamsSchema = {
5
+ height: { default: 1024, max: 1536, min: 512, step: 1 },
6
+ prompt: { default: '' },
7
+ seed: { default: null },
8
+ steps: { default: 4, max: 12, min: 1 },
9
+ width: { default: 1024, max: 1536, min: 512, step: 1 },
10
+ };
7
11
 
8
12
  const falImageModels: AIImageModelCard[] = [
9
13
  {
@@ -11,7 +15,12 @@ const falImageModels: AIImageModelCard[] = [
11
15
  displayName: 'FLUX.1 Kontext Dev',
12
16
  enabled: true,
13
17
  id: 'flux-kontext/dev',
14
- parameters: fluxKontextDevParamsSchema,
18
+ parameters: {
19
+ imageUrl: { default: null },
20
+ prompt: { default: '' },
21
+ seed: { default: null },
22
+ steps: { default: 28, max: 50, min: 10 },
23
+ },
15
24
  releasedAt: '2025-06-28',
16
25
  type: 'image',
17
26
  },
@@ -21,7 +30,15 @@ const falImageModels: AIImageModelCard[] = [
21
30
  displayName: 'FLUX.1 Kontext [pro]',
22
31
  enabled: true,
23
32
  id: 'flux-pro/kontext',
24
- parameters: fluxProKontextParamsSchema,
33
+ parameters: {
34
+ aspectRatio: {
35
+ default: '1:1',
36
+ enum: ['21:9', '16:9', '4:3', '3:2', '1:1', '2:3', '3:4', '9:16', '9:21'],
37
+ },
38
+ imageUrl: { default: null },
39
+ prompt: { default: '' },
40
+ seed: { default: null },
41
+ },
25
42
  releasedAt: '2025-05-01',
26
43
  type: 'image',
27
44
  },
@@ -41,7 +58,14 @@ const falImageModels: AIImageModelCard[] = [
41
58
  enabled: true,
42
59
  id: 'imagen4/preview',
43
60
  organization: 'Deepmind',
44
- parameters: imagen4ParamsSchema,
61
+ parameters: {
62
+ aspectRatio: {
63
+ default: '1:1',
64
+ enum: ['1:1', '16:9', '9:16', '3:4', '4:3'],
65
+ },
66
+ prompt: { default: '' },
67
+ seed: { default: null },
68
+ },
45
69
  releasedAt: '2025-05-21',
46
70
  type: 'image',
47
71
  },
@@ -1,4 +1,4 @@
1
- import { gptImage1ParamsSchema } from '@/config/paramsSchemas/openai/gpt-image-1';
1
+ import { ModelParamsSchema } from '@/libs/standard-parameters';
2
2
  import {
3
3
  AIChatModelCard,
4
4
  AIEmbeddingModelCard,
@@ -8,6 +8,15 @@ import {
8
8
  AITTSModelCard,
9
9
  } from '@/types/aiModel';
10
10
 
11
+ export const gptImage1ParamsSchema: ModelParamsSchema = {
12
+ imageUrls: { default: [] },
13
+ prompt: { default: '' },
14
+ size: {
15
+ default: 'auto',
16
+ enum: ['auto', '1024x1024', '1536x1024', '1024x1536'],
17
+ },
18
+ };
19
+
11
20
  export const openaiChatModels: AIChatModelCard[] = [
12
21
  {
13
22
  abilities: {
@@ -1 +1,10 @@
1
+ import { DESKTOP_HOTKEYS_REGISTRATION } from '@/const/hotkeys';
2
+ import { DesktopHotkeyConfig } from '@/types/hotkey';
3
+
1
4
  export const DESKTOP_USER_ID = 'DEFAULT_DESKTOP_USER';
5
+
6
+ export const DEFAULT_DESKTOP_HOTKEY_CONFIG: DesktopHotkeyConfig =
7
+ DESKTOP_HOTKEYS_REGISTRATION.reduce((acc: DesktopHotkeyConfig, item) => {
8
+ acc[item.id] = item.keys;
9
+ return acc;
10
+ }, {} as DesktopHotkeyConfig);
@@ -1,23 +1,20 @@
1
1
  import {
2
+ DesktopHotkeyEnum,
3
+ DesktopHotkeyItem,
2
4
  HotkeyEnum,
3
5
  HotkeyGroupEnum,
4
- HotkeyRegistration,
6
+ HotkeyItem,
5
7
  HotkeyScopeEnum,
6
8
  KeyEnum,
7
9
  } from '@/types/hotkey';
8
10
 
9
11
  const combineKeys = (keys: string[]) => keys.join('+');
10
12
 
13
+ export type HotkeyRegistration = HotkeyItem[];
14
+
11
15
  // mod 在 Mac 上是 command 键,alt 在 Win 上是 ctrl 键
12
16
  export const HOTKEYS_REGISTRATION: HotkeyRegistration = [
13
17
  // basic
14
- {
15
- group: HotkeyGroupEnum.Essential,
16
- id: HotkeyEnum.ShowApp,
17
- keys: combineKeys([KeyEnum.Mod, 'e']),
18
- nonEditable: true,
19
- scopes: [HotkeyScopeEnum.Global],
20
- },
21
18
  {
22
19
  group: HotkeyGroupEnum.Essential,
23
20
  id: HotkeyEnum.Search,
@@ -55,14 +52,6 @@ export const HOTKEYS_REGISTRATION: HotkeyRegistration = [
55
52
  keys: combineKeys([KeyEnum.Ctrl, KeyEnum.Shift, KeyEnum.QuestionMark]),
56
53
  scopes: [HotkeyScopeEnum.Global],
57
54
  },
58
- {
59
- group: HotkeyGroupEnum.Essential,
60
- id: HotkeyEnum.OpenSettings,
61
- isDesktop: true,
62
- keys: combineKeys([KeyEnum.Mod, KeyEnum.Comma]),
63
- nonEditable: true,
64
- scopes: [HotkeyScopeEnum.Global],
65
- },
66
55
  // Chat
67
56
  {
68
57
  group: HotkeyGroupEnum.Conversation,
@@ -102,3 +91,18 @@ export const HOTKEYS_REGISTRATION: HotkeyRegistration = [
102
91
  scopes: [HotkeyScopeEnum.Chat],
103
92
  },
104
93
  ];
94
+
95
+ type DesktopHotkeyRegistration = DesktopHotkeyItem[];
96
+
97
+ // 桌面端快捷键配置
98
+ export const DESKTOP_HOTKEYS_REGISTRATION: DesktopHotkeyRegistration = [
99
+ {
100
+ id: DesktopHotkeyEnum.ShowApp,
101
+ keys: combineKeys([KeyEnum.Ctrl, 'e']),
102
+ },
103
+ {
104
+ id: DesktopHotkeyEnum.OpenSettings,
105
+ keys: combineKeys([KeyEnum.Mod, KeyEnum.Comma]),
106
+ nonEditable: true,
107
+ },
108
+ ];
@@ -1,95 +1,90 @@
1
- import { createStyles } from 'antd-style';
2
- import { Minus, Square, XIcon } from 'lucide-react';
3
-
4
- import { electronSystemService } from '@/services/electron/system';
5
-
6
- import { TITLE_BAR_HEIGHT } from '../const';
7
-
8
- const useStyles = createStyles(({ css, cx, token }) => {
9
- const icon = css`
10
- display: flex;
11
- align-items: center;
12
- justify-content: center;
13
-
14
- width: ${TITLE_BAR_HEIGHT * 1.2}px;
15
- min-height: ${TITLE_BAR_HEIGHT}px;
16
-
17
- color: ${token.colorTextSecondary};
18
-
19
- transition: all ease-in-out 100ms;
20
-
21
- -webkit-app-region: no-drag;
22
-
23
- &:hover {
24
- color: ${token.colorText};
25
- background: ${token.colorFillTertiary};
26
- }
27
-
28
- &:active {
29
- color: ${token.colorText};
30
- background: ${token.colorFillSecondary};
31
- }
32
- `;
33
- return {
34
- close: cx(
35
- icon,
36
- css`
37
- padding-inline-end: 2px;
38
-
39
- &:hover {
40
- color: ${token.colorTextLightSolid};
41
-
42
- /* win11 的色值,亮暗色均不变 */
43
- background: #d33328;
44
- }
45
-
46
- &:active {
47
- color: ${token.colorTextLightSolid};
48
-
49
- /* win11 的色值 */
50
- background: #8b2b25;
51
- }
52
- `,
53
- ),
54
- container: css`
55
- cursor: pointer;
56
- display: flex;
57
- `,
58
- icon,
59
- };
60
- });
1
+ // const useStyles = createStyles(({ css, cx, token }) => {
2
+ // const icon = css`
3
+ // display: flex;
4
+ // align-items: center;
5
+ // justify-content: center;
6
+ //
7
+ // width: ${TITLE_BAR_HEIGHT * 1.2}px;
8
+ // min-height: ${TITLE_BAR_HEIGHT}px;
9
+ //
10
+ // color: ${token.colorTextSecondary};
11
+ //
12
+ // transition: all ease-in-out 100ms;
13
+ //
14
+ // -webkit-app-region: no-drag;
15
+ //
16
+ // &:hover {
17
+ // color: ${token.colorText};
18
+ // background: ${token.colorFillTertiary};
19
+ // }
20
+ //
21
+ // &:active {
22
+ // color: ${token.colorText};
23
+ // background: ${token.colorFillSecondary};
24
+ // }
25
+ // `;
26
+ // return {
27
+ // close: cx(
28
+ // icon,
29
+ // css`
30
+ // padding-inline-end: 2px;
31
+ //
32
+ // &:hover {
33
+ // color: ${token.colorTextLightSolid};
34
+ //
35
+ // /* win11 的色值,亮暗色均不变 */
36
+ // background: #d33328;
37
+ // }
38
+ //
39
+ // &:active {
40
+ // color: ${token.colorTextLightSolid};
41
+ //
42
+ // /* win11 的色值 */
43
+ // background: #8b2b25;
44
+ // }
45
+ // `,
46
+ // ),
47
+ // container: css`
48
+ // cursor: pointer;
49
+ // display: flex;
50
+ // `,
51
+ // icon,
52
+ // };
53
+ // });
61
54
 
62
55
  const WinControl = () => {
63
- const { styles } = useStyles();
64
-
65
- return (
66
- <div className={styles.container}>
67
- <div
68
- className={styles.icon}
69
- onClick={() => {
70
- electronSystemService.minimizeWindow();
71
- }}
72
- >
73
- <Minus absoluteStrokeWidth size={14} strokeWidth={1.2} />
74
- </div>
75
- <div
76
- className={styles.icon}
77
- onClick={() => {
78
- electronSystemService.maximizeWindow();
79
- }}
80
- >
81
- <Square absoluteStrokeWidth size={10} strokeWidth={1.2} />
82
- </div>
83
- <div
84
- className={styles.close}
85
- onClick={() => {
86
- electronSystemService.closeWindow();
87
- }}
88
- >
89
- <XIcon absoluteStrokeWidth size={14} strokeWidth={1.2} />
90
- </div>
91
- </div>
92
- );
56
+ return <div style={{ width: 132 }} />;
57
+
58
+ // const { styles } = useStyles();
59
+ //
60
+ // return (
61
+ // <div className={styles.container}>
62
+ // <div
63
+ // className={styles.icon}
64
+ // onClick={() => {
65
+ // electronSystemService.minimizeWindow();
66
+ // }}
67
+ // >
68
+ // <Minus absoluteStrokeWidth size={14} strokeWidth={1.2} />
69
+ // </div>
70
+ // <div
71
+ // className={styles.icon}
72
+ // onClick={() => {
73
+ // electronSystemService.maximizeWindow();
74
+ // }}
75
+ // >
76
+ // <Square absoluteStrokeWidth size={10} strokeWidth={1.2} />
77
+ // </div>
78
+ // <div
79
+ // className={styles.close}
80
+ // onClick={() => {
81
+ // electronSystemService.closeWindow();
82
+ // }}
83
+ // >
84
+ // <XIcon absoluteStrokeWidth size={14} strokeWidth={1.2} />
85
+ // </div>
86
+ // </div>
87
+ // );
93
88
  };
94
89
 
95
90
  export default WinControl;
@@ -7,10 +7,14 @@ import { useElectronStore } from '@/store/electron';
7
7
  import { useGlobalStore } from '@/store/global';
8
8
 
9
9
  export const useWatchThemeUpdate = () => {
10
- const [systemAppearance, updateElectronAppState] = useElectronStore((s) => [
11
- s.appState.systemAppearance,
12
- s.updateElectronAppState,
13
- ]);
10
+ const [isAppStateInit, systemAppearance, updateElectronAppState, isMac] = useElectronStore(
11
+ (s) => [
12
+ s.isAppStateInit,
13
+ s.appState.systemAppearance,
14
+ s.updateElectronAppState,
15
+ s.appState.isMac,
16
+ ],
17
+ );
14
18
  const switchThemeMode = useGlobalStore((s) => s.switchThemeMode);
15
19
 
16
20
  const theme = useTheme();
@@ -24,11 +28,12 @@ export const useWatchThemeUpdate = () => {
24
28
  });
25
29
 
26
30
  useEffect(() => {
31
+ if (!isAppStateInit || !isMac) return;
27
32
  document.documentElement.style.background = 'none';
28
33
 
29
34
  // https://x.com/alanblogsooo/status/1939208908993896684
30
35
  const isNotSameTheme = !systemAppearance ? true : theme.appearance !== systemAppearance;
31
36
 
32
37
  document.body.style.background = rgba(theme.colorBgLayout, isNotSameTheme ? 0.95 : 0.66);
33
- }, [theme, systemAppearance]);
38
+ }, [theme, systemAppearance, isAppStateInit, isMac]);
34
39
  };
@@ -67,7 +67,6 @@ const ImageTopicPanel = memo<PropsWithChildren>(({ children }) => {
67
67
  flex: 'none',
68
68
  height: '100%',
69
69
  minWidth: 80,
70
- padding: 16,
71
70
  }}
72
71
  >
73
72
  {children}
@@ -7,6 +7,8 @@ import { Trans, useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import { WIKI_PLUGIN_GUIDE } from '@/const/url';
10
+ import { isDesktop } from '@/const/version';
11
+ import { TITLE_BAR_HEIGHT } from '@/features/ElectronTitlebar';
10
12
  import { LobeToolCustomPlugin } from '@/types/tool/plugin';
11
13
 
12
14
  import MCPManifestForm from './MCPManifestForm';
@@ -112,7 +114,7 @@ const DevModal = memo<DevModalProps>(
112
114
  containerMaxWidth={'auto'}
113
115
  destroyOnHidden
114
116
  footer={footer}
115
- height={'100vh'}
117
+ height={isDesktop ? `calc(100vh - ${TITLE_BAR_HEIGHT}px)` : '100vh'}
116
118
  onClose={(e) => {
117
119
  e.stopPropagation();
118
120
  onOpenChange(false);
@@ -23,7 +23,7 @@ import { Flexbox } from 'react-layout-kit';
23
23
  import type { MenuProps } from '@/components/Menu';
24
24
  import { enableAuth } from '@/const/auth';
25
25
  import { BRANDING_EMAIL, LOBE_CHAT_CLOUD, SOCIAL_URL } from '@/const/branding';
26
- import { DEFAULT_HOTKEY_CONFIG } from '@/const/settings';
26
+ import { DEFAULT_DESKTOP_HOTKEY_CONFIG } from '@/const/desktop';
27
27
  import {
28
28
  CHANGELOG,
29
29
  DOCUMENTS_REFER_URL,
@@ -85,7 +85,7 @@ export const useMenu = () => {
85
85
  {
86
86
  extra: isDesktop ? (
87
87
  <div>
88
- <Hotkey keys={DEFAULT_HOTKEY_CONFIG.openSettings} />
88
+ <Hotkey keys={DEFAULT_DESKTOP_HOTKEY_CONFIG.openSettings} />
89
89
  </div>
90
90
  ) : undefined,
91
91
  icon: <Icon icon={Settings2} />,
@@ -1,6 +1,7 @@
1
1
  import { act, render, screen } from '@testing-library/react';
2
2
  import { afterEach, describe, expect, it, vi } from 'vitest';
3
3
 
4
+ import { BRANDING_NAME } from '@/const/branding';
4
5
  import { DEFAULT_USER_AVATAR_URL } from '@/const/meta';
5
6
  import { useUserStore } from '@/store/user';
6
7
 
@@ -63,8 +64,8 @@ describe('UserAvatar', () => {
63
64
  });
64
65
 
65
66
  render(<UserAvatar />);
66
- expect(screen.getByAltText('LobeChat')).toBeInTheDocument();
67
- expect(screen.getByAltText('LobeChat')).toHaveAttribute('src', DEFAULT_USER_AVATAR_URL);
67
+ expect(screen.getByAltText(BRANDING_NAME)).toBeInTheDocument();
68
+ expect(screen.getByAltText(BRANDING_NAME)).toHaveAttribute('src', DEFAULT_USER_AVATAR_URL);
68
69
  });
69
70
  });
70
71
 
@@ -76,8 +77,8 @@ describe('UserAvatar', () => {
76
77
  });
77
78
 
78
79
  render(<UserAvatar />);
79
- expect(screen.getByAltText('LobeChat')).toBeInTheDocument();
80
- expect(screen.getByAltText('LobeChat')).toHaveAttribute('src', DEFAULT_USER_AVATAR_URL);
80
+ expect(screen.getByAltText(BRANDING_NAME)).toBeInTheDocument();
81
+ expect(screen.getByAltText(BRANDING_NAME)).toHaveAttribute('src', DEFAULT_USER_AVATAR_URL);
81
82
  });
82
83
  });
83
84
  });
@@ -3,7 +3,7 @@ import debug from 'debug';
3
3
  import { pick } from 'lodash-es';
4
4
  import { ClientOptions } from 'openai';
5
5
 
6
- import { RuntimeImageGenParamsValue } from '@/libs/standard-parameters/meta-schema';
6
+ import { RuntimeImageGenParamsValue } from '@/libs/standard-parameters/index';
7
7
 
8
8
  import { LobeRuntimeAI } from '../BaseAI';
9
9
  import { AgentRuntimeErrorType } from '../error';
@@ -1,4 +1,4 @@
1
- import { RuntimeImageGenParams } from '@/libs/standard-parameters/meta-schema';
1
+ import { RuntimeImageGenParams } from '@/libs/standard-parameters/index';
2
2
 
3
3
  export type CreateImagePayload = {
4
4
  model: string;
@@ -5,7 +5,7 @@ import OpenAI, { ClientOptions } from 'openai';
5
5
  import { Stream } from 'openai/streaming';
6
6
 
7
7
  import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
8
- import { RuntimeImageGenParamsValue } from '@/libs/standard-parameters/meta-schema';
8
+ import { RuntimeImageGenParamsValue } from '@/libs/standard-parameters/index';
9
9
  import type { ChatModelCard } from '@/types/llm';
10
10
  import { getModelPropertyWithFallback } from '@/utils/getFallbackModelProperty';
11
11
 
@@ -6,6 +6,8 @@ export const StreamingResponse = (
6
6
  headers: {
7
7
  'Cache-Control': 'no-cache',
8
8
  'Content-Type': 'text/event-stream',
9
+ // for Nginx: disable chunk buffering
10
+ 'X-Accel-Buffering': 'no',
9
11
  ...options?.headers,
10
12
  },
11
13
  });