@lobehub/lobehub 2.0.0-next.102 → 2.0.0-next.103

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,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.103](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.102...v2.0.0-next.103)
6
+
7
+ <sup>Released on **2025-11-22**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Hide ai image config item in settings category.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Hide ai image config item in settings category, closes [#10066](https://github.com/lobehub/lobe-chat/issues/10066) ([90354eb](https://github.com/lobehub/lobe-chat/commit/90354eb))
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
+
5
30
  ## [Version 2.0.0-next.102](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.101...v2.0.0-next.102)
6
31
 
7
32
  <sup>Released on **2025-11-22**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Hide ai image config item in settings category."
6
+ ]
7
+ },
8
+ "date": "2025-11-22",
9
+ "version": "2.0.0-next.103"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "features": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.102",
3
+ "version": "2.0.0-next.103",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent 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",
@@ -104,6 +104,7 @@ export class AiModelModel {
104
104
  id: aiModels.id,
105
105
  parameters: aiModels.parameters,
106
106
  providerId: aiModels.providerId,
107
+ releasedAt: aiModels.releasedAt,
107
108
  sort: aiModels.sort,
108
109
  source: aiModels.source,
109
110
  type: aiModels.type,
@@ -1057,6 +1057,7 @@ const aihubmixModels: AIChatModelCard[] = [
1057
1057
  description:
1058
1058
  'Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态对话。',
1059
1059
  displayName: 'Nano Banana Pro',
1060
+ enabled: true,
1060
1061
  id: 'gemini-3-pro-image-preview',
1061
1062
  maxOutput: 32_768,
1062
1063
  pricing: {
@@ -1067,6 +1068,7 @@ const aihubmixModels: AIChatModelCard[] = [
1067
1068
  { name: 'textOutput', rate: 12, strategy: 'fixed', unit: 'millionTokens' },
1068
1069
  ],
1069
1070
  },
1071
+ releasedAt: '2025-11-20',
1070
1072
  settings: {
1071
1073
  searchImpl: 'params',
1072
1074
  searchProvider: 'google',
@@ -184,6 +184,7 @@ const googleChatModels: AIChatModelCard[] = [
184
184
  description:
185
185
  'Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态对话。',
186
186
  displayName: 'Nano Banana Pro',
187
+ enabled: true,
187
188
  id: 'gemini-3-pro-image-preview',
188
189
  maxOutput: 32_768,
189
190
  pricing: {
@@ -194,6 +195,7 @@ const googleChatModels: AIChatModelCard[] = [
194
195
  { name: 'textOutput', rate: 12, strategy: 'fixed', unit: 'millionTokens' },
195
196
  ],
196
197
  },
198
+ releasedAt: '2025-11-20',
197
199
  settings: {
198
200
  searchImpl: 'params',
199
201
  searchProvider: 'google',
@@ -891,6 +893,7 @@ const googleImageModels: AIImageModelCard[] = [
891
893
  displayName: 'Nano Banana Pro',
892
894
  id: 'gemini-3-pro-image-preview:image',
893
895
  type: 'image',
896
+ enabled: true,
894
897
  description:
895
898
  'Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态对话。',
896
899
  releasedAt: '2025-11-18',
@@ -25,6 +25,7 @@ const ollamaCloudModels: AIChatModelCard[] = [
25
25
  displayName: 'Gemini 3 Pro Preview',
26
26
  enabled: true,
27
27
  id: 'gemini-3-pro-preview',
28
+ releasedAt: '2025-11-20',
28
29
  type: 'chat',
29
30
  },
30
31
  {
@@ -14,6 +14,7 @@ const vertexaiChatModels: AIChatModelCard[] = [
14
14
  description:
15
15
  'Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态对话。',
16
16
  displayName: 'Nano Banana Pro',
17
+ enabled: true,
17
18
  id: 'gemini-3-pro-image-preview',
18
19
  maxOutput: 32_768,
19
20
  pricing: {
@@ -24,6 +25,7 @@ const vertexaiChatModels: AIChatModelCard[] = [
24
25
  { name: 'textOutput', rate: 12, strategy: 'fixed', unit: 'millionTokens' },
25
26
  ],
26
27
  },
28
+ releasedAt: '2025-11-20',
27
29
  settings: {
28
30
  searchImpl: 'params',
29
31
  searchProvider: 'google',
@@ -19,6 +19,7 @@ const zenmuxChatModels: AIChatModelCard[] = [
19
19
  description:
20
20
  'Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态对话。',
21
21
  displayName: 'Gemini 3 Pro Image (Nano Banana Pro)',
22
+ enabled: true,
22
23
  id: 'google/gemini-3-pro-image-preview',
23
24
  maxOutput: 32_768,
24
25
  pricing: {
@@ -28,6 +29,7 @@ const zenmuxChatModels: AIChatModelCard[] = [
28
29
  { name: 'textOutput', rate: 12, strategy: 'fixed', unit: 'millionTokens' },
29
30
  ],
30
31
  },
32
+ releasedAt: '2025-11-20',
31
33
  type: 'chat',
32
34
  },
33
35
  {
@@ -68,6 +70,7 @@ const zenmuxChatModels: AIChatModelCard[] = [
68
70
  { name: 'textOutput', rate: 12, strategy: 'fixed', unit: 'millionTokens' },
69
71
  ],
70
72
  },
73
+ releasedAt: '2025-11-20',
71
74
  type: 'chat',
72
75
  },
73
76
  {
@@ -412,6 +412,7 @@ export interface AiModelForSelect {
412
412
  */
413
413
  pricePerImage?: number;
414
414
  pricing?: Pricing;
415
+ releasedAt?: string;
415
416
  }
416
417
 
417
418
  export interface EnabledAiModel {
@@ -7,6 +7,8 @@ import numeral from 'numeral';
7
7
  import { memo, useMemo } from 'react';
8
8
  import { Flexbox } from 'react-layout-kit';
9
9
 
10
+ import NewModelBadge from '@/components/ModelSelect/NewModelBadge';
11
+
10
12
  const POPOVER_MAX_WIDTH = 320;
11
13
 
12
14
  const useStyles = createStyles(({ css, token, isDarkMode }) => ({
@@ -25,6 +27,11 @@ const useStyles = createStyles(({ css, token, isDarkMode }) => ({
25
27
  }));
26
28
 
27
29
  type ImageModelItemProps = AiModelForSelect & {
30
+ /**
31
+ * Whether to show new model badge
32
+ * @default true
33
+ */
34
+ showBadge?: boolean;
28
35
  /**
29
36
  * Whether to show popover on hover
30
37
  * @default true
@@ -33,7 +40,14 @@ type ImageModelItemProps = AiModelForSelect & {
33
40
  };
34
41
 
35
42
  const ImageModelItem = memo<ImageModelItemProps>(
36
- ({ approximatePricePerImage, description, pricePerImage, showPopover = true, ...model }) => {
43
+ ({
44
+ approximatePricePerImage,
45
+ description,
46
+ pricePerImage,
47
+ showPopover = true,
48
+ showBadge = true,
49
+ ...model
50
+ }) => {
37
51
  const { styles } = useStyles();
38
52
 
39
53
  const priceLabel = useMemo(() => {
@@ -67,6 +81,7 @@ const ImageModelItem = memo<ImageModelItemProps>(
67
81
  <Text ellipsis title={model.displayName || model.id}>
68
82
  {model.displayName || model.id}
69
83
  </Text>
84
+ {showBadge && <NewModelBadge releasedAt={model.releasedAt} />}
70
85
  </Flexbox>
71
86
  );
72
87
 
@@ -2,7 +2,6 @@ import { EnabledProviderWithModels } from '@lobechat/types';
2
2
  import { ActionIcon, Icon, Select, type SelectProps } from '@lobehub/ui';
3
3
  import { createStyles, useTheme } from 'antd-style';
4
4
  import { LucideArrowRight, LucideBolt } from 'lucide-react';
5
- import Link from 'next/link';
6
5
  import { memo, useMemo } from 'react';
7
6
  import { useTranslation } from 'react-i18next';
8
7
  import { Flexbox } from 'react-layout-kit';
@@ -107,13 +106,15 @@ const ModelSelect = memo(() => {
107
106
  provider={provider.id}
108
107
  source={provider.source}
109
108
  />
110
- <Link href={`/settings?active=provider&provider=${provider.id}`}>
111
- <ActionIcon
112
- icon={LucideBolt}
113
- size={'small'}
114
- title={t('ModelSwitchPanel.goToSettings')}
115
- />
116
- </Link>
109
+ <ActionIcon
110
+ icon={LucideBolt}
111
+ onClick={(e) => {
112
+ e.stopPropagation();
113
+ navigate(`/settings?active=provider&provider=${provider.id}`);
114
+ }}
115
+ size={'small'}
116
+ title={t('ModelSwitchPanel.goToSettings')}
117
+ />
117
118
  </Flexbox>
118
119
  ),
119
120
  options: getImageModels(provider),
@@ -129,7 +130,7 @@ const ModelSelect = memo(() => {
129
130
 
130
131
  if (!modelInfo) return props.label;
131
132
 
132
- return <ImageModelItem {...modelInfo} showPopover={false} />;
133
+ return <ImageModelItem {...modelInfo} showBadge={false} showPopover={false} />;
133
134
  };
134
135
 
135
136
  return (
@@ -22,7 +22,7 @@ import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfi
22
22
  export const useCategory = () => {
23
23
  const { t } = useTranslation('setting');
24
24
  const mobile = useServerConfigStore((s) => s.isMobile);
25
- const { enableSTT, hideDocs } = useServerConfigStore(featureFlagsSelectors);
25
+ const { enableSTT, hideDocs, showAiImage } = useServerConfigStore(featureFlagsSelectors);
26
26
 
27
27
  const cateItems: MenuProps['items'] = useMemo(
28
28
  () =>
@@ -50,7 +50,7 @@ export const useCategory = () => {
50
50
  key: SettingsTabs.Provider,
51
51
  label: t('tab.provider'),
52
52
  },
53
- {
53
+ showAiImage && {
54
54
  icon: <Icon icon={ImageIcon} />,
55
55
  key: SettingsTabs.Image,
56
56
  label: t('tab.image'),
@@ -84,7 +84,7 @@ export const useCategory = () => {
84
84
  label: t('tab.about'),
85
85
  },
86
86
  ].filter(Boolean) as MenuProps['items'],
87
- [t, enableSTT, hideDocs, mobile],
87
+ [t, enableSTT, hideDocs, mobile, showAiImage],
88
88
  );
89
89
 
90
90
  return cateItems;
@@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next';
9
9
  import { Flexbox } from 'react-layout-kit';
10
10
 
11
11
  import { ModelInfoTags } from '@/components/ModelSelect';
12
+ import NewModelBadge from '@/components/ModelSelect/NewModelBadge';
12
13
  import { useIsMobile } from '@/hooks/useIsMobile';
13
14
  import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
14
15
  import { formatPriceByCurrency } from '@/utils/format';
@@ -17,7 +18,6 @@ import {
17
18
  getTextInputUnitRate,
18
19
  getTextOutputUnitRate,
19
20
  } from '@/utils/pricing';
20
- import { isNewReleaseDate } from '@/utils/time';
21
21
 
22
22
  import ModelConfigModal from './ModelConfigModal';
23
23
  import { ProviderSettingsContext } from './ProviderSettingsContext';
@@ -163,12 +163,7 @@ const ModelItem = memo<ModelItemProps>(
163
163
 
164
164
  const isMobile = useIsMobile();
165
165
 
166
- const NewTag =
167
- releasedAt && isNewReleaseDate(releasedAt) ? (
168
- <Tag color="blue" style={{ marginLeft: 8 }}>
169
- {t('new', { ns: 'common' })}
170
- </Tag>
171
- ) : null;
166
+ const NewTag = <NewModelBadge releasedAt={releasedAt} />;
172
167
 
173
168
  const ModelIdTag = (
174
169
  <Tag onClick={copyModelId} style={{ cursor: 'pointer', marginRight: 0 }}>
@@ -0,0 +1,23 @@
1
+ import { Tag } from '@lobehub/ui';
2
+ import { memo } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+
5
+ import { isNewReleaseDate } from '@/utils/time';
6
+
7
+ interface NewModelBadgeProps {
8
+ releasedAt?: string;
9
+ }
10
+
11
+ const NewModelBadge = memo<NewModelBadgeProps>(({ releasedAt }) => {
12
+ const { t } = useTranslation('common');
13
+
14
+ if (!releasedAt || !isNewReleaseDate(releasedAt)) return null;
15
+
16
+ return (
17
+ <Tag color="blue" size="small">
18
+ {t('new')}
19
+ </Tag>
20
+ );
21
+ });
22
+
23
+ export default NewModelBadge;
@@ -21,6 +21,8 @@ import { Flexbox } from 'react-layout-kit';
21
21
  import { AiProviderSourceType } from '@/types/aiProvider';
22
22
  import { formatTokenNumber } from '@/utils/format';
23
23
 
24
+ import NewModelBadge from './NewModelBadge';
25
+
24
26
  export const TAG_CLASSNAME = 'lobe-model-info-tags';
25
27
 
26
28
  const useStyles = createStyles(({ css, token }) => ({
@@ -179,6 +181,7 @@ interface ModelItemRenderProps extends ChatModelCard {
179
181
 
180
182
  export const ModelItemRender = memo<ModelItemRenderProps>(({ showInfoTag = true, ...model }) => {
181
183
  const { mobile } = useResponsive();
184
+
182
185
  return (
183
186
  <Flexbox
184
187
  align={'center'}
@@ -202,6 +205,7 @@ export const ModelItemRender = memo<ModelItemRenderProps>(({ showInfoTag = true,
202
205
  <Text style={mobile ? { maxWidth: '60vw', overflowX: 'auto', whiteSpace: 'nowrap' } : {}}>
203
206
  {model.displayName || model.id}
204
207
  </Text>
208
+ <NewModelBadge releasedAt={model.releasedAt} />
205
209
  </Flexbox>
206
210
  {showInfoTag && <ModelInfoTags {...model} />}
207
211
  </Flexbox>
@@ -40,6 +40,7 @@ export type ProviderModelListItem = {
40
40
  parameters?: ModelParamsSchema;
41
41
  pricePerImage?: number;
42
42
  pricing?: Pricing;
43
+ releasedAt?: string;
43
44
  };
44
45
 
45
46
  type ModelNormalizer = (model: EnabledAiModel) => Promise<ProviderModelListItem>;
@@ -67,6 +68,7 @@ export const normalizeChatModel = (model: EnabledAiModel): ProviderModelListItem
67
68
  contextWindowTokens: model.contextWindowTokens,
68
69
  displayName: model.displayName ?? '',
69
70
  id: model.id,
71
+ releasedAt: model.releasedAt,
70
72
  });
71
73
 
72
74
  export const normalizeImageModel = async (
@@ -107,6 +109,7 @@ export const normalizeImageModel = async (
107
109
  contextWindowTokens: model.contextWindowTokens,
108
110
  displayName: model.displayName ?? '',
109
111
  id: model.id,
112
+ releasedAt: model.releasedAt,
110
113
  ...(parameters && { parameters }),
111
114
  ...(description && { description }),
112
115
  ...(pricing && { pricing }),