@lobehub/chat 1.99.0 → 1.99.2
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.
- package/CHANGELOG.md +42 -0
- package/apps/desktop/src/main/appBrowsers.ts +1 -1
- package/apps/desktop/src/main/core/BrowserManager.ts +5 -2
- package/changelog/v1.json +14 -0
- package/package.json +2 -2
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/index.tsx +9 -4
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/index.tsx +1 -1
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/LoadingState.tsx +2 -1
- package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/index.tsx +3 -5
- package/src/app/[variants]/(main)/image/features/ImageWorkspace/EmptyState.tsx +12 -26
- package/src/database/repositories/aiInfra/index.test.ts +6 -0
- package/src/database/repositories/aiInfra/index.ts +10 -1
- package/src/server/routers/lambda/aiProvider.test.ts +1 -0
- package/src/store/aiInfra/slices/aiProvider/action.ts +6 -1
- package/src/types/aiProvider.ts +1 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,48 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.99.2](https://github.com/lobehub/lobe-chat/compare/v1.99.1...v1.99.2)
|
6
|
+
|
7
|
+
<sup>Released on **2025-07-15**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Some ai image generation feedback issues.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Some ai image generation feedback issues, closes [#8440](https://github.com/lobehub/lobe-chat/issues/8440) ([bc41329](https://github.com/lobehub/lobe-chat/commit/bc41329))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
### [Version 1.99.1](https://github.com/lobehub/lobe-chat/compare/v1.99.0...v1.99.1)
|
31
|
+
|
32
|
+
<sup>Released on **2025-07-15**</sup>
|
33
|
+
|
34
|
+
<br/>
|
35
|
+
|
36
|
+
<details>
|
37
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
38
|
+
|
39
|
+
</details>
|
40
|
+
|
41
|
+
<div align="right">
|
42
|
+
|
43
|
+
[](#readme-top)
|
44
|
+
|
45
|
+
</div>
|
46
|
+
|
5
47
|
## [Version 1.99.0](https://github.com/lobehub/lobe-chat/compare/v1.98.2...v1.99.0)
|
6
48
|
|
7
49
|
<sup>Released on **2025-07-14**</sup>
|
@@ -128,9 +128,12 @@ export default class BrowserManager {
|
|
128
128
|
*/
|
129
129
|
initializeBrowsers() {
|
130
130
|
logger.info('Initializing all browsers');
|
131
|
-
Object.values(appBrowsers).forEach((browser) => {
|
131
|
+
Object.values(appBrowsers).forEach((browser: BrowserWindowOpts) => {
|
132
132
|
logger.debug(`Initializing browser: ${browser.identifier}`);
|
133
|
-
|
133
|
+
|
134
|
+
if (browser.keepAlive) {
|
135
|
+
this.retrieveOrInitialize(browser);
|
136
|
+
}
|
134
137
|
});
|
135
138
|
}
|
136
139
|
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,18 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"Some ai image generation feedback issues."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-07-15",
|
9
|
+
"version": "1.99.2"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {},
|
13
|
+
"date": "2025-07-15",
|
14
|
+
"version": "1.99.1"
|
15
|
+
},
|
2
16
|
{
|
3
17
|
"children": {
|
4
18
|
"features": [
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.99.
|
3
|
+
"version": "1.99.2",
|
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",
|
@@ -210,7 +210,7 @@
|
|
210
210
|
"mdast-util-to-markdown": "^2.1.2",
|
211
211
|
"modern-screenshot": "^4.6.0",
|
212
212
|
"nanoid": "^5.1.5",
|
213
|
-
"next": "
|
213
|
+
"next": "~15.3.3",
|
214
214
|
"next-auth": "5.0.0-beta.25",
|
215
215
|
"next-mdx-remote": "^5.0.0",
|
216
216
|
"nextjs-toploader": "^3.8.16",
|
@@ -3,6 +3,7 @@
|
|
3
3
|
// Removed Image import - using img tags instead
|
4
4
|
import { createStyles, useTheme } from 'antd-style';
|
5
5
|
import { Image as ImageIcon, X } from 'lucide-react';
|
6
|
+
import Image from 'next/image';
|
6
7
|
import React, { type FC, memo, useEffect, useRef, useState } from 'react';
|
7
8
|
import { useTranslation } from 'react-i18next';
|
8
9
|
import { Center } from 'react-layout-kit';
|
@@ -429,10 +430,12 @@ const ImageThumbnails: FC<ImageThumbnailsProps> = memo(({ images, onClick, onDel
|
|
429
430
|
|
430
431
|
return (
|
431
432
|
<div className={styles.imageItem} key={imageUrl}>
|
432
|
-
<
|
433
|
+
<Image
|
433
434
|
alt={`Uploaded image ${index + 1}`}
|
435
|
+
fill
|
434
436
|
src={imageUrl}
|
435
|
-
style={{
|
437
|
+
style={{ objectFit: 'cover' }}
|
438
|
+
unoptimized
|
436
439
|
/>
|
437
440
|
{!showOverlay && (
|
438
441
|
<div
|
@@ -478,10 +481,12 @@ const SingleImageDisplay: FC<SingleImageDisplayProps> = memo(({ imageUrl, onClic
|
|
478
481
|
|
479
482
|
return (
|
480
483
|
<div className={styles.singleImageDisplay}>
|
481
|
-
<
|
484
|
+
<Image
|
482
485
|
alt="Uploaded image"
|
486
|
+
fill
|
483
487
|
src={imageUrl}
|
484
|
-
style={{
|
488
|
+
style={{ objectFit: 'contain' }}
|
489
|
+
unoptimized
|
485
490
|
/>
|
486
491
|
|
487
492
|
{/* Delete button */}
|
@@ -26,7 +26,7 @@ interface ConfigItemLayoutProps {
|
|
26
26
|
const ConfigItemLayout = memo<ConfigItemLayoutProps>(({ label, children }) => {
|
27
27
|
return (
|
28
28
|
<Flexbox gap={8}>
|
29
|
-
{label && <Text weight={500}>{label
|
29
|
+
{label && <Text weight={500}>{label}</Text>}
|
30
30
|
{children}
|
31
31
|
</Flexbox>
|
32
32
|
);
|
package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/LoadingState.tsx
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
|
+
import { LoadingOutlined } from '@ant-design/icons';
|
3
4
|
import { Block } from '@lobehub/ui';
|
4
5
|
import { Spin } from 'antd';
|
5
6
|
import { memo } from 'react';
|
@@ -32,7 +33,7 @@ export const LoadingState = memo<LoadingStateProps>(({ generation, aspectRatio,
|
|
32
33
|
variant={'filled'}
|
33
34
|
>
|
34
35
|
<Center gap={8}>
|
35
|
-
<Spin
|
36
|
+
<Spin indicator={<LoadingOutlined spin />} />
|
36
37
|
<ElapsedTime generationId={generation.id} isActive={isGenerating} />
|
37
38
|
</Center>
|
38
39
|
<ActionButtons onDelete={onDelete} />
|
@@ -59,12 +59,10 @@ export const GenerationItem = memo<GenerationItemProps>(
|
|
59
59
|
|
60
60
|
// Generate filename with prompt and timestamp
|
61
61
|
const timestamp = dayjs(generation.createdAt).format('YYYY-MM-DD_HH-mm-ss');
|
62
|
-
const
|
63
|
-
|
64
|
-
|
65
|
-
.trim();
|
62
|
+
const baseName = prompt.slice(0, 30).trim();
|
63
|
+
const sanitizedBaseName = baseName.replaceAll(/["%*/:<>?\\|]/g, '').replaceAll(/\s+/g, '_');
|
64
|
+
const safePrompt = sanitizedBaseName || 'Untitled';
|
66
65
|
|
67
|
-
// Detect file extension from URL
|
68
66
|
const fileExtension = inferFileExtensionFromImageUrl(generation.asset.url);
|
69
67
|
const fileName = `${safePrompt}_${timestamp}.${fileExtension}`;
|
70
68
|
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import { AuroraBackground } from '@lobehub/ui/awesome';
|
2
1
|
import { memo } from 'react';
|
3
2
|
import { Center, Flexbox } from 'react-layout-kit';
|
4
3
|
|
@@ -6,31 +5,18 @@ import PromptInput from '../PromptInput';
|
|
6
5
|
|
7
6
|
const EmptyState = memo(() => {
|
8
7
|
return (
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
<Flexbox
|
22
|
-
flex={1}
|
23
|
-
height="100%"
|
24
|
-
style={{
|
25
|
-
overflow: 'hidden',
|
26
|
-
zIndex: 1,
|
27
|
-
}}
|
28
|
-
>
|
29
|
-
<Center flex={1} padding={24}>
|
30
|
-
<PromptInput showTitle={true} />
|
31
|
-
</Center>
|
32
|
-
</Flexbox>
|
33
|
-
</>
|
8
|
+
<Flexbox
|
9
|
+
flex={1}
|
10
|
+
height="100%"
|
11
|
+
style={{
|
12
|
+
overflow: 'hidden',
|
13
|
+
zIndex: 1,
|
14
|
+
}}
|
15
|
+
>
|
16
|
+
<Center flex={1} padding={24}>
|
17
|
+
<PromptInput showTitle={true} />
|
18
|
+
</Center>
|
19
|
+
</Flexbox>
|
34
20
|
);
|
35
21
|
});
|
36
22
|
|
@@ -443,6 +443,9 @@ describe('AiInfraRepos', () => {
|
|
443
443
|
}),
|
444
444
|
],
|
445
445
|
enabledAiProviders: [{ id: 'openai', logo: 'logo1', name: 'OpenAI', source: 'builtin' }],
|
446
|
+
enabledChatAiProviders: [
|
447
|
+
{ id: 'openai', logo: 'logo1', name: 'OpenAI', source: 'builtin' },
|
448
|
+
],
|
446
449
|
enabledImageAiProviders: [],
|
447
450
|
runtimeConfig: {
|
448
451
|
openai: {
|
@@ -525,6 +528,9 @@ describe('AiInfraRepos', () => {
|
|
525
528
|
{ id: 'openai', logo: 'openai-logo', name: 'OpenAI', source: 'builtin' },
|
526
529
|
{ id: 'fal', logo: 'fal-logo', name: 'Fal', source: 'builtin' },
|
527
530
|
],
|
531
|
+
enabledChatAiProviders: [
|
532
|
+
{ id: 'openai', logo: 'openai-logo', name: 'OpenAI', source: 'builtin' },
|
533
|
+
],
|
528
534
|
enabledImageAiProviders: [
|
529
535
|
expect.objectContaining({
|
530
536
|
id: 'fal',
|
@@ -159,11 +159,20 @@ export class AiInfraRepos {
|
|
159
159
|
runtimeConfig[key] = merge(this.providerConfigs[key] || {}, value);
|
160
160
|
});
|
161
161
|
const enabledAiModels = allModels.filter((model) => model.enabled);
|
162
|
+
const enabledChatAiProviders = enabledAiProviders.filter((provider) => {
|
163
|
+
return allModels.some((model) => model.providerId === provider.id && model.type === 'chat');
|
164
|
+
});
|
162
165
|
const enabledImageAiProviders = enabledAiProviders.filter((provider) => {
|
163
166
|
return allModels.some((model) => model.providerId === provider.id && model.type === 'image');
|
164
167
|
});
|
165
168
|
|
166
|
-
return {
|
169
|
+
return {
|
170
|
+
enabledAiModels,
|
171
|
+
enabledAiProviders,
|
172
|
+
enabledChatAiProviders,
|
173
|
+
enabledImageAiProviders,
|
174
|
+
runtimeConfig,
|
175
|
+
};
|
167
176
|
};
|
168
177
|
|
169
178
|
getAiProviderModelList = async (providerId: string) => {
|
@@ -183,6 +183,11 @@ export const createAiProviderSlice: StateCreator<
|
|
183
183
|
return {
|
184
184
|
enabledAiModels: allModels.filter((m) => m.enabled),
|
185
185
|
enabledAiProviders: enabledAiProviders,
|
186
|
+
enabledChatAiProviders: enabledAiProviders.filter((provider) => {
|
187
|
+
return allModels.some(
|
188
|
+
(model) => model.providerId === provider.id && model.type === 'chat',
|
189
|
+
);
|
190
|
+
}),
|
186
191
|
enabledImageAiProviders: enabledAiProviders
|
187
192
|
.filter((provider) => {
|
188
193
|
return allModels.some(
|
@@ -215,7 +220,7 @@ export const createAiProviderSlice: StateCreator<
|
|
215
220
|
};
|
216
221
|
|
217
222
|
// 3. 组装最终数据结构
|
218
|
-
const enabledChatModelList = data.
|
223
|
+
const enabledChatModelList = data.enabledChatAiProviders.map((provider) => ({
|
219
224
|
...provider,
|
220
225
|
children: getModelListByType(provider.id, 'chat'),
|
221
226
|
name: provider.name || provider.id,
|
package/src/types/aiProvider.ts
CHANGED
@@ -255,6 +255,7 @@ export interface AiProviderRuntimeConfig {
|
|
255
255
|
export interface AiProviderRuntimeState {
|
256
256
|
enabledAiModels: EnabledAiModel[];
|
257
257
|
enabledAiProviders: EnabledProvider[];
|
258
|
+
enabledChatAiProviders: EnabledProvider[];
|
258
259
|
enabledImageAiProviders: EnabledProvider[];
|
259
260
|
runtimeConfig: Record<string, AiProviderRuntimeConfig>;
|
260
261
|
}
|