@lobehub/chat 0.73.0 → 0.75.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 (106) hide show
  1. package/.github/workflows/contributor-help.yml +1 -1
  2. package/.github/workflows/sync.yml +43 -0
  3. package/.github/workflows/wiki-sync.yml +1 -0
  4. package/CHANGELOG.md +87 -0
  5. package/README.md +40 -6
  6. package/{README-zh_CN.md → README.zh-CN.md} +38 -4
  7. package/docs/Home.md +6 -0
  8. package/docs/Upstream-Sync.en-US.md +16 -0
  9. package/docs/Upstream-Sync.zh-CN.md +16 -0
  10. package/docs/_Sidebar.md +6 -0
  11. package/locales/en_US/market.json +12 -0
  12. package/locales/en_US/plugin.json +11 -2
  13. package/locales/en_US/setting.json +63 -2
  14. package/locales/ru_RU/market.json +12 -0
  15. package/locales/ru_RU/plugin.json +44 -49
  16. package/locales/ru_RU/setting.json +63 -2
  17. package/locales/zh_CN/common.json +1 -1
  18. package/locales/zh_CN/market.json +12 -0
  19. package/locales/zh_CN/plugin.json +19 -5
  20. package/locales/zh_CN/setting.json +62 -14
  21. package/package.json +5 -4
  22. package/scripts/i18nWorkflow/const.ts +19 -0
  23. package/scripts/i18nWorkflow/genDefaultLocale.ts +19 -0
  24. package/scripts/i18nWorkflow/genDiff.ts +50 -0
  25. package/scripts/i18nWorkflow/genResources.ts +26 -0
  26. package/scripts/i18nWorkflow/index.ts +15 -0
  27. package/scripts/i18nWorkflow/utils.ts +85 -0
  28. package/src/const/layoutTokens.ts +3 -1
  29. package/src/const/market.ts +15 -0
  30. package/src/const/url.ts +29 -4
  31. package/src/features/AgentSetting/AgentConfig/index.tsx +1 -2
  32. package/src/features/AgentSetting/AgentPlugin/MarketSettingModal.tsx +1 -1
  33. package/src/features/AgentSetting/store/initialState.ts +1 -1
  34. package/src/features/MobileTabBar/index.tsx +2 -2
  35. package/src/features/SideBar/TopActions.tsx +2 -2
  36. package/src/locales/default/market.ts +12 -0
  37. package/src/locales/resources/en_US.ts +2 -0
  38. package/src/locales/resources/ru_RU.ts +2 -0
  39. package/src/locales/resources/zh_CN.ts +2 -0
  40. package/src/pages/chat/features/Conversation/Input/{Action.tsx → ActionBar/ActionLeft.tsx} +2 -2
  41. package/src/pages/chat/features/Conversation/Input/ActionBar/ActionRight.tsx +36 -0
  42. package/src/pages/chat/features/Conversation/Input/Desktop.tsx +40 -0
  43. package/src/pages/chat/features/Conversation/Input/Footer/SaveTopic.tsx +27 -0
  44. package/src/pages/chat/features/Conversation/Input/Footer/index.tsx +35 -0
  45. package/src/pages/chat/features/Conversation/Input/Mobile.tsx +12 -0
  46. package/src/pages/chat/features/Conversation/Input/index.tsx +20 -38
  47. package/src/pages/chat/features/SessionList/Header/Desktop.tsx +6 -13
  48. package/src/pages/chat/features/SessionList/Header/Mobile.tsx +1 -1
  49. package/src/pages/chat/features/SessionList/List/AddButton.tsx +1 -1
  50. package/src/pages/chat/features/SessionList/List/Item/index.tsx +4 -3
  51. package/src/pages/chat/features/SessionList/List/index.tsx +1 -3
  52. package/src/pages/chat/features/SessionList/Mobile.tsx +2 -12
  53. package/src/pages/chat/features/SessionSearchBar/index.tsx +26 -0
  54. package/src/pages/chat/features/Sidebar/Topic/index.tsx +18 -6
  55. package/src/pages/chat/features/Sidebar/TopicSearchBar/index.tsx +24 -0
  56. package/src/pages/chat/features/Sidebar/index.tsx +2 -9
  57. package/src/pages/market/features/AgentCard/AgentCardBanner.tsx +64 -0
  58. package/src/pages/market/features/AgentCard/AgentCardItem.tsx +57 -0
  59. package/src/pages/market/features/AgentCard/Loading.tsx +13 -0
  60. package/src/pages/market/features/AgentCard/index.tsx +37 -0
  61. package/src/pages/market/features/AgentCard/style.ts +22 -0
  62. package/src/pages/market/features/AgentSearchBar/index.tsx +26 -0
  63. package/src/pages/market/features/Header/Desktop.tsx +15 -4
  64. package/src/pages/market/features/Header/Mobile.tsx +7 -24
  65. package/src/pages/market/features/SideBar/AgentInfo/Comment.tsx +16 -0
  66. package/src/pages/market/features/SideBar/AgentInfo/Header.tsx +68 -0
  67. package/src/pages/market/features/SideBar/AgentInfo/Loading.tsx +61 -0
  68. package/src/pages/market/features/SideBar/AgentInfo/TokenTag.tsx +25 -0
  69. package/src/pages/market/features/SideBar/AgentInfo/index.tsx +68 -0
  70. package/src/pages/market/features/SideBar/AgentInfo/style.ts +46 -0
  71. package/src/pages/market/features/SideBar/Desktop.tsx +70 -0
  72. package/src/pages/market/features/SideBar/Mobile.tsx +36 -0
  73. package/src/pages/market/features/SideBar/index.tsx +23 -0
  74. package/src/pages/market/features/TagList/index.tsx +29 -0
  75. package/src/pages/market/index.page.tsx +11 -2
  76. package/src/pages/market/layout.mobile.tsx +8 -7
  77. package/src/pages/market/layout.tsx +39 -1
  78. package/src/pages/settings/features/Header/Desktop.tsx +2 -4
  79. package/src/pages/settings/features/Header/Mobile.tsx +1 -1
  80. package/src/pages/settings/features/Settings/Common.tsx +1 -2
  81. package/src/pages/settings/features/Settings/index.tsx +17 -4
  82. package/src/services/agentMarket.ts +21 -0
  83. package/src/store/global/initialState.ts +1 -2
  84. package/src/store/market/action.ts +57 -0
  85. package/src/store/market/index.ts +3 -0
  86. package/src/store/market/initialState.ts +17 -0
  87. package/src/store/market/selectors.ts +36 -0
  88. package/src/store/market/store.ts +51 -0
  89. package/src/store/session/slices/session/action.ts +5 -3
  90. package/src/store/session/slices/session/initialState.ts +2 -0
  91. package/src/store/session/slices/session/reducers/session.test.ts +4 -4
  92. package/src/types/market.ts +19 -0
  93. package/src/types/meta.ts +1 -1
  94. package/src/utils/filter.ts +1 -1
  95. package/src/utils/findDuplicates.ts +22 -0
  96. package/scripts/genDefaultLocale.ts +0 -18
  97. package/scripts/genResources.ts +0 -51
  98. package/scripts/toc.ts +0 -7
  99. package/src/components/Empty/index.tsx +0 -63
  100. package/src/components/Empty/style.ts +0 -31
  101. package/src/components/SelectWithImg/index.tsx +0 -97
  102. package/src/components/SelectWithImg/styles.ts +0 -43
  103. package/src/pages/chat/features/Conversation/Input/ActionRight.tsx +0 -31
  104. package/src/pages/chat/features/Conversation/Input/Footer.tsx +0 -49
  105. /package/src/components/{ManifestPreviewer.tsx → ManifestPreviewer/index.tsx} +0 -0
  106. /package/src/pages/chat/features/Conversation/Input/{Token.tsx → ActionBar/Token.tsx} +0 -0
@@ -0,0 +1,85 @@
1
+ import { consola } from 'consola';
2
+ import { colors } from 'consola/utils';
3
+ import { tocForResources } from 'i18next-resources-for-ts';
4
+ import { readFileSync, writeFileSync } from 'node:fs';
5
+ import { resolve } from 'node:path';
6
+
7
+ import i18nConfig from '../../.i18nrc';
8
+ import { srcLocalesResources } from './const';
9
+
10
+ export const readJSON = (filePath: string) => {
11
+ const data = readFileSync(filePath, 'utf8');
12
+ return JSON.parse(data);
13
+ };
14
+
15
+ export const writeJSON = (filePath: string, data: any) => {
16
+ const jsonStr = JSON.stringify(data, null, 2);
17
+ writeFileSync(filePath, jsonStr, 'utf8');
18
+ };
19
+
20
+ export const genResourcesContent = (locales: string[]) => {
21
+ let index = '';
22
+ let indexObj = '';
23
+
24
+ for (const locale of locales) {
25
+ index += `import ${locale} from "./${locale}";\n`;
26
+ indexObj += ` "${locale.replace('_', '-')}": ${locale},\n`;
27
+ }
28
+
29
+ return `${index}
30
+ const resources = {
31
+ ${indexObj}} as const;
32
+ export default resources;
33
+ export const defaultResources = ${i18nConfig.entryLocale};
34
+ export type Resources = typeof resources;
35
+ export type DefaultResources = typeof defaultResources;
36
+ export type Namespaces = keyof DefaultResources;
37
+ export type Locales = keyof Resources;
38
+ `;
39
+ };
40
+
41
+ export const genNamespaceList = (files: string[], locale: string) => {
42
+ return files.map((file) => ({
43
+ name: file.replace('.json', ''),
44
+ path: resolve(i18nConfig.output, locale, file),
45
+ }));
46
+ };
47
+
48
+ export const genToc = (files: string[], locale: string) => {
49
+ const ns = genNamespaceList(files, locale);
50
+ let toc = tocForResources(ns, srcLocalesResources).replaceAll('\\', '/');
51
+ if (locale === i18nConfig.entryLocale) {
52
+ toc = toc.replaceAll('.json', '').replaceAll('../../../locales/zh_CN', '../default');
53
+ }
54
+ return toc;
55
+ };
56
+
57
+ export const diff = (obj1: any, obj2: any, prefix: string = ''): string[] => {
58
+ let result: string[] = [];
59
+ for (const key in obj1) {
60
+ if (!obj2[key]) continue;
61
+ if (typeof obj1[key] === 'object' && obj1[key] !== null && obj2[key]) {
62
+ result = result.concat(diff(obj1[key], obj2[key], `${key}.`));
63
+ } else if (obj1[key] !== obj2[key]) {
64
+ result.push(`${prefix}${key}`);
65
+ }
66
+ }
67
+ return result;
68
+ };
69
+
70
+ export const unset = (obj: any, key: string) => {
71
+ const keys = key.split('.');
72
+ const lastKey = keys.pop();
73
+ const lastObj = keys.reduce((obj, key) => (obj[key] = obj[key] || {}), obj);
74
+ if (lastKey) delete lastObj[lastKey];
75
+ };
76
+
77
+ export const tagBlue = (text: string) => colors.bgBlueBright(colors.black(` ${text} `));
78
+ export const tagYellow = (text: string) => colors.bgYellowBright(colors.black(` ${text} `));
79
+ export const tagGreen = (text: string) => colors.bgGreenBright(colors.black(` ${text} `));
80
+ export const tagWhite = (text: string) => colors.bgWhiteBright(colors.black(` ${text} `));
81
+
82
+ export const split = (name: string) => {
83
+ consola.log('');
84
+ consola.log(colors.gray(`========================== ${name} ==============================`));
85
+ };
@@ -5,8 +5,10 @@ export const MOBILE_NABBAR_HEIGHT = 44;
5
5
  export const MOBILE_TABBAR_HEIGHT = 48;
6
6
  export const CHAT_TEXTAREA_HEIGHT = 200;
7
7
  export const CHAT_SIDEBAR_WIDTH = 280;
8
+ export const MARKET_SIDEBAR_WIDTH = 400;
8
9
  export const FOLDER_WIDTH = 256;
10
+ export const MAX_WIDTH = 1024;
9
11
  export const FORM_STYLE: FormProps = {
10
12
  itemMinWidth: 'max(30%,240px)',
11
- style: { maxWidth: 1024, width: '100%' },
13
+ style: { maxWidth: MAX_WIDTH, width: '100%' },
12
14
  };
@@ -0,0 +1,15 @@
1
+ import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
2
+ import { AgentsMarketItem } from '@/types/market';
3
+
4
+ import { DEFAULT_AGENT_META } from './meta';
5
+
6
+ export const DEFAULT_AGENTS_MARKET_ITEM: AgentsMarketItem = {
7
+ author: '',
8
+ config: DEFAULT_AGENT_CONFIG,
9
+ createAt: Date.now().toString(),
10
+ homepage: '',
11
+ identifier: '',
12
+ manifest: '',
13
+ meta: DEFAULT_AGENT_META,
14
+ schemaVersion: 1,
15
+ };
package/src/const/url.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import urlJoin from 'url-join';
2
+
1
3
  import { localeOptions } from '@/locales/options';
2
4
  import { Locales } from '@/locales/resources';
3
5
 
@@ -11,15 +13,38 @@ export const FEEDBACK = pkg.bugs.url;
11
13
  export const DISCORD = 'https://discord.gg/AYFPHvv2jT';
12
14
 
13
15
  export const PLUGINS_INDEX_URL =
14
- process.env.PLUGINS_INDEX_URL ?? 'https://chat-plugins.lobehub.com/index';
16
+ process.env.PLUGINS_INDEX_URL ?? 'https://chat-plugins.lobehub.com';
17
+
18
+ export const DEFAULT_LANG = 'en-US';
15
19
 
16
- export const getPluginIndexJSON = (lang: Locales = 'en-US', baseUrl = PLUGINS_INDEX_URL) => {
17
- if (lang === 'en-US' || !localeOptions.map((o) => o.value).includes(lang)) return baseUrl;
20
+ export const checkLang = (lang: Locales) => {
21
+ return lang === DEFAULT_LANG || !localeOptions.map((o) => o.value).includes(lang);
22
+ };
23
+ export const getPluginIndexJSON = (lang: Locales = DEFAULT_LANG, baseUrl = PLUGINS_INDEX_URL) => {
24
+ if (checkLang(lang)) return baseUrl;
18
25
 
19
- return `${baseUrl}.${lang}.json`;
26
+ return urlJoin(baseUrl, `index.${lang}.json`);
20
27
  };
21
28
 
22
29
  export const AGENTS_INDEX_URL = process.env.AGENTS_INDEX_URL ?? 'https://chat-agents.lobehub.com';
23
30
 
31
+ export const getAgentIndexJSON = (lang: Locales = DEFAULT_LANG, baseUrl = AGENTS_INDEX_URL) => {
32
+ if (checkLang(lang)) return baseUrl;
33
+
34
+ return urlJoin(baseUrl, `index.${lang}.json`);
35
+ };
36
+
37
+ export const getAgentJSON = (
38
+ identifier: string,
39
+ lang: Locales = DEFAULT_LANG,
40
+ baseUrl = AGENTS_INDEX_URL,
41
+ ) => {
42
+ if (checkLang(lang)) return urlJoin(baseUrl, `${identifier}.json`);
43
+
44
+ return urlJoin(baseUrl, `${identifier}.${lang}.json`);
45
+ };
46
+
47
+ export const AGENTS_INDEX_GITHUB = 'https://github.com/lobehub/lobe-chat-agents';
48
+
24
49
  export const SESSION_CHAT_URL = (id: string = INBOX_SESSION_ID, mobile?: boolean) =>
25
50
  mobile ? `/chat/mobile#session=${id}` : `/chat#session=${id}`;
@@ -1,4 +1,4 @@
1
- import { Form, ItemGroup, SliderWithInput } from '@lobehub/ui';
1
+ import { Form, ItemGroup, SelectWithImg, SliderWithInput } from '@lobehub/ui';
2
2
  import { ConfigProvider, Input, Select, Switch } from 'antd';
3
3
  import { useTheme, useThemeMode } from 'antd-style';
4
4
  import isEqual from 'fast-deep-equal';
@@ -7,7 +7,6 @@ import { BrainCog, LayoutList, MessageSquare, MessagesSquare } from 'lucide-reac
7
7
  import { memo, useMemo } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
- import SelectWithImg from '@/components/SelectWithImg';
11
10
  import { FORM_STYLE } from '@/const/layoutTokens';
12
11
  import { LanguageModel } from '@/types/llm';
13
12
  import { LobeAgentConfigKeys } from '@/types/session';
@@ -62,7 +62,7 @@ const MarketSettingModal = memo<MarketSettingModalProps>(({ open, onOpenChange }
62
62
  <Input
63
63
  defaultValue={PLUGINS_INDEX_URL}
64
64
  disabled
65
- placeholder={'https://xxxxx.com/index.json'}
65
+ placeholder={'https://xxxxx.com'}
66
66
  />
67
67
  </Tooltip>
68
68
  </Form.Item>
@@ -19,7 +19,7 @@ export const initialState: State = {
19
19
  avatar: false,
20
20
  backgroundColor: false,
21
21
  description: false,
22
- tag: false,
22
+ tags: false,
23
23
  title: false,
24
24
  },
25
25
 
@@ -1,6 +1,6 @@
1
1
  import { Icon, MobileTabBar, type MobileTabBarProps } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
- import { MessageSquare, Sticker } from 'lucide-react';
3
+ import { Bot, MessageSquare } from 'lucide-react';
4
4
  import Router from 'next/router';
5
5
  import { rgba } from 'polished';
6
6
  import { memo, useMemo } from 'react';
@@ -33,7 +33,7 @@ export default memo<{ className?: string }>(({ className }) => {
33
33
  title: t('tab.chat'),
34
34
  },
35
35
  {
36
- icon: (active) => <Icon className={active ? styles.active : undefined} icon={Sticker} />,
36
+ icon: (active) => <Icon className={active ? styles.active : undefined} icon={Bot} />,
37
37
  key: 'market',
38
38
  onClick: () => {
39
39
  Router.push({ hash: '', pathname: `/market` });
@@ -1,5 +1,5 @@
1
1
  import { ActionIcon } from '@lobehub/ui';
2
- import { MessageSquare, Sticker } from 'lucide-react';
2
+ import { Bot, MessageSquare } from 'lucide-react';
3
3
  import Router from 'next/router';
4
4
  import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
@@ -32,7 +32,7 @@ const TopActions = memo<TopActionProps>(({ tab, setTab }) => {
32
32
  />
33
33
  <ActionIcon
34
34
  active={tab === 'market'}
35
- icon={Sticker}
35
+ icon={Bot}
36
36
  onClick={() => {
37
37
  if (Router.asPath.startsWith('/market')) return;
38
38
  Router.push('/market');
@@ -0,0 +1,12 @@
1
+ export default {
2
+ addAgent: '添加助手',
3
+ search: {
4
+ placeholder: '搜索助手名称介绍或关键词...',
5
+ },
6
+ sidebar: {
7
+ comment: '讨论区',
8
+ prompt: '提示词',
9
+ title: '助手详情',
10
+ },
11
+ submitAgent: '提交助手',
12
+ };
@@ -1,6 +1,7 @@
1
1
  import common from '../../../locales/en_US/common.json';
2
2
  import empty from '../../../locales/en_US/empty.json';
3
3
  import error from '../../../locales/en_US/error.json';
4
+ import market from '../../../locales/en_US/market.json';
4
5
  import plugin from '../../../locales/en_US/plugin.json';
5
6
  import setting from '../../../locales/en_US/setting.json';
6
7
  import welcome from '../../../locales/en_US/welcome.json';
@@ -9,6 +10,7 @@ const resources = {
9
10
  common,
10
11
  empty,
11
12
  error,
13
+ market,
12
14
  plugin,
13
15
  setting,
14
16
  welcome,
@@ -1,6 +1,7 @@
1
1
  import common from '../../../locales/ru_RU/common.json';
2
2
  import empty from '../../../locales/ru_RU/empty.json';
3
3
  import error from '../../../locales/ru_RU/error.json';
4
+ import market from '../../../locales/ru_RU/market.json';
4
5
  import plugin from '../../../locales/ru_RU/plugin.json';
5
6
  import setting from '../../../locales/ru_RU/setting.json';
6
7
  import welcome from '../../../locales/ru_RU/welcome.json';
@@ -9,6 +10,7 @@ const resources = {
9
10
  common,
10
11
  empty,
11
12
  error,
13
+ market,
12
14
  plugin,
13
15
  setting,
14
16
  welcome,
@@ -1,6 +1,7 @@
1
1
  import common from '../default/common';
2
2
  import empty from '../default/empty';
3
3
  import error from '../default/error';
4
+ import market from '../default/market';
4
5
  import plugin from '../default/plugin';
5
6
  import setting from '../default/setting';
6
7
  import welcome from '../default/welcome';
@@ -9,6 +10,7 @@ const resources = {
9
10
  common,
10
11
  empty,
11
12
  error,
13
+ market,
12
14
  plugin,
13
15
  setting,
14
16
  welcome,
@@ -9,7 +9,7 @@ import { useSessionStore } from '@/store/session';
9
9
  import { agentSelectors } from '@/store/session/selectors';
10
10
  import { LanguageModel } from '@/types/llm';
11
11
 
12
- const InputActions = memo(() => {
12
+ const ActionLeft = memo(() => {
13
13
  const { t } = useTranslation('setting');
14
14
 
15
15
  const [model, temperature, historyCount, unlimited, updateAgentConfig] = useSessionStore((s) => {
@@ -107,4 +107,4 @@ const InputActions = memo(() => {
107
107
  );
108
108
  });
109
109
 
110
- export default InputActions;
110
+ export default ActionLeft;
@@ -0,0 +1,36 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import { Popconfirm } from 'antd';
3
+ import { Eraser } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import { useSessionStore } from '@/store/session';
8
+
9
+ import SaveTopic from '../Footer/SaveTopic';
10
+
11
+ const ActionsRight = memo(() => {
12
+ const { t } = useTranslation('setting');
13
+ const [clearMessage] = useSessionStore((s) => [s.clearMessage, s.updateAgentConfig]);
14
+
15
+ return (
16
+ <>
17
+ <SaveTopic />
18
+ <Popconfirm
19
+ cancelText={t('cancel', { ns: 'common' })}
20
+ okButtonProps={{ danger: true }}
21
+ okText={t('ok', { ns: 'common' })}
22
+ onConfirm={() => clearMessage()}
23
+ placement={'topRight'}
24
+ title={t('confirmClearCurrentMessages', { ns: 'common' })}
25
+ >
26
+ <ActionIcon
27
+ icon={Eraser}
28
+ placement={'bottom'}
29
+ title={t('clearCurrentMessages', { ns: 'common' })}
30
+ />
31
+ </Popconfirm>
32
+ </>
33
+ );
34
+ });
35
+
36
+ export default ActionsRight;
@@ -0,0 +1,40 @@
1
+ import { DraggablePanel } from '@lobehub/ui';
2
+ import { ReactNode, memo } from 'react';
3
+
4
+ import { CHAT_TEXTAREA_HEIGHT, HEADER_HEIGHT } from '@/const/layoutTokens';
5
+ import { useGlobalStore } from '@/store/global';
6
+
7
+ interface ChatInputDesktopLayoutProps {
8
+ children: ReactNode;
9
+ expand?: boolean;
10
+ }
11
+
12
+ const ChatInputDesktopLayout = memo<ChatInputDesktopLayoutProps>(({ children, expand }) => {
13
+ const [inputHeight, updatePreference] = useGlobalStore((s) => [
14
+ s.preference.inputHeight,
15
+ s.updatePreference,
16
+ ]);
17
+
18
+ return (
19
+ <DraggablePanel
20
+ expandable={false}
21
+ fullscreen={expand}
22
+ headerHeight={HEADER_HEIGHT}
23
+ minHeight={CHAT_TEXTAREA_HEIGHT}
24
+ onSizeChange={(_, size) => {
25
+ if (!size) return;
26
+
27
+ updatePreference({
28
+ inputHeight: typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
29
+ });
30
+ }}
31
+ placement="bottom"
32
+ size={{ height: inputHeight, width: '100%' }}
33
+ style={{ zIndex: 10 }}
34
+ >
35
+ {children}
36
+ </DraggablePanel>
37
+ );
38
+ });
39
+
40
+ export default ChatInputDesktopLayout;
@@ -0,0 +1,27 @@
1
+ import { ActionIcon, Icon, Tooltip } from '@lobehub/ui';
2
+ import { Button } from 'antd';
3
+ import { useResponsive } from 'antd-style';
4
+ import { LucideGalleryVerticalEnd } from 'lucide-react';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ import { useSessionStore } from '@/store/session';
9
+
10
+ const SaveTopic = memo(() => {
11
+ const { t } = useTranslation('common');
12
+ const [hasTopic, saveToTopic] = useSessionStore((s) => [!!s.activeTopicId, s.saveToTopic]);
13
+ const { mobile } = useResponsive();
14
+
15
+ if (hasTopic) return undefined;
16
+
17
+ const Render = mobile ? ActionIcon : Button;
18
+ const icon: any = mobile ? LucideGalleryVerticalEnd : <Icon icon={LucideGalleryVerticalEnd} />;
19
+
20
+ return (
21
+ <Tooltip title={t('topic.saveCurrentMessages')}>
22
+ <Render icon={icon} onClick={saveToTopic} />
23
+ </Tooltip>
24
+ );
25
+ });
26
+
27
+ export default SaveTopic;
@@ -0,0 +1,35 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { useTheme } from 'antd-style';
3
+ import { ArrowBigUp, CornerDownLeft } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import SaveTopic from './SaveTopic';
9
+
10
+ const Footer = memo(() => {
11
+ const theme = useTheme();
12
+ const { t } = useTranslation('common');
13
+
14
+ return (
15
+ <>
16
+ <Flexbox
17
+ gap={4}
18
+ horizontal
19
+ style={{ color: theme.colorTextDescription, fontSize: 12, marginRight: 12 }}
20
+ >
21
+ <Icon icon={CornerDownLeft} />
22
+ <span>{t('send')}</span>
23
+ <span>/</span>
24
+ <Flexbox horizontal>
25
+ <Icon icon={ArrowBigUp} />
26
+ <Icon icon={CornerDownLeft} />
27
+ </Flexbox>
28
+ <span>{t('warp')}</span>
29
+ </Flexbox>
30
+ <SaveTopic />
31
+ </>
32
+ );
33
+ });
34
+
35
+ export default Footer;
@@ -0,0 +1,12 @@
1
+ import { ReactNode, memo } from 'react';
2
+
3
+ interface ChatInputMobileLayoutProps {
4
+ children: ReactNode;
5
+ expand?: boolean;
6
+ }
7
+
8
+ const ChatInputMobileLayout = memo<ChatInputMobileLayoutProps>(({ children }) => {
9
+ return <div>{children}</div>;
10
+ });
11
+
12
+ export default ChatInputMobileLayout;
@@ -1,64 +1,46 @@
1
- import { ChatInputArea, DraggablePanel } from '@lobehub/ui';
1
+ import { ChatInputArea } from '@lobehub/ui';
2
+ import { useResponsive } from 'antd-style';
2
3
  import { memo, useState } from 'react';
3
4
  import { useTranslation } from 'react-i18next';
4
5
 
5
- import { CHAT_TEXTAREA_HEIGHT, HEADER_HEIGHT } from '@/const/layoutTokens';
6
- import { useGlobalStore } from '@/store/global';
6
+ import { CHAT_TEXTAREA_HEIGHT } from '@/const/layoutTokens';
7
7
  import { useSessionStore } from '@/store/session';
8
8
 
9
- import InputActions from './Action';
10
- import ActionsRight from './ActionRight';
9
+ import ActionLeft from './ActionBar/ActionLeft';
10
+ import ActionsRight from './ActionBar/ActionRight';
11
+ import Token from './ActionBar/Token';
12
+ import Desktop from './Desktop';
11
13
  import Footer from './Footer';
12
- import Token from './Token';
14
+ import Mobile from './Mobile';
13
15
 
14
16
  const ChatInput = () => {
15
17
  const { t } = useTranslation('common');
16
18
  const [expand, setExpand] = useState<boolean>(false);
17
19
  const [message, setMessage] = useState('');
20
+ const { mobile } = useResponsive();
18
21
 
19
- const [inputHeight, updatePreference] = useGlobalStore((s) => [
20
- s.preference.inputHeight,
21
- s.updatePreference,
22
+ const [isLoading, sendMessage, stopGenerateMessage] = useSessionStore((s) => [
23
+ !!s.chatLoadingId,
24
+ s.sendMessage,
25
+ s.stopGenerateMessage,
22
26
  ]);
23
- const [isLoading, hasTopic, sendMessage, saveToTopic, stopGenerateMessage] = useSessionStore(
24
- (s) => [
25
- !!s.chatLoadingId,
26
- !!s.activeTopicId,
27
- s.sendMessage,
28
- s.saveToTopic,
29
- s.stopGenerateMessage,
30
- ],
31
- );
32
27
 
33
- return (
34
- <DraggablePanel
35
- expandable={false}
36
- fullscreen={expand}
37
- headerHeight={HEADER_HEIGHT}
38
- minHeight={CHAT_TEXTAREA_HEIGHT}
39
- onSizeChange={(_, size) => {
40
- if (!size) return;
28
+ const Render = mobile ? Mobile : Desktop;
41
29
 
42
- updatePreference({
43
- inputHeight: typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
44
- });
45
- }}
46
- placement="bottom"
47
- size={{ height: inputHeight, width: '100%' }}
48
- style={{ zIndex: 10 }}
49
- >
30
+ return (
31
+ <Render expand={expand}>
50
32
  <ChatInputArea
51
33
  actions={
52
34
  <>
53
- <InputActions />
35
+ <ActionLeft />
54
36
  <Token input={message} />
55
37
  </>
56
38
  }
57
39
  actionsRight={<ActionsRight />}
58
40
  expand={expand}
59
- footer={<Footer hasTopic={hasTopic} saveToTopic={saveToTopic} />}
41
+ footer={<Footer />}
60
42
  loading={isLoading}
61
- minHeight={CHAT_TEXTAREA_HEIGHT}
43
+ minHeight={mobile ? 0 : CHAT_TEXTAREA_HEIGHT}
62
44
  onExpandChange={setExpand}
63
45
  onInputChange={setMessage}
64
46
  onSend={sendMessage}
@@ -70,7 +52,7 @@ const ChatInput = () => {
70
52
  }}
71
53
  value={message}
72
54
  />
73
- </DraggablePanel>
55
+ </Render>
74
56
  );
75
57
  };
76
58
 
@@ -1,4 +1,4 @@
1
- import { ActionIcon, Logo, SearchBar } from '@lobehub/ui';
1
+ import { ActionIcon, Logo } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import { MessageSquarePlus } from 'lucide-react';
4
4
  import Link from 'next/link';
@@ -8,6 +8,8 @@ import { Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import { useSessionStore } from '@/store/session';
10
10
 
11
+ import SessionSearchBar from '../../SessionSearchBar';
12
+
11
13
  export const useStyles = createStyles(({ css, token }) => ({
12
14
  logo: css`
13
15
  fill: ${token.colorText};
@@ -21,7 +23,7 @@ export const useStyles = createStyles(({ css, token }) => ({
21
23
  const Header = memo(() => {
22
24
  const { styles } = useStyles();
23
25
  const { t } = useTranslation('common');
24
- const [keywords, createSession] = useSessionStore((s) => [s.searchKeywords, s.createSession]);
26
+ const [createSession] = useSessionStore((s) => [s.createSession]);
25
27
 
26
28
  return (
27
29
  <Flexbox className={styles.top} gap={16} padding={16}>
@@ -31,22 +33,13 @@ const Header = memo(() => {
31
33
  </Link>
32
34
  <ActionIcon
33
35
  icon={MessageSquarePlus}
34
- onClick={createSession}
36
+ onClick={() => createSession()}
35
37
  size={{ fontSize: 24 }}
36
38
  style={{ flex: 'none' }}
37
39
  title={t('newAgent')}
38
40
  />
39
41
  </Flexbox>
40
- <SearchBar
41
- allowClear
42
- enableShortKey
43
- onChange={(e) => useSessionStore.setState({ searchKeywords: e.target.value })}
44
- placeholder={t('searchAgentPlaceholder')}
45
- shortKey={'k'}
46
- spotlight
47
- type={'ghost'}
48
- value={keywords}
49
- />
42
+ <SessionSearchBar />
50
43
  </Flexbox>
51
44
  );
52
45
  });
@@ -26,7 +26,7 @@ const Header = memo(() => {
26
26
  left={<AvatarWithUpload size={28} style={{ marginLeft: 8 }} />}
27
27
  right={
28
28
  <>
29
- <ActionIcon icon={MessageSquarePlus} onClick={createSession} />
29
+ <ActionIcon icon={MessageSquarePlus} onClick={() => createSession()} />
30
30
  <ActionIcon
31
31
  icon={Settings2}
32
32
  onClick={() => {
@@ -12,7 +12,7 @@ const AddButton = memo<ButtonProps>((props) => {
12
12
  const createSession = useSessionStore((s) => s.createSession);
13
13
  return (
14
14
  <Flexbox style={{ margin: '12px 16px' }}>
15
- <Button block icon={<Icon icon={Plus} />} onClick={createSession} {...props}>
15
+ <Button block icon={<Icon icon={Plus} />} onClick={() => createSession()} {...props}>
16
16
  {t('newAgent')}
17
17
  </Button>
18
18
  </Flexbox>
@@ -1,7 +1,7 @@
1
1
  import { SiOpenai } from '@icons-pack/react-simple-icons';
2
2
  import { Avatar, List, Tag } from '@lobehub/ui';
3
3
  import { useHover } from 'ahooks';
4
- import { createStyles } from 'antd-style';
4
+ import { createStyles, useResponsive } from 'antd-style';
5
5
  import { memo, useMemo, useRef, useState } from 'react';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
  import { shallow } from 'zustand/shallow';
@@ -34,7 +34,7 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
34
34
  const [open, setOpen] = useState(false);
35
35
  const ref = useRef(null);
36
36
  const isHovering = useHover(ref);
37
-
37
+ const { mobile } = useResponsive();
38
38
  const { styles } = useStyles();
39
39
  const [defaultModel] = useGlobalStore((s) => [globalSelectors.defaultAgentConfig(s).model]);
40
40
 
@@ -98,7 +98,8 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
98
98
  return (
99
99
  <Item
100
100
  actions={actions}
101
- active={active}
101
+ // needn't active state in mobile
102
+ active={mobile ? false : active}
102
103
  addon={addon}
103
104
  avatar={avatarRender}
104
105
  className={styles.container}