@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 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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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>
@@ -36,7 +36,7 @@ export const appBrowsers = {
36
36
  autoHideMenuBar: true,
37
37
  height: 800,
38
38
  identifier: 'settings',
39
- keepAlive: true,
39
+ // keepAlive: true,
40
40
  minWidth: 600,
41
41
  parentIdentifier: 'chat',
42
42
  path: '/settings',
@@ -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
- this.retrieveOrInitialize(browser);
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.0",
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": "^15.3.3",
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
- <img
433
+ <Image
433
434
  alt={`Uploaded image ${index + 1}`}
435
+ fill
434
436
  src={imageUrl}
435
- style={{ height: '100%', objectFit: 'cover', width: '100%' }}
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
- <img
484
+ <Image
482
485
  alt="Uploaded image"
486
+ fill
483
487
  src={imageUrl}
484
- style={{ height: '100%', objectFit: 'cover', width: '100%' }}
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.toUpperCase()}</Text>}
29
+ {label && <Text weight={500}>{label}</Text>}
30
30
  {children}
31
31
  </Flexbox>
32
32
  );
@@ -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 percent={'auto'} />
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 safePrompt = prompt
63
- .slice(0, 30)
64
- .replaceAll(/[^\s\w-]/g, '')
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
- <AuroraBackground
11
- style={{
12
- height: 400,
13
- inset: 0,
14
- overflow: 'hidden',
15
- pointerEvents: 'none',
16
- position: 'absolute',
17
- width: '100%',
18
- zIndex: 0,
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 { enabledAiModels, enabledAiProviders, enabledImageAiProviders, runtimeConfig };
169
+ return {
170
+ enabledAiModels,
171
+ enabledAiProviders,
172
+ enabledChatAiProviders,
173
+ enabledImageAiProviders,
174
+ runtimeConfig,
175
+ };
167
176
  };
168
177
 
169
178
  getAiProviderModelList = async (providerId: string) => {
@@ -37,6 +37,7 @@ describe('aiProviderRouter', () => {
37
37
  const mockRuntimeState: AiProviderRuntimeState = {
38
38
  enabledAiModels: [],
39
39
  enabledAiProviders: [],
40
+ enabledChatAiProviders: [],
40
41
  runtimeConfig: {},
41
42
  enabledImageAiProviders: [],
42
43
  };
@@ -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.enabledAiProviders.map((provider) => ({
223
+ const enabledChatModelList = data.enabledChatAiProviders.map((provider) => ({
219
224
  ...provider,
220
225
  children: getModelListByType(provider.id, 'chat'),
221
226
  name: provider.name || provider.id,
@@ -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
  }