@lobehub/chat 0.88.0 → 0.89.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 (43) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/locales/en_US/common.json +1 -0
  3. package/locales/en_US/error.json +2 -1
  4. package/locales/en_US/setting.json +5 -0
  5. package/locales/ja_JP/common.json +1 -0
  6. package/locales/ja_JP/error.json +2 -1
  7. package/locales/ja_JP/setting.json +5 -0
  8. package/locales/ko_KR/common.json +1 -0
  9. package/locales/ko_KR/error.json +2 -1
  10. package/locales/ko_KR/setting.json +5 -0
  11. package/locales/ru_RU/common.json +1 -0
  12. package/locales/ru_RU/error.json +2 -1
  13. package/locales/ru_RU/setting.json +5 -0
  14. package/locales/zh_CN/common.json +1 -0
  15. package/locales/zh_CN/error.json +1 -0
  16. package/locales/zh_CN/setting.json +5 -0
  17. package/locales/zh_TW/common.json +1 -0
  18. package/locales/zh_TW/error.json +2 -1
  19. package/locales/zh_TW/setting.json +5 -0
  20. package/next.config.mjs +10 -2
  21. package/package.json +1 -2
  22. package/src/app/chat/(mobile)/features/TopicList.tsx +1 -2
  23. package/src/app/home/index.tsx +3 -14
  24. package/src/app/market/(desktop)/features/AgentDetail.tsx +1 -2
  25. package/src/app/market/(desktop)/index.tsx +6 -3
  26. package/src/app/market/(mobile)/features/AgentCard.tsx +2 -2
  27. package/src/app/market/(mobile)/features/AgentDetail.tsx +1 -2
  28. package/src/app/market/(mobile)/index.tsx +8 -3
  29. package/src/app/market/features/AgentCard/AgentCardBanner.tsx +9 -22
  30. package/src/app/market/features/AgentCard/AgentCardItem.tsx +1 -6
  31. package/src/app/market/features/AgentCard/index.tsx +4 -8
  32. package/src/app/market/features/AgentDetailContent/AgentInfo/index.tsx +3 -10
  33. package/src/app/market/features/TagList/index.tsx +16 -12
  34. package/src/app/market/index.tsx +1 -3
  35. package/src/app/metadata.ts +13 -4
  36. package/src/app/settings/(mobile)/features/AvatarBanner.tsx +20 -23
  37. package/src/app/settings/(mobile)/mobile/index.tsx +1 -1
  38. package/src/app/welcome/features/Banner/Hero.tsx +11 -12
  39. package/src/app/welcome/features/Banner/index.tsx +4 -10
  40. package/src/components/FullscreenLoading/index.tsx +20 -0
  41. package/src/const/layoutTokens.ts +2 -2
  42. package/src/layout/ResponsiveLayout.client.tsx +9 -2
  43. package/src/locales/default/common.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.89.1](https://github.com/lobehub/lobe-chat/compare/v0.89.0...v0.89.1)
6
+
7
+ <sup>Released on **2023-10-12**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Remove useless dynamic import.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Remove useless dynamic import ([4a9c426](https://github.com/lobehub/lobe-chat/commit/4a9c426))
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 0.89.0](https://github.com/lobehub/lobe-chat/compare/v0.88.0...v0.89.0)
31
+
32
+ <sup>Released on **2023-10-12**</sup>
33
+
34
+ #### ✨ Features
35
+
36
+ - **agent-card**: Add and modify features for agent card.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### What's improved
44
+
45
+ - **agent-card**: Add and modify features for agent card ([3e3090a](https://github.com/lobehub/lobe-chat/commit/3e3090a))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ## [Version 0.88.0](https://github.com/lobehub/lobe-chat/compare/v0.87.0...v0.88.0)
6
56
 
7
57
  <sup>Released on **2023-10-11**</sup>
@@ -73,6 +73,7 @@
73
73
  "shareToShareGPT": "Generate ShareGPT sharing link"
74
74
  },
75
75
  "stop": "Stop",
76
+ "switchMobileLayout": "Switching to mobile layout...",
76
77
  "tab": {
77
78
  "chat": "Chat",
78
79
  "market": "Discover",
@@ -31,7 +31,8 @@
31
31
  "unlock": {
32
32
  "apikey": {
33
33
  "title": "Use Custom API Key",
34
- "description": "Enter your OpenAI API Key to start the conversation. The application will not record your API Key."
34
+ "description": "Enter your OpenAI API Key to start the conversation. The application will not record your API Key.",
35
+ "addProxyUrl": "Add OpenAI Proxy URL (optional)"
35
36
  },
36
37
  "confirm": "Confirm and Retry",
37
38
  "password": {
@@ -75,6 +75,11 @@
75
75
  "fetch": "Get list",
76
76
  "title": "Azure OpenAI",
77
77
  "serverConfig": "The administrator has enabled Azure OpenAI on the server side, switching is prohibited"
78
+ },
79
+ "customModelName": {
80
+ "desc": "Add custom models, separate multiple models with commas (,)",
81
+ "placeholder": "model1,model2,model3",
82
+ "title": "Custom Model Name"
78
83
  }
79
84
  },
80
85
  "waitingForMore": "<1>Access to other models is planned</1>, so stay tuned ✨"
@@ -73,6 +73,7 @@
73
73
  "withSystemRole": "アシスタントの役割設定を含む"
74
74
  },
75
75
  "stop": "停止",
76
+ "switchMobileLayout": "モバイルレイアウトに切り替え中...",
76
77
  "tab": {
77
78
  "chat": "チャット",
78
79
  "market": "発見",
@@ -31,7 +31,8 @@
31
31
  "unlock": {
32
32
  "apikey": {
33
33
  "title": "カスタムAPIキーの使用",
34
- "description": "OpenAI APIキーを入力すると、会話を開始できます。アプリはあなたのAPIキーを記録しません。"
34
+ "description": "OpenAI APIキーを入力すると、会話を開始できます。アプリはあなたのAPIキーを記録しません。",
35
+ "addProxyUrl": "OpenAIプロキシURLを追加(オプション)"
35
36
  },
36
37
  "closeMessage": "ヒントを閉じる",
37
38
  "confirm": "確認して再試行",
@@ -75,6 +75,11 @@
75
75
  "fetch": "リストを取得",
76
76
  "serverConfig": "管理者がサーバー側でAzure OpenAIを設定しており、切り替えが禁止されています",
77
77
  "title": "Azure OpenAI"
78
+ },
79
+ "customModelName": {
80
+ "desc": "カスタムモデルを追加します。複数のモデルはカンマ(,)で区切ってください",
81
+ "placeholder": "model1,model2,model3",
82
+ "title": "カスタムモデル名"
78
83
  }
79
84
  },
80
85
  "waitingForMore": "他のモデルは現在 <1>計画中</1> です。お楽しみに ✨"
@@ -73,6 +73,7 @@
73
73
  "withSystemRole": "어시스턴트 역할 포함"
74
74
  },
75
75
  "stop": "중지",
76
+ "switchMobileLayout": "모바일 레이아웃으로 전환 중...",
76
77
  "tab": {
77
78
  "chat": "채팅",
78
79
  "market": "탐색",
@@ -31,7 +31,8 @@
31
31
  "unlock": {
32
32
  "apikey": {
33
33
  "title": "사용자 정의 API 키 사용",
34
- "description": "OpenAI API 키를 입력하면 대화를 시작할 수 있습니다. 애플리케이션은 API 키를 기록하지 않습니다."
34
+ "description": "OpenAI API 키를 입력하면 대화를 시작할 수 있습니다. 애플리케이션은 API 키를 기록하지 않습니다.",
35
+ "addProxyUrl": "OpenAI 프록시 주소 추가하기 (선택 사항)"
35
36
  },
36
37
  "closeMessage": "알림 닫기",
37
38
  "confirm": "확인 및 다시 시도",
@@ -75,6 +75,11 @@
75
75
  "fetch": "목록 가져오기",
76
76
  "serverConfig": "관리자가 서버에서 Azure OpenAI를 구성했으므로 변경이 금지되었습니다.",
77
77
  "title": "Azure OpenAI"
78
+ },
79
+ "customModelName": {
80
+ "desc": "사용자 정의 모델 추가, 여러 모델은 쉼표(,)로 구분합니다",
81
+ "placeholder": "model1,model2,model3",
82
+ "title": "사용자 정의 모델 이름"
78
83
  }
79
84
  },
80
85
  "waitingForMore": "더 많은 모델이 <1>계획 중</1>에 있으니 기대해주세요 ✨"
@@ -73,6 +73,7 @@
73
73
  "shareToShareGPT": "Создать ссылку для обмена ShareGPT"
74
74
  },
75
75
  "stop": "Остановить",
76
+ "switchMobileLayout": "Переключение на мобильный макет...",
76
77
  "tab": {
77
78
  "chat": "Чат",
78
79
  "market": "Магазин ассистентов",
@@ -31,7 +31,8 @@
31
31
  "unlock": {
32
32
  "apikey": {
33
33
  "title": "Использовать собственный ключ API",
34
- "description": "Введите свой ключ API OpenAI, чтобы начать сеанс. Приложение не будет сохранять ваш ключ API."
34
+ "description": "Введите свой ключ API OpenAI, чтобы начать сеанс. Приложение не будет сохранять ваш ключ API.",
35
+ "addProxyUrl": "Добавить адрес прокси OpenAI (необязательно)"
35
36
  },
36
37
  "confirm": "Подтвердить и повторить попытку",
37
38
  "password": {
@@ -75,6 +75,11 @@
75
75
  "fetch": "Получить список",
76
76
  "title": "Azure OpenAI",
77
77
  "serverConfig": "Администратор настроил сервер для использования Azure OpenAI. Переключение запрещено."
78
+ },
79
+ "customModelName": {
80
+ "desc": "Добавьте пользовательскую модель, разделяя их запятыми (,)",
81
+ "placeholder": "model1,model2,model3",
82
+ "title": "Название пользовательской модели"
78
83
  }
79
84
  },
80
85
  "waitingForMore": "<1>Планируется доступ к другим моделям</1>, так что следите за обновлениями ✨"
@@ -73,6 +73,7 @@
73
73
  "withSystemRole": "包含助手角色设定"
74
74
  },
75
75
  "stop": "停止",
76
+ "switchMobileLayout": "正在切换到移动端布局...",
76
77
  "tab": {
77
78
  "chat": "会话",
78
79
  "market": "发现",
@@ -30,6 +30,7 @@
30
30
  },
31
31
  "unlock": {
32
32
  "apikey": {
33
+ "addProxyUrl": "添加 OpenAI 代理地址(可选)",
33
34
  "description": "输入你的 OpenAI API Key 即可开始会话。应用不会记录你的 API Key",
34
35
  "title": "使用自定义 API Key"
35
36
  },
@@ -50,6 +50,11 @@
50
50
  "pass": "检查通过",
51
51
  "title": "连通性检查"
52
52
  },
53
+ "customModelName": {
54
+ "desc": "增加自定义模型,多个模型使用逗号(,) 隔开",
55
+ "placeholder": "model1,model2,model3",
56
+ "title": "自定义模型名称"
57
+ },
53
58
  "endpoint": {
54
59
  "desc": "除默认地址外,必须包含 http(s)://",
55
60
  "placeholder": "https://api.openai.com/v1",
@@ -73,6 +73,7 @@
73
73
  "shareToShareGPT": "生成 ShareGPT 分享連結"
74
74
  },
75
75
  "stop": "停止",
76
+ "switchMobileLayout": "正在切換到行動端佈局...",
76
77
  "tab": {
77
78
  "chat": "聊天",
78
79
  "market": "探索",
@@ -31,7 +31,8 @@
31
31
  "unlock": {
32
32
  "apikey": {
33
33
  "title": "使用自定義 API Key",
34
- "description": "輸入你的 OpenAI API Key 即可開始會話。應用程式不會記錄你的 API Key"
34
+ "description": "輸入你的 OpenAI API Key 即可開始會話。應用程式不會記錄你的 API Key",
35
+ "addProxyUrl": "添加 OpenAI 代理地址(可選)"
35
36
  },
36
37
  "confirm": "確認並重試",
37
38
  "password": {
@@ -75,6 +75,11 @@
75
75
  "fetch": "取得列表",
76
76
  "title": "Azure OpenAI",
77
77
  "serverConfig": "管理員已在伺服器端啟用 Azure OpenAI,禁止切換"
78
+ },
79
+ "customModelName": {
80
+ "desc": "增加自訂模型,多個模型使用逗號(,) 隔開",
81
+ "placeholder": "model1,model2,model3",
82
+ "title": "自訂模型名稱"
78
83
  }
79
84
  },
80
85
  "waitingForMore": "<1>計劃支援其他模型</1>,敬請期待 ✨"
package/next.config.mjs CHANGED
@@ -16,8 +16,8 @@ const withPWA = nextPWA({
16
16
 
17
17
  /** @type {import('next').NextConfig} */
18
18
  const nextConfig = {
19
+ compress: isProd,
19
20
  reactStrictMode: true,
20
- swcMinify: true,
21
21
  images: {
22
22
  unoptimized: !isProd,
23
23
  remotePatterns: [
@@ -39,12 +39,20 @@ const nextConfig = {
39
39
  port: '',
40
40
  pathname: '/@lobehub/assets-logo/1.1.0/files/assets/**',
41
41
  },
42
+ {
43
+ protocol: 'https',
44
+ hostname: 'registry.npmmirror.com',
45
+ port: '',
46
+ pathname: '/@lobehub/assets-favicons/latest/files/assets/**',
47
+ },
42
48
  ],
43
49
  },
44
50
  experimental: {
51
+ webVitalsAttribution: ['CLS', 'LCP'],
45
52
  forceSwcTransforms: true,
53
+ optimizePackageImports: ['modern-screenshot','emoji-mart','@emoji-mart/react','@emoji-mart/data','@icons-pack/react-simple-icons','gpt-tokenizer','chroma-js'],
46
54
  },
47
- transpilePackages: ['@lobehub/ui', 'antd-style'],
55
+ transpilePackages: ['@lobehub/ui', 'antd-style', 'lodash-es'],
48
56
 
49
57
  webpack(config) {
50
58
  config.experiments = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.88.0",
3
+ "version": "0.89.1",
4
4
  "description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -66,7 +66,6 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@ant-design/colors": "^7",
69
- "@ant-design/icons": "^5",
70
69
  "@emoji-mart/data": "^1",
71
70
  "@emoji-mart/react": "^1",
72
71
  "@icons-pack/react-simple-icons": "^9",
@@ -1,11 +1,10 @@
1
1
  import { Modal } from '@lobehub/ui';
2
- import dynamic from 'next/dynamic';
3
2
  import { memo } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
5
4
 
6
5
  import { useGlobalStore } from '@/store/global';
7
6
 
8
- const TopicListContent = dynamic(() => import('../../features/TopicListContent'));
7
+ import TopicListContent from '../../features/TopicListContent';
9
8
 
10
9
  const Topics = memo(() => {
11
10
  const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
@@ -1,24 +1,13 @@
1
1
  'use client';
2
2
 
3
- import { Icon, Logo } from '@lobehub/ui';
4
- import { Loader2 } from 'lucide-react';
5
3
  import { useTranslation } from 'react-i18next';
6
- import { Center, Flexbox } from 'react-layout-kit';
4
+
5
+ import FullscreenLoading from '@/components/FullscreenLoading';
7
6
 
8
7
  const Loading = () => {
9
8
  const { t } = useTranslation();
10
9
 
11
- return (
12
- <Flexbox height={'100%'} width={'100%'}>
13
- <Center flex={1} gap={12} width={'100%'}>
14
- <Logo extra={'Chat'} size={48} type={'combine'} />
15
- <Center gap={16} horizontal>
16
- <Icon icon={Loader2} spin />
17
- {t('appInitializing', { ns: 'common' })}
18
- </Center>
19
- </Center>
20
- </Flexbox>
21
- );
10
+ return <FullscreenLoading title={t('switchMobileLayout')} />;
22
11
  };
23
12
 
24
13
  export default Loading;
@@ -1,13 +1,12 @@
1
1
  import { DraggablePanel, DraggablePanelBody, DraggablePanelContainer } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
- import dynamic from 'next/dynamic';
4
3
  import { memo, useState } from 'react';
5
4
 
6
5
  import SafeSpacing from '@/components/SafeSpacing';
7
6
  import { MARKET_SIDEBAR_WIDTH } from '@/const/layoutTokens';
8
7
  import { agentMarketSelectors, useMarketStore } from '@/store/market';
9
8
 
10
- const AgentDetailContent = dynamic(() => import('../../features/AgentDetailContent'));
9
+ import AgentDetailContent from '../../features/AgentDetailContent';
11
10
 
12
11
  const useStyles = createStyles(({ css, token, stylish }) => ({
13
12
  content: css`
@@ -1,19 +1,22 @@
1
1
  'use client';
2
2
 
3
+ import { SpotlightCard } from '@lobehub/ui';
3
4
  import dynamic from 'next/dynamic';
4
5
  import { FC, memo } from 'react';
5
6
 
6
- import { AgentCardProps } from '@/app/market/features/AgentCard';
7
+ import AgentCard from '@/app/market/features/AgentCard';
7
8
  import ResponsiveIndex from '@/components/ResponsiveIndex';
9
+ import { AgentsMarketIndexItem } from '@/types/market';
8
10
 
9
11
  import Index from '../index';
10
12
  import Layout from './layout.desktop';
11
13
 
12
14
  const Mobile: FC = dynamic(() => import('../(mobile)')) as FC;
13
- export default memo<AgentCardProps>((props) => (
15
+ export default memo<{ defaultAgents?: AgentsMarketIndexItem[] }>(({ defaultAgents }) => (
14
16
  <ResponsiveIndex Mobile={Mobile}>
15
17
  <Layout>
16
- <Index {...props} />
18
+ <Index />
19
+ <AgentCard CardRender={SpotlightCard} defaultAgents={defaultAgents} />
17
20
  </Layout>
18
21
  </ResponsiveIndex>
19
22
  ));
@@ -3,11 +3,11 @@ import { createStyles } from 'antd-style';
3
3
  import { memo } from 'react';
4
4
  import { Flexbox } from 'react-layout-kit';
5
5
 
6
- const useStyles = createStyles(({ css, token }) => ({
6
+ const useStyles = createStyles(({ css, token, isDarkMode }) => ({
7
7
  container: css`
8
8
  overflow: hidden;
9
9
  background: ${token.colorBgContainer};
10
- border: 1px solid ${token.colorFillTertiary};
10
+ border: 1px solid ${isDarkMode ? token.colorFillTertiary : token.colorFillSecondary};
11
11
  border-radius: ${token.borderRadiusLG}px;
12
12
  `,
13
13
  }));
@@ -1,11 +1,10 @@
1
1
  import { Modal } from '@lobehub/ui';
2
- import dynamic from 'next/dynamic';
3
2
  import { memo } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
5
4
 
6
5
  import { agentMarketSelectors, useMarketStore } from '@/store/market';
7
6
 
8
- const AgentDetailContent = dynamic(() => import('../../features/AgentDetailContent'));
7
+ import AgentDetailContent from '../../features/AgentDetailContent';
9
8
 
10
9
  const AgentDetail = memo(() => {
11
10
  const [showAgentSidebar, deactivateAgent] = useMarketStore((s) => [
@@ -1,12 +1,17 @@
1
+ 'use client';
2
+
1
3
  import { memo } from 'react';
2
4
 
3
- import { AgentCardProps } from '@/app/market/features/AgentCard';
5
+ import AgentCard from '@/app/market/features/AgentCard';
6
+ import { AgentsMarketIndexItem } from '@/types/market';
4
7
 
5
8
  import Index from '../index';
9
+ import CardRender from './features/AgentCard';
6
10
  import Layout from './layout.mobile';
7
11
 
8
- export default memo<AgentCardProps>((props) => (
12
+ export default memo<{ defaultAgents?: AgentsMarketIndexItem[] }>(({ defaultAgents }) => (
9
13
  <Layout>
10
- <Index {...props} />
14
+ <Index />
15
+ <AgentCard CardRender={CardRender} defaultAgents={defaultAgents} />
11
16
  </Layout>
12
17
  ));
@@ -5,7 +5,7 @@ import { Flexbox } from 'react-layout-kit';
5
5
 
6
6
  import { MetaData } from '@/types/meta';
7
7
 
8
- export const useStyles = createStyles(({ css, token }, maskColor: string) => ({
8
+ export const useStyles = createStyles(({ css, token }) => ({
9
9
  banner: css`
10
10
  position: relative;
11
11
 
@@ -14,20 +14,14 @@ export const useStyles = createStyles(({ css, token }, maskColor: string) => ({
14
14
  height: 64px;
15
15
  margin-bottom: -56px;
16
16
 
17
- background: ${token.colorFill};
17
+ background: ${token.colorFillSecondary};
18
+
19
+ mask-image: linear-gradient(to bottom, #fff, transparent);
18
20
  `,
19
21
  bannerImg: css`
20
- filter: blur(6px) saturate(1.6);
21
- `,
22
- bannerMask: css`
23
22
  position: absolute;
24
- bottom: 0;
25
- left: 0;
26
-
27
- width: 100%;
28
- height: 100%;
29
-
30
- background: linear-gradient(to bottom, transparent, ${maskColor || token.colorBgLayout});
23
+ top: -50%;
24
+ filter: blur(50px) saturate(2);
31
25
  `,
32
26
  }));
33
27
 
@@ -39,8 +33,8 @@ interface AgentCardBannerProps extends DivProps {
39
33
  }
40
34
 
41
35
  const AgentCardBanner = memo<AgentCardBannerProps>(
42
- ({ meta, className, mask, size = 8, maskColor, children, ...props }) => {
43
- const { styles, theme, cx } = useStyles(maskColor);
36
+ ({ meta, className, size = 200, children, ...props }) => {
37
+ const { styles, cx } = useStyles();
44
38
 
45
39
  return (
46
40
  <Flexbox
@@ -49,14 +43,7 @@ const AgentCardBanner = memo<AgentCardBannerProps>(
49
43
  justify={'center'}
50
44
  {...props}
51
45
  >
52
- <Avatar
53
- alt={'banner'}
54
- avatar={meta.avatar}
55
- background={meta.backgroundColor || theme.colorBgContainer}
56
- className={styles.bannerImg}
57
- style={{ transform: `scale(${size})` }}
58
- />
59
- {mask && <div className={styles.bannerMask} />}
46
+ <Avatar alt={'banner'} avatar={meta.avatar} className={styles.bannerImg} size={size} />
60
47
  {children}
61
48
  </Flexbox>
62
49
  );
@@ -22,12 +22,7 @@ const AgentCardItem = memo<AgentsMarketIndexItem>(({ meta, identifier }) => {
22
22
  const { isDarkMode } = useThemeMode();
23
23
  return (
24
24
  <Flexbox className={styles.container} onClick={() => onAgentCardClick(identifier)}>
25
- <AgentCardBanner
26
- mask
27
- maskColor={theme.colorBgContainer}
28
- meta={meta}
29
- style={{ opacity: isDarkMode ? 0.9 : 0.4 }}
30
- />
25
+ <AgentCardBanner meta={meta} style={{ opacity: isDarkMode ? 0.9 : 0.4 }} />
31
26
  <Flexbox className={styles.inner} gap={8} ref={ref}>
32
27
  <Avatar
33
28
  animation={isHovering}
@@ -1,6 +1,6 @@
1
+ import { SpotlightCardProps } from '@lobehub/ui';
1
2
  import { useResponsive } from 'antd-style';
2
- import dynamic from 'next/dynamic';
3
- import { memo, useMemo } from 'react';
3
+ import { FC, memo, useMemo } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
@@ -12,16 +12,14 @@ import AgentCardItem from './AgentCardItem';
12
12
  import Loading from './Loading';
13
13
  import { useStyles } from './style';
14
14
 
15
- const AgentCardMobile = dynamic(() => import('../../(mobile)/features/AgentCard'));
16
- const AgentCardDesktop = dynamic(() => import('@lobehub/ui/es/SpotlightCard'));
17
-
18
15
  const gridRender = (item: any) => <AgentCardItem {...item} />;
19
16
 
20
17
  export interface AgentCardProps {
18
+ CardRender: FC<SpotlightCardProps>;
21
19
  defaultAgents?: AgentsMarketIndexItem[];
22
20
  }
23
21
 
24
- const AgentCard = memo<AgentCardProps>(({ defaultAgents }) => {
22
+ const AgentCard = memo<AgentCardProps>(({ defaultAgents, CardRender }) => {
25
23
  const { t } = useTranslation('market');
26
24
  const { mobile } = useResponsive();
27
25
  const { styles } = useStyles();
@@ -42,8 +40,6 @@ const AgentCard = memo<AgentCardProps>(({ defaultAgents }) => {
42
40
 
43
41
  if (agentList.length === 0) return <Loading />;
44
42
 
45
- const CardRender = mobile ? AgentCardMobile : AgentCardDesktop;
46
-
47
43
  return (
48
44
  <Flexbox gap={mobile ? 16 : 24}>
49
45
  <TagList />
@@ -1,5 +1,4 @@
1
1
  import { Markdown, TabsNav } from '@lobehub/ui';
2
- import { useResponsive } from 'antd-style';
3
2
  import { memo, useState } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
5
4
  import { Flexbox } from 'react-layout-kit';
@@ -23,8 +22,8 @@ const AgentModalInner = memo(() => {
23
22
  s.useFetchAgent,
24
23
  s.currentIdentifier,
25
24
  ]);
26
- const { styles, theme } = useStyles();
27
- const { mobile } = useResponsive();
25
+ const { styles } = useStyles();
26
+
28
27
  const { data, isLoading } = useFetchAgent(currentIdentifier);
29
28
  const { t } = useTranslation('market');
30
29
  const [tab, setTab] = useState<string>(InfoTabs.prompt);
@@ -36,13 +35,7 @@ const AgentModalInner = memo(() => {
36
35
 
37
36
  return (
38
37
  <>
39
- <AgentCardBanner
40
- mask
41
- maskColor={mobile ? theme.colorBgContainer : undefined}
42
- meta={meta}
43
- size={10}
44
- style={{ height: 120, marginBottom: -60 }}
45
- />
38
+ <AgentCardBanner meta={meta} size={400} style={{ height: 120, marginBottom: -60 }} />
46
39
  <Header />
47
40
  <Flexbox align={'center'}>
48
41
  <TabsNav
@@ -1,4 +1,4 @@
1
- import { Button } from 'antd';
1
+ import { Button, Skeleton } from 'antd';
2
2
  import isEqual from 'fast-deep-equal';
3
3
  import { startCase } from 'lodash-es';
4
4
  import { memo } from 'react';
@@ -12,17 +12,21 @@ const TagList = memo(() => {
12
12
 
13
13
  return (
14
14
  <Flexbox gap={6} horizontal style={{ flexWrap: 'wrap' }}>
15
- {agentTagList.map((item) => (
16
- <Button
17
- key={item}
18
- onClick={() => useMarketStore.setState({ searchKeywords: item })}
19
- shape={'round'}
20
- size={'small'}
21
- type={keywords === item ? 'primary' : 'default'}
22
- >
23
- {startCase(item)}
24
- </Button>
25
- ))}
15
+ {agentTagList?.length > 0
16
+ ? agentTagList.map((item) => (
17
+ <Button
18
+ key={item}
19
+ onClick={() => useMarketStore.setState({ searchKeywords: item })}
20
+ shape={'round'}
21
+ size={'small'}
22
+ type={keywords === item ? 'primary' : 'default'}
23
+ >
24
+ {startCase(item)}
25
+ </Button>
26
+ ))
27
+ : Array.from({ length: 5 })
28
+ .fill('')
29
+ .map((_, index) => <Skeleton.Button key={index} shape={'round'} size={'small'} />)}
26
30
  </Flexbox>
27
31
  );
28
32
  });
@@ -7,10 +7,9 @@ import PageTitle from '@/components/PageTitle';
7
7
  import { useMarketStore } from '@/store/market';
8
8
  import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
9
9
 
10
- import AgentCard, { AgentCardProps } from './features/AgentCard';
11
10
  import AgentSearchBar from './features/AgentSearchBar';
12
11
 
13
- const Market = memo<AgentCardProps>(({ defaultAgents }) => {
12
+ const Market = memo(() => {
14
13
  const { t } = useTranslation('common');
15
14
  const pageTitle = genSiteHeadTitle(t('tab.market'));
16
15
 
@@ -23,7 +22,6 @@ const Market = memo<AgentCardProps>(({ defaultAgents }) => {
23
22
  <>
24
23
  <PageTitle title={pageTitle} />
25
24
  <AgentSearchBar />
26
- <AgentCard defaultAgents={defaultAgents} />
27
25
  </>
28
26
  );
29
27
  });
@@ -9,9 +9,9 @@ const { description, homepage } = pkg;
9
9
  const metadata: Metadata = {
10
10
  appleWebApp: {
11
11
  statusBarStyle: 'black-translucent',
12
- title: title,
12
+ title,
13
13
  },
14
- description: description,
14
+ description,
15
15
  icons: {
16
16
  apple:
17
17
  'https://registry.npmmirror.com/@lobehub/assets-favicons/latest/files/assets/apple-touch-icon.png',
@@ -26,13 +26,13 @@ const metadata: Metadata = {
26
26
  {
27
27
  alt: title,
28
28
  height: 360,
29
- url: 'https://registry.npmmirror.com/@lobehub/assets-favicons/1.4.0/files/assets/og-480x270.png',
29
+ url: 'https://registry.npmmirror.com/@lobehub/assets-favicons/latest/files/assets/og-480x270.png',
30
30
  width: 480,
31
31
  },
32
32
  {
33
33
  alt: title,
34
34
  height: 720,
35
- url: 'https://registry.npmmirror.com/@lobehub/assets-favicons/1.4.0/files/assets/og-960x540.png',
35
+ url: 'https://registry.npmmirror.com/@lobehub/assets-favicons/latest/files/assets/og-960x540.png',
36
36
  width: 960,
37
37
  },
38
38
  ],
@@ -50,6 +50,15 @@ const metadata: Metadata = {
50
50
  default: title,
51
51
  template: '%s · LobeChat',
52
52
  },
53
+ twitter: {
54
+ card: 'summary_large_image',
55
+ creator: '@lobehub',
56
+ description,
57
+ images: [
58
+ 'https://registry.npmmirror.com/@lobehub/assets-favicons/latest/files/assets/og-960x540.png',
59
+ ],
60
+ title,
61
+ },
53
62
  viewport: {
54
63
  initialScale: 1,
55
64
  maximumScale: 1,
@@ -6,7 +6,7 @@ import { Flexbox } from 'react-layout-kit';
6
6
 
7
7
  import { DEFAULT_USER_AVATAR_URL } from '@/const/meta';
8
8
 
9
- export const useStyles = createStyles(({ css, token }, maskColor: string) => ({
9
+ export const useStyles = createStyles(({ css, token }) => ({
10
10
  banner: css`
11
11
  position: relative;
12
12
 
@@ -15,36 +15,32 @@ export const useStyles = createStyles(({ css, token }, maskColor: string) => ({
15
15
  height: 172px;
16
16
  min-height: 172px;
17
17
  max-height: 172px;
18
+ `,
19
+ bannerBox: css`
20
+ position: relative;
21
+
22
+ width: 100%;
23
+ height: 100%;
18
24
 
19
25
  background: ${token.colorBgLayout};
26
+
27
+ mask-image: linear-gradient(to bottom, #fff, transparent);
20
28
  `,
21
29
  bannerImg: css`
22
30
  position: absolute;
23
31
  top: -50%;
24
32
  filter: blur(100px) saturate(2);
25
33
  `,
26
- bannerMask: css`
27
- position: absolute;
28
- bottom: 0;
29
- left: 0;
30
-
31
- width: 100%;
32
- height: 100%;
33
-
34
- background: linear-gradient(to bottom, transparent, ${maskColor || token.colorBgLayout});
35
- `,
36
34
  }));
37
35
 
38
36
  interface AgentCardBannerProps extends DivProps {
39
37
  avatar: string;
40
- mask?: boolean;
41
- maskColor?: string;
42
38
  size?: number;
43
39
  }
44
40
 
45
41
  const AvatarBanner = memo<AgentCardBannerProps>(
46
- ({ avatar, className, mask, size = 200, maskColor, children, ...props }) => {
47
- const { styles, cx } = useStyles(maskColor);
42
+ ({ avatar, className, size = 200, children, ...props }) => {
43
+ const { styles, cx } = useStyles();
48
44
 
49
45
  return (
50
46
  <Flexbox
@@ -53,14 +49,15 @@ const AvatarBanner = memo<AgentCardBannerProps>(
53
49
  justify={'center'}
54
50
  {...props}
55
51
  >
56
- <Image
57
- alt={'banner'}
58
- className={styles.bannerImg}
59
- height={size}
60
- src={avatar || DEFAULT_USER_AVATAR_URL}
61
- width={size}
62
- />
63
- {mask && <div className={styles.bannerMask} />}
52
+ <Flexbox align={'center'} className={styles.bannerBox} justify={'center'}>
53
+ <Image
54
+ alt={'banner'}
55
+ className={styles.bannerImg}
56
+ height={size}
57
+ src={avatar || DEFAULT_USER_AVATAR_URL}
58
+ width={size}
59
+ />
60
+ </Flexbox>
64
61
  {children}
65
62
  </Flexbox>
66
63
  );
@@ -33,7 +33,7 @@ const Setting = memo(() => {
33
33
 
34
34
  return (
35
35
  <Layout>
36
- <AvatarBanner avatar={avatar} mask>
36
+ <AvatarBanner avatar={avatar}>
37
37
  <Center style={{ position: 'absolute', zIndex: 2 }}>
38
38
  <AvatarWithUpload size={88} />
39
39
  </Center>
@@ -1,5 +1,5 @@
1
1
  import dynamic from 'next/dynamic';
2
- import { memo, useMemo } from 'react';
2
+ import { memo } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
 
5
5
  import { genSize, useStyles } from './style';
@@ -7,24 +7,23 @@ import { genSize, useStyles } from './style';
7
7
  const LogoThree = dynamic(() => import('@lobehub/ui/es/LogoThree'));
8
8
 
9
9
  const Hero = memo<{ mobile?: boolean; width: number }>(({ width, mobile }) => {
10
- const size = useMemo(
11
- () => ({
12
- base: genSize(width / 3.5, 240),
13
- desc: genSize(width / 50, 14),
14
- logo: genSize(width / 3.8, 180),
15
- title: genSize(width / 20, 32),
16
- }),
17
- [width],
18
- );
10
+ const size = {
11
+ base: genSize(width / 3.5, 240),
12
+ desc: genSize(width / 50, 14),
13
+ logo: genSize(width / 3.8, 180),
14
+ title: genSize(width / 20, 32),
15
+ };
19
16
  const { styles } = useStyles(size.base);
20
17
 
21
18
  const { t } = useTranslation('welcome');
22
19
 
23
20
  return (
24
21
  <>
25
- <LogoThree size={size.logo} style={{ marginTop: -size.logo / 5 }} />
22
+ <div style={{ height: size.logo, marginTop: -size.logo / 5, width: size.logo }}>
23
+ <LogoThree size={size.logo} />
24
+ </div>
26
25
  <div className={styles.title} style={{ fontSize: size.title }}>
27
- <span style={mobile ? { fontSize: '1.2em' } : {}}>LobeChat</span>
26
+ <strong style={mobile ? { fontSize: '1.2em' } : {}}>LobeChat</strong>
28
27
  {mobile ? <br /> : ' '}
29
28
  {t('slogan.title')}
30
29
  </div>
@@ -1,26 +1,20 @@
1
1
  'use client';
2
2
 
3
3
  import { Icon } from '@lobehub/ui';
4
- import { useSize } from 'ahooks';
5
4
  import { Button, Upload } from 'antd';
6
5
  import { SendHorizonal } from 'lucide-react';
7
- import dynamic from 'next/dynamic';
8
- import { memo, useCallback, useRef } from 'react';
6
+ import { memo, useCallback } from 'react';
9
7
  import { useTranslation } from 'react-i18next';
10
8
  import { Flexbox } from 'react-layout-kit';
11
9
 
12
10
  import { useImportConfig } from '@/hooks/useImportConfig';
13
11
  import { useSessionStore } from '@/store/session';
14
12
 
13
+ import Hero from './Hero';
15
14
  import { useStyles } from './style';
16
15
 
17
- const Hero = dynamic(() => import('./Hero'));
18
-
19
16
  const Banner = memo<{ mobile?: boolean }>(({ mobile }) => {
20
17
  const { importConfig } = useImportConfig();
21
- const ref = useRef(null);
22
- const domSize = useSize(ref);
23
- const width = domSize?.width || 1024;
24
18
  const { t } = useTranslation('welcome');
25
19
  const { styles } = useStyles();
26
20
  const [switchSession, switchBackToChat, router, isMobile] = useSessionStore((s) => [
@@ -36,8 +30,8 @@ const Banner = memo<{ mobile?: boolean }>(({ mobile }) => {
36
30
 
37
31
  return (
38
32
  <>
39
- <div className={styles.container} ref={ref}>
40
- <Hero mobile={mobile} width={width} />
33
+ <div className={styles.container}>
34
+ <Hero mobile={mobile} width={mobile ? 640 : 1024} />
41
35
  </div>
42
36
  <Flexbox
43
37
  className={styles.buttonGroup}
@@ -0,0 +1,20 @@
1
+ import { Icon, Logo } from '@lobehub/ui';
2
+ import { Loader2 } from 'lucide-react';
3
+ import { memo } from 'react';
4
+ import { Center, Flexbox } from 'react-layout-kit';
5
+
6
+ const FullscreenLoading = memo<{ title?: string }>(({ title }) => {
7
+ return (
8
+ <Flexbox height={'100%'} width={'100%'}>
9
+ <Center flex={1} gap={12} width={'100%'}>
10
+ <Logo extra={'Chat'} size={48} type={'combine'} />
11
+ <Center gap={16} horizontal>
12
+ <Icon icon={Loader2} spin />
13
+ {title}
14
+ </Center>
15
+ </Center>
16
+ </Flexbox>
17
+ );
18
+ });
19
+
20
+ export default FullscreenLoading;
@@ -2,9 +2,9 @@ import type { FormProps } from '@lobehub/ui';
2
2
 
3
3
  export const HEADER_HEIGHT = 64;
4
4
  export const MOBILE_NABBAR_HEIGHT = 44;
5
- export const MOBILE_TABBAR_HEIGHT = 60;
5
+ export const MOBILE_TABBAR_HEIGHT = 48;
6
6
  export const CHAT_TEXTAREA_HEIGHT = 200;
7
- export const CHAT_TEXTAREA_HEIGHT_MOBILE = 120;
7
+ export const CHAT_TEXTAREA_HEIGHT_MOBILE = 108;
8
8
  export const CHAT_SIDEBAR_WIDTH = 280;
9
9
  export const MARKET_SIDEBAR_WIDTH = 400;
10
10
  export const FOLDER_WIDTH = 256;
@@ -1,7 +1,9 @@
1
1
  'use client';
2
2
 
3
- import { PropsWithChildren, ReactNode } from 'react';
3
+ import { PropsWithChildren, ReactNode, Suspense } from 'react';
4
+ import { useTranslation } from 'react-i18next';
4
5
 
6
+ import FullscreenLoading from '@/components/FullscreenLoading';
5
7
  import { useIsMobile } from '@/hooks/useIsMobile';
6
8
 
7
9
  interface ServerResponsiveLayoutProps {
@@ -10,9 +12,14 @@ interface ServerResponsiveLayoutProps {
10
12
  children?: ReactNode;
11
13
  }
12
14
  const ResponsiveLayout = ({ children, Desktop, Mobile }: ServerResponsiveLayoutProps) => {
15
+ const { t } = useTranslation();
13
16
  const mobile = useIsMobile();
14
17
 
15
- return mobile ? <Mobile>{children}</Mobile> : <Desktop>{children}</Desktop>;
18
+ return (
19
+ <Suspense fallback={<FullscreenLoading title={t('appInitializing')} />}>
20
+ {mobile ? <Mobile>{children}</Mobile> : <Desktop>{children}</Desktop>}
21
+ </Suspense>
22
+ );
16
23
  };
17
24
 
18
25
  export default ResponsiveLayout;
@@ -75,6 +75,7 @@ export default {
75
75
  withSystemRole: '包含助手角色设定',
76
76
  },
77
77
  stop: '停止',
78
+ switchMobileLayout: '正在切换到移动端布局...',
78
79
  tab: {
79
80
  chat: '会话',
80
81
  market: '发现',