@lobehub/chat 0.41.1 → 0.42.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 (60) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/locales/en_US/error.json +1 -0
  3. package/locales/en_US/plugin.json +5 -0
  4. package/locales/en_US/welcome.json +14 -0
  5. package/locales/zh_CN/error.json +1 -0
  6. package/locales/zh_CN/plugin.json +5 -0
  7. package/locales/zh_CN/welcome.json +14 -0
  8. package/package.json +1 -1
  9. package/src/components/DiscordIcon/index.tsx +15 -0
  10. package/src/const/url.ts +7 -0
  11. package/src/features/AvatarWithUpload/index.tsx +1 -2
  12. package/src/features/FolderPanel/index.tsx +4 -5
  13. package/src/features/SideBar/BottomActions.tsx +7 -6
  14. package/src/features/SideBar/TopActions.tsx +1 -1
  15. package/src/features/SideBar/index.tsx +1 -2
  16. package/src/layout/AppLayout.tsx +15 -0
  17. package/src/layout/index.tsx +6 -6
  18. package/src/locales/default/common.ts +1 -0
  19. package/src/locales/default/welcome.ts +14 -0
  20. package/src/locales/resources/zh_CN.ts +2 -0
  21. package/src/pages/chat/SessionList/Header.tsx +2 -6
  22. package/src/pages/chat/SessionList/List/SessionItem.tsx +2 -2
  23. package/src/pages/chat/SessionList/List/index.tsx +2 -6
  24. package/src/pages/chat/[id]/Conversation/ChatList/Error/ApiKeyForm.tsx +1 -5
  25. package/src/pages/chat/[id]/Conversation/ChatList/Error/InvalidAccess.tsx +2 -6
  26. package/src/pages/chat/[id]/Conversation/ChatList/Error/OpenAiBizError.tsx +1 -5
  27. package/src/pages/chat/[id]/Conversation/ChatList/MessageExtra.tsx +1 -2
  28. package/src/pages/chat/[id]/Conversation/ChatList/index.tsx +7 -11
  29. package/src/pages/chat/[id]/Conversation/Input/Action.tsx +1 -2
  30. package/src/pages/chat/[id]/Conversation/Input/ActionRight.tsx +1 -2
  31. package/src/pages/chat/[id]/Conversation/Input/Token.tsx +6 -10
  32. package/src/pages/chat/[id]/Conversation/Input/index.tsx +6 -6
  33. package/src/pages/chat/[id]/Header/index.tsx +13 -17
  34. package/src/pages/chat/[id]/Sidebar/Inner.tsx +45 -28
  35. package/src/pages/chat/[id]/Sidebar/Topic/TopicContent.tsx +5 -5
  36. package/src/pages/chat/[id]/Sidebar/Topic/TopicItem.tsx +1 -2
  37. package/src/pages/chat/[id]/Sidebar/Topic/index.tsx +2 -6
  38. package/src/pages/chat/[id]/Sidebar/index.tsx +4 -5
  39. package/src/pages/chat/[id]/index.page.tsx +5 -6
  40. package/src/pages/chat/{layout.tsx → [id]/layout.tsx} +4 -12
  41. package/src/pages/chat/[id]/setting/index.page.tsx +10 -14
  42. package/src/pages/chat/index.page.tsx +1 -24
  43. package/src/pages/index.page.tsx +33 -1
  44. package/src/pages/settings/Settings/Agent.tsx +5 -5
  45. package/src/pages/settings/Settings/Common.tsx +5 -9
  46. package/src/pages/settings/ThemeSwatches/ThemeSwatchesNeutral.tsx +1 -5
  47. package/src/pages/settings/ThemeSwatches/ThemeSwatchesPrimary.tsx +1 -5
  48. package/src/pages/welcome/Banner/AgentCard.tsx +71 -0
  49. package/src/pages/welcome/Banner/AgentTemplate.tsx +43 -0
  50. package/src/pages/welcome/Banner/Hero.tsx +39 -0
  51. package/src/pages/welcome/Banner/index.tsx +56 -0
  52. package/src/pages/welcome/Footer/index.tsx +24 -0
  53. package/src/pages/welcome/index.page.tsx +28 -0
  54. package/src/pages/welcome/layout.tsx +31 -0
  55. package/src/pages/welcome/style.ts +63 -0
  56. package/src/store/session/slices/session/selectors/index.ts +2 -0
  57. package/src/store/session/slices/session/selectors/list.ts +5 -0
  58. package/src/store/session/store.ts +2 -2
  59. package/src/store/settings/hooks/useHydrated.ts +20 -0
  60. package/src/store/settings/store.ts +2 -2
@@ -6,7 +6,6 @@ import Router from 'next/router';
6
6
  import { memo } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { Flexbox } from 'react-layout-kit';
9
- import { shallow } from 'zustand/shallow';
10
9
 
11
10
  import HeaderTitle from '@/components/HeaderTitle';
12
11
  import Tag from '@/components/Tag';
@@ -19,23 +18,20 @@ const Header = memo(() => {
19
18
  const init = useSessionHydrated();
20
19
  const { t } = useTranslation('common');
21
20
 
22
- const [title, description, avatar, backgroundColor, id, model, plugins] = useSessionStore(
23
- (s) => [
24
- agentSelectors.currentAgentTitle(s),
25
- agentSelectors.currentAgentDescription(s),
26
- agentSelectors.currentAgentAvatar(s),
27
- agentSelectors.currentAgentBackgroundColor(s),
28
- s.activeId,
29
- agentSelectors.currentAgentModel(s),
30
- agentSelectors.currentAgentPlugins(s),
31
- ],
32
- shallow,
33
- );
21
+ const [title, description, avatar, backgroundColor, id, model, plugins] = useSessionStore((s) => [
22
+ agentSelectors.currentAgentTitle(s),
23
+ agentSelectors.currentAgentDescription(s),
24
+ agentSelectors.currentAgentAvatar(s),
25
+ agentSelectors.currentAgentBackgroundColor(s),
26
+ s.activeId,
27
+ agentSelectors.currentAgentModel(s),
28
+ agentSelectors.currentAgentPlugins(s),
29
+ ]);
34
30
 
35
- const [showAgentSettings, toggleConfig] = useSettings(
36
- (s) => [s.showAgentConfig, s.toggleAgentPanel],
37
- shallow,
38
- );
31
+ const [showAgentSettings, toggleConfig] = useSettings((s) => [
32
+ s.showAgentConfig,
33
+ s.toggleAgentPanel,
34
+ ]);
39
35
 
40
36
  return (
41
37
  <ChatHeader
@@ -5,7 +5,6 @@ import { Maximize2Icon } from 'lucide-react';
5
5
  import { memo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
- import { shallow } from 'zustand/shallow';
9
8
 
10
9
  import { agentSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
11
10
 
@@ -29,22 +28,37 @@ const useStyles = createStyles(({ css, token }) => ({
29
28
  opacity: 1;
30
29
  }
31
30
  `,
31
+ promptBox: css`
32
+ position: relative;
33
+ overflow: hidden;
34
+ border-bottom: 1px solid ${token.colorBorder};
35
+ `,
36
+ promptMask: css`
37
+ pointer-events: none;
38
+
39
+ position: absolute;
40
+ z-index: 10;
41
+ bottom: 0;
42
+ left: 0;
43
+
44
+ width: 100%;
45
+ height: 32px;
46
+
47
+ background: linear-gradient(to bottom, transparent, ${token.colorBgLayout});
48
+ `,
32
49
  title: css`
33
50
  font-size: ${token.fontSizeHeading4}px;
34
51
  font-weight: bold;
35
52
  `,
36
- topic: css`
37
- border-top: 1px solid ${token.colorBorder};
38
- `,
39
53
  }));
40
54
 
41
55
  const Inner = memo(() => {
42
56
  const [openModal, setOpenModal] = useState(false);
43
57
  const { styles } = useStyles();
44
- const [systemRole, updateAgentConfig] = useSessionStore(
45
- (s) => [agentSelectors.currentAgentSystemRole(s), s.updateAgentConfig],
46
- shallow,
47
- );
58
+ const [systemRole, updateAgentConfig] = useSessionStore((s) => [
59
+ agentSelectors.currentAgentSystemRole(s),
60
+ s.updateAgentConfig,
61
+ ]);
48
62
 
49
63
  const hydrated = useSessionHydrated();
50
64
  const { t } = useTranslation('common');
@@ -61,33 +75,36 @@ const Inner = memo(() => {
61
75
  }
62
76
  title={t('settingAgent.prompt.title', { ns: 'setting' })}
63
77
  />
64
- <Flexbox height={200} padding={'0 16px 16px'}>
78
+ <Flexbox className={styles.promptBox} height={200} padding={'0 16px 16px'}>
65
79
  {hydrated ? (
66
- <EditableMessage
67
- classNames={{ markdown: styles.prompt }}
68
- onChange={(e) => {
69
- updateAgentConfig({ systemRole: e });
70
- }}
71
- onOpenChange={setOpenModal}
72
- openModal={openModal}
73
- placeholder={`${t('settingAgent.prompt.placeholder', { ns: 'setting' })}...`}
74
- styles={{ markdown: systemRole ? {} : { opacity: 0.5 } }}
75
- text={{
76
- cancel: t('cancel'),
77
- confirm: t('ok'),
78
- edit: t('edit'),
79
- title: t('settingAgent.prompt.title', { ns: 'setting' }),
80
- }}
81
- value={systemRole}
82
- />
80
+ <>
81
+ <EditableMessage
82
+ classNames={{ markdown: styles.prompt }}
83
+ onChange={(e) => {
84
+ updateAgentConfig({ systemRole: e });
85
+ }}
86
+ onOpenChange={setOpenModal}
87
+ openModal={openModal}
88
+ placeholder={`${t('settingAgent.prompt.placeholder', { ns: 'setting' })}...`}
89
+ styles={{ markdown: systemRole ? {} : { opacity: 0.5 } }}
90
+ text={{
91
+ cancel: t('cancel'),
92
+ confirm: t('ok'),
93
+ edit: t('edit'),
94
+ title: t('settingAgent.prompt.title', { ns: 'setting' }),
95
+ }}
96
+ value={systemRole}
97
+ />
98
+ <div className={styles.promptMask} />
99
+ </>
83
100
  ) : (
84
101
  <Skeleton active avatar={false} style={{ marginTop: 12 }} title={false} />
85
102
  )}
86
103
  </Flexbox>
87
- <Flexbox className={styles.topic} gap={16} padding={16}>
104
+ <Flexbox gap={16} padding={16}>
88
105
  <SearchBar placeholder={t('topic.searchPlaceholder')} spotlight type={'ghost'} />
89
106
  {!hydrated ? (
90
- <Flexbox gap={8} style={{ marginTop: 12 }}>
107
+ <Flexbox gap={8} style={{ marginTop: 8 }}>
91
108
  {Array.from({ length: 8 }).map((_, i) => (
92
109
  <Skeleton
93
110
  active
@@ -5,7 +5,6 @@ import { MoreVertical, PencilLine, Star, Trash } from 'lucide-react';
5
5
  import { memo, useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
- import { shallow } from 'zustand/shallow';
9
8
 
10
9
  import { useSessionStore } from '@/store/session';
11
10
 
@@ -33,10 +32,11 @@ interface TopicContentProps {
33
32
  const TopicContent = memo<TopicContentProps>(({ id, title, fav }) => {
34
33
  const { t } = useTranslation('common');
35
34
 
36
- const [editing, dispatchTopic, removeTopic] = useSessionStore(
37
- (s) => [s.renameTopicId === id, s.dispatchTopic, s.removeTopic],
38
- shallow,
39
- );
35
+ const [editing, dispatchTopic, removeTopic] = useSessionStore((s) => [
36
+ s.renameTopicId === id,
37
+ s.dispatchTopic,
38
+ s.removeTopic,
39
+ ]);
40
40
  const { styles, theme } = useStyles();
41
41
 
42
42
  const toggleEditing = (visible?: boolean) => {
@@ -1,7 +1,6 @@
1
1
  import { createStyles } from 'antd-style';
2
2
  import { memo } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
- import { shallow } from 'zustand/shallow';
5
4
 
6
5
  import { useSessionStore } from '@/store/session';
7
6
 
@@ -50,7 +49,7 @@ export interface ConfigCellProps {
50
49
  const TopicItem = memo<ConfigCellProps>(({ title, active, id, fav }) => {
51
50
  const { styles, cx } = useStyles();
52
51
 
53
- const [toggleTopic] = useSessionStore((s) => [s.toggleTopic], shallow);
52
+ const [toggleTopic] = useSessionStore((s) => [s.toggleTopic]);
54
53
 
55
54
  return (
56
55
  <Flexbox
@@ -2,7 +2,6 @@ import { useThemeMode } from 'antd-style';
2
2
  import isEqual from 'fast-deep-equal';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Flexbox } from 'react-layout-kit';
5
- import { shallow } from 'zustand/shallow';
6
5
 
7
6
  import Empty from '@/components/Empty';
8
7
  import { topicSelectors, useSessionStore } from '@/store/session';
@@ -13,13 +12,10 @@ import TopicItem from './TopicItem';
13
12
  export const Topic = () => {
14
13
  const topics = useSessionStore(topicSelectors.currentTopics, isEqual);
15
14
  const { isDarkMode } = useThemeMode();
16
- const [activeTopicId] = useSessionStore((s) => [s.activeTopicId], shallow);
15
+ const [activeTopicId] = useSessionStore((s) => [s.activeTopicId]);
17
16
  const { t } = useTranslation('empty');
18
17
 
19
- const [visible, updateGuideState] = useSettings(
20
- (s) => [s.guide?.topic, s.updateGuideState],
21
- shallow,
22
- );
18
+ const [visible, updateGuideState] = useSettings((s) => [s.guide?.topic, s.updateGuideState]);
23
19
 
24
20
  return (
25
21
  <Flexbox gap={2}>
@@ -1,7 +1,6 @@
1
1
  import { DraggablePanel, DraggablePanelContainer } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import { rgba } from 'polished';
4
- import { shallow } from 'zustand/shallow';
5
4
 
6
5
  import HeaderSpacing from '@/components/HeaderSpacing';
7
6
  import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
@@ -23,10 +22,10 @@ const useStyles = createStyles(({ cx, css, token, stylish }) => ({
23
22
 
24
23
  const Config = () => {
25
24
  const { styles } = useStyles();
26
- const [showAgentSettings, toggleConfig] = useSettings(
27
- (s) => [s.showAgentConfig, s.toggleAgentPanel],
28
- shallow,
29
- );
25
+ const [showAgentSettings, toggleConfig] = useSettings((s) => [
26
+ s.showAgentConfig,
27
+ s.toggleAgentPanel,
28
+ ]);
30
29
 
31
30
  return (
32
31
  <DraggablePanel
@@ -1,4 +1,3 @@
1
- import isEqual from 'fast-deep-equal';
2
1
  import Head from 'next/head';
3
2
  import { memo } from 'react';
4
3
  import { Flexbox } from 'react-layout-kit';
@@ -6,16 +5,16 @@ import { Flexbox } from 'react-layout-kit';
6
5
  import { agentSelectors, useSessionStore } from '@/store/session';
7
6
  import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
8
7
 
9
- import Layout from '../layout';
10
8
  import Conversation from './Conversation';
11
9
  import Header from './Header';
12
10
  import Config from './Sidebar';
11
+ import Layout from './layout';
13
12
 
14
13
  const Chat = memo(() => {
15
- const [avatar, title] = useSessionStore(
16
- (s) => [agentSelectors.currentAgentAvatar(s), agentSelectors.currentAgentTitle(s)],
17
- isEqual,
18
- );
14
+ const [avatar, title] = useSessionStore((s) => [
15
+ agentSelectors.currentAgentAvatar(s),
16
+ agentSelectors.currentAgentTitle(s),
17
+ ]);
19
18
 
20
19
  const pageTitle = genSiteHeadTitle([avatar, title].filter(Boolean).join(' '));
21
20
 
@@ -3,20 +3,13 @@ import { PropsWithChildren, memo, useEffect } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
  import { shallow } from 'zustand/shallow';
5
5
 
6
- import SideBar from '@/features/SideBar';
7
- import { createI18nNext } from '@/locales/create';
6
+ import AppLayout from '@/layout/AppLayout';
8
7
  import { useSessionStore } from '@/store/session';
9
8
  import { useSettings } from '@/store/settings';
10
9
 
11
- import { Sessions } from './SessionList';
12
-
13
- const initI18n = createI18nNext();
10
+ import { Sessions } from '../SessionList';
14
11
 
15
12
  const ChatLayout = memo<PropsWithChildren>(({ children }) => {
16
- useEffect(() => {
17
- initI18n.finally();
18
- }, []);
19
-
20
13
  const [activeSession, toggleTopic] = useSessionStore((s) => {
21
14
  return [s.activeSession, s.toggleTopic];
22
15
  }, shallow);
@@ -46,13 +39,12 @@ const ChatLayout = memo<PropsWithChildren>(({ children }) => {
46
39
  }, []);
47
40
 
48
41
  return (
49
- <Flexbox horizontal width={'100%'}>
50
- <SideBar />
42
+ <AppLayout>
51
43
  <Sessions />
52
44
  <Flexbox flex={1} height={'100vh'} style={{ position: 'relative' }}>
53
45
  {children}
54
46
  </Flexbox>
55
- </Flexbox>
47
+ </AppLayout>
56
48
  );
57
49
  });
58
50
 
@@ -3,31 +3,27 @@ import Head from 'next/head';
3
3
  import { memo } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { Flexbox } from 'react-layout-kit';
6
- import { shallow } from 'zustand/shallow';
7
6
 
8
7
  import HeaderSpacing from '@/components/HeaderSpacing';
9
8
  import { HEADER_HEIGHT } from '@/const/layoutTokens';
10
9
  import { AgentConfig, AgentMeta, AgentPlugin, AgentPrompt } from '@/features/AgentSetting';
10
+ import AppLayout from '@/layout/AppLayout';
11
11
  import { agentSelectors, useSessionStore } from '@/store/session';
12
12
  import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
13
13
 
14
- import ChatLayout from '../../layout';
15
14
  import Header from './Header';
16
15
 
17
16
  const EditPage = memo(() => {
18
17
  const { t } = useTranslation('setting');
19
18
  const config = useSessionStore(agentSelectors.currentAgentConfig, isEqual);
20
19
  const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
21
- const autocomplete = useSessionStore(agentSelectors.currentAutocomplete, shallow);
22
- const [updateAgentConfig, toggleAgentPlugin, updateAgentMeta, title] = useSessionStore(
23
- (s) => [
24
- s.updateAgentConfig,
25
- s.toggleAgentPlugin,
26
- s.updateAgentMeta,
27
- agentSelectors.currentAgentTitle(s),
28
- ],
29
- shallow,
30
- );
20
+ const autocomplete = useSessionStore(agentSelectors.currentAutocomplete);
21
+ const [updateAgentConfig, toggleAgentPlugin, updateAgentMeta, title] = useSessionStore((s) => [
22
+ s.updateAgentConfig,
23
+ s.toggleAgentPlugin,
24
+ s.updateAgentMeta,
25
+ agentSelectors.currentAgentTitle(s),
26
+ ]);
31
27
 
32
28
  const pageTitle = genSiteHeadTitle(t('header.sessionWithName', { name: title }));
33
29
 
@@ -36,7 +32,7 @@ const EditPage = memo(() => {
36
32
  <Head>
37
33
  <title>{pageTitle}</title>
38
34
  </Head>
39
- <ChatLayout>
35
+ <AppLayout>
40
36
  <Header />
41
37
  <Flexbox align={'center'} flex={1} gap={16} padding={24} style={{ overflow: 'auto' }}>
42
38
  <HeaderSpacing height={HEADER_HEIGHT - 16} />
@@ -50,7 +46,7 @@ const EditPage = memo(() => {
50
46
  <AgentConfig config={config} updateConfig={updateAgentConfig} />
51
47
  <AgentPlugin config={config} updateConfig={toggleAgentPlugin} />
52
48
  </Flexbox>
53
- </ChatLayout>
49
+ </AppLayout>
54
50
  </>
55
51
  );
56
52
  });
@@ -1,24 +1 @@
1
- import Router from 'next/router';
2
- import { useEffect } from 'react';
3
-
4
- import { sessionSelectors, useSessionStore } from '@/store/session';
5
-
6
- import Chat from './[id]/index.page';
7
-
8
- export default () => {
9
- // 支持用户在进到首页时,自动激活列表中的第一个角色
10
- useEffect(() => {
11
- const hasRehydrated = useSessionStore.persist.hasHydrated();
12
- // 只有当水合完毕后,才往下走
13
- if (!hasRehydrated) return;
14
-
15
- // 如果当前有会话,那么就激活第一个会话
16
- const list = sessionSelectors.sessionList(useSessionStore.getState());
17
- if (list.length > 0) {
18
- const sessionId = list[0].id;
19
- Router.push(`/chat/${sessionId}`);
20
- }
21
- }, []);
22
-
23
- return <Chat />;
24
- };
1
+ export { default } from './[id]/index.page';
@@ -1 +1,33 @@
1
- export { default } from './chat/index.page';
1
+ import { Icon } from '@lobehub/ui';
2
+ import { useWhyDidYouUpdate } from 'ahooks';
3
+ import { Loader2 } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Center } from 'react-layout-kit';
7
+
8
+ import { sessionSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
9
+
10
+ import Chat from './chat/index.page';
11
+ import Welcome from './welcome/index.page';
12
+
13
+ const Loading = memo(() => {
14
+ const { t } = useTranslation('common');
15
+
16
+ return (
17
+ <Center gap={12} height={'100vh'} width={'100%'}>
18
+ <Icon icon={Loader2} size={{ fontSize: 64 }} spin />
19
+ {t('appInitializing')}
20
+ </Center>
21
+ );
22
+ });
23
+
24
+ export default memo(() => {
25
+ const hydrated = useSessionHydrated();
26
+ const hasSession = useSessionStore(sessionSelectors.hasSessionList);
27
+
28
+ useWhyDidYouUpdate('index.page', { hasSession, hydrated });
29
+
30
+ if (!hydrated) return <Loading />;
31
+
32
+ return !hasSession ? <Welcome /> : <Chat />;
33
+ });
@@ -1,6 +1,5 @@
1
1
  import isEqual from 'fast-deep-equal';
2
2
  import { memo } from 'react';
3
- import { shallow } from 'zustand/shallow';
4
3
 
5
4
  import { AgentConfig, AgentMeta, AgentPlugin, AgentPrompt } from '@/features/AgentSetting';
6
5
  import { settingsSelectors, useSettings } from '@/store/settings';
@@ -8,10 +7,11 @@ import { settingsSelectors, useSettings } from '@/store/settings';
8
7
  const Agent = memo(() => {
9
8
  const config = useSettings(settingsSelectors.currentAgentConfig, isEqual);
10
9
  const meta = useSettings(settingsSelectors.currentAgentMeta, isEqual);
11
- const [setAgentConfig, setAgentMeta, toggleAgentPlugin] = useSettings(
12
- (s) => [s.setAgentConfig, s.setAgentMeta, s.toggleAgentPlugin],
13
- shallow,
14
- );
10
+ const [setAgentConfig, setAgentMeta, toggleAgentPlugin] = useSettings((s) => [
11
+ s.setAgentConfig,
12
+ s.setAgentMeta,
13
+ s.toggleAgentPlugin,
14
+ ]);
15
15
  return (
16
16
  <>
17
17
  <AgentPrompt config={config} updateConfig={setAgentConfig} />
@@ -6,7 +6,6 @@ import { AppWindow, Palette, Webhook } from 'lucide-react';
6
6
  import { memo, useCallback, useMemo } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import SliderWithInput from 'src/components/SliderWithInput';
9
- import { shallow } from 'zustand/shallow';
10
9
 
11
10
  import { FORM_STYLE } from '@/const/layoutTokens';
12
11
  import { DEFAULT_SETTINGS } from '@/const/settings';
@@ -29,14 +28,11 @@ const Common = memo(() => {
29
28
  const [form] = AntForm.useForm();
30
29
  const clearSessions = useSessionStore((s) => s.clearSessions);
31
30
  const settings = useSettings(settingsSelectors.currentSettings, isEqual);
32
- const { setThemeMode, setSettings, resetSettings } = useSettings(
33
- (s) => ({
34
- resetSettings: s.resetSettings,
35
- setSettings: s.setSettings,
36
- setThemeMode: s.setThemeMode,
37
- }),
38
- shallow,
39
- );
31
+ const [setThemeMode, setSettings, resetSettings] = useSettings((s) => [
32
+ s.setThemeMode,
33
+ s.setSettings,
34
+ s.resetSettings,
35
+ ]);
40
36
 
41
37
  const { message, modal } = App.useApp();
42
38
  const handleReset = useCallback(() => {
@@ -6,15 +6,11 @@ import {
6
6
  neutralColorsSwatches,
7
7
  } from '@lobehub/ui';
8
8
  import { memo } from 'react';
9
- import { shallow } from 'zustand/shallow';
10
9
 
11
10
  import { useSettings } from '@/store/settings';
12
11
 
13
12
  const ThemeSwatchesNeutral = memo(() => {
14
- const { neutralColor, setSettings } = useSettings(
15
- (s) => ({ neutralColor: s.settings.neutralColor, setSettings: s.setSettings }),
16
- shallow,
17
- );
13
+ const [neutralColor, setSettings] = useSettings((s) => [s.settings.neutralColor, s.setSettings]);
18
14
 
19
15
  const handleSelect = (v: any) => {
20
16
  const name = findCustomThemeName('neutral', v) as NeutralColors;
@@ -6,15 +6,11 @@ import {
6
6
  primaryColorsSwatches,
7
7
  } from '@lobehub/ui';
8
8
  import { memo } from 'react';
9
- import { shallow } from 'zustand/shallow';
10
9
 
11
10
  import { useSettings } from '@/store/settings';
12
11
 
13
12
  const ThemeSwatchesPrimary = memo(() => {
14
- const { primaryColor, setSettings } = useSettings(
15
- (s) => ({ primaryColor: s.settings.primaryColor, setSettings: s.setSettings }),
16
- shallow,
17
- );
13
+ const [primaryColor, setSettings] = useSettings((s) => [s.settings.primaryColor, s.setSettings]);
18
14
 
19
15
  const handleSelect = (v: any) => {
20
16
  const name = findCustomThemeName('primary', v) as PrimaryColors;
@@ -0,0 +1,71 @@
1
+ import { Avatar, Spotlight } from '@lobehub/ui';
2
+ import { Typography } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import { rgba } from 'polished';
5
+ import { memo } from 'react';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { LobeAgentSession } from '@/types/session';
9
+
10
+ const { Paragraph, Title } = Typography;
11
+
12
+ const useStyles = createStyles(({ css, token, cx, stylish }) => ({
13
+ container: cx(
14
+ stylish.blur,
15
+ css`
16
+ cursor: pointer;
17
+
18
+ position: relative;
19
+
20
+ overflow: hidden;
21
+ flex: 1;
22
+
23
+ padding: 16px;
24
+
25
+ background-color: ${rgba(token.colorBgContainer, 0.5)};
26
+ border: 1px solid ${rgba(token.colorText, 0.2)};
27
+ border-radius: ${token.borderRadiusLG}px;
28
+
29
+ transition: all 400ms ${token.motionEaseOut};
30
+
31
+ &:hover {
32
+ background-color: ${rgba(token.colorBgElevated, 0.2)};
33
+ border-color: ${token.colorText};
34
+ box-shadow: 0 0 0 1px ${token.colorText};
35
+ }
36
+
37
+ ,
38
+ &:active {
39
+ scale: 0.98;
40
+ }
41
+ `,
42
+ ),
43
+ desc: css`
44
+ margin: 0 !important;
45
+ `,
46
+ title: css`
47
+ margin: 0 !important;
48
+ `,
49
+ }));
50
+
51
+ export interface AgentCardProps {
52
+ meta: LobeAgentSession['meta'];
53
+ }
54
+
55
+ const AgentCard = memo<AgentCardProps>(({ meta }) => {
56
+ const { styles } = useStyles();
57
+ return (
58
+ <Flexbox className={styles.container} gap={8}>
59
+ <Spotlight size={200} />
60
+ <Avatar avatar={meta.avatar} background={meta.backgroundColor} />
61
+ <Title className={styles.title} ellipsis level={5}>
62
+ {meta.title}
63
+ </Title>
64
+ <Paragraph className={styles.desc} ellipsis={{ rows: 2 }} type="secondary">
65
+ {meta.description}
66
+ </Paragraph>
67
+ </Flexbox>
68
+ );
69
+ });
70
+
71
+ export default AgentCard;
@@ -0,0 +1,43 @@
1
+ import { useResponsive } from 'antd-style';
2
+ import { memo } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import { useStyles } from '../style';
6
+ import AgentCard, { type AgentCardProps } from './AgentCard';
7
+
8
+ const items: AgentCardProps['meta'][] = [
9
+ {
10
+ avatar: '😀',
11
+ description: 'dddddd',
12
+ title: 'Title',
13
+ },
14
+ {
15
+ avatar: '😀',
16
+ description: 'dddddd',
17
+ title: 'Title',
18
+ },
19
+ {
20
+ avatar: '😀',
21
+ description: 'dddddd',
22
+ title: 'Title',
23
+ },
24
+ ];
25
+
26
+ const AgentTemplate = memo<{ width: number }>(({ width }) => {
27
+ const { styles } = useStyles();
28
+ const { mobile } = useResponsive();
29
+ return (
30
+ <Flexbox
31
+ className={styles.templateContainer}
32
+ gap={16}
33
+ horizontal={!mobile}
34
+ style={{ marginTop: -width / 20 }}
35
+ >
36
+ {items.map((meta, index) => (
37
+ <AgentCard key={index} meta={meta} />
38
+ ))}
39
+ </Flexbox>
40
+ );
41
+ });
42
+
43
+ export default AgentTemplate;