@lobehub/chat 0.74.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 (99) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +26 -5
  3. package/README.zh-CN.md +25 -4
  4. package/locales/en_US/market.json +12 -0
  5. package/locales/en_US/plugin.json +11 -2
  6. package/locales/en_US/setting.json +3 -1
  7. package/locales/ru_RU/market.json +12 -0
  8. package/locales/ru_RU/plugin.json +44 -49
  9. package/locales/ru_RU/setting.json +3 -1
  10. package/locales/zh_CN/common.json +1 -1
  11. package/locales/zh_CN/market.json +12 -0
  12. package/locales/zh_CN/plugin.json +19 -5
  13. package/locales/zh_CN/setting.json +62 -14
  14. package/package.json +5 -4
  15. package/scripts/i18nWorkflow/const.ts +19 -0
  16. package/scripts/i18nWorkflow/genDefaultLocale.ts +19 -0
  17. package/scripts/i18nWorkflow/genDiff.ts +50 -0
  18. package/scripts/i18nWorkflow/genResources.ts +26 -0
  19. package/scripts/i18nWorkflow/index.ts +15 -0
  20. package/scripts/i18nWorkflow/utils.ts +85 -0
  21. package/src/const/layoutTokens.ts +3 -1
  22. package/src/const/market.ts +15 -0
  23. package/src/const/url.ts +29 -4
  24. package/src/features/AgentSetting/AgentConfig/index.tsx +1 -2
  25. package/src/features/AgentSetting/AgentPlugin/MarketSettingModal.tsx +1 -1
  26. package/src/features/AgentSetting/store/initialState.ts +1 -1
  27. package/src/features/MobileTabBar/index.tsx +2 -2
  28. package/src/features/SideBar/TopActions.tsx +2 -2
  29. package/src/locales/default/market.ts +12 -0
  30. package/src/locales/resources/en_US.ts +2 -0
  31. package/src/locales/resources/ru_RU.ts +2 -0
  32. package/src/locales/resources/zh_CN.ts +2 -0
  33. package/src/pages/chat/features/Conversation/Input/{Action.tsx → ActionBar/ActionLeft.tsx} +2 -2
  34. package/src/pages/chat/features/Conversation/Input/ActionBar/ActionRight.tsx +36 -0
  35. package/src/pages/chat/features/Conversation/Input/Desktop.tsx +40 -0
  36. package/src/pages/chat/features/Conversation/Input/Footer/SaveTopic.tsx +27 -0
  37. package/src/pages/chat/features/Conversation/Input/Footer/index.tsx +35 -0
  38. package/src/pages/chat/features/Conversation/Input/Mobile.tsx +12 -0
  39. package/src/pages/chat/features/Conversation/Input/index.tsx +20 -38
  40. package/src/pages/chat/features/SessionList/Header/Desktop.tsx +6 -13
  41. package/src/pages/chat/features/SessionList/Header/Mobile.tsx +1 -1
  42. package/src/pages/chat/features/SessionList/List/AddButton.tsx +1 -1
  43. package/src/pages/chat/features/SessionList/List/Item/index.tsx +4 -3
  44. package/src/pages/chat/features/SessionList/List/index.tsx +1 -3
  45. package/src/pages/chat/features/SessionList/Mobile.tsx +2 -12
  46. package/src/pages/chat/features/SessionSearchBar/index.tsx +26 -0
  47. package/src/pages/chat/features/Sidebar/Topic/index.tsx +18 -6
  48. package/src/pages/chat/features/Sidebar/TopicSearchBar/index.tsx +24 -0
  49. package/src/pages/chat/features/Sidebar/index.tsx +2 -9
  50. package/src/pages/market/features/AgentCard/AgentCardBanner.tsx +64 -0
  51. package/src/pages/market/features/AgentCard/AgentCardItem.tsx +57 -0
  52. package/src/pages/market/features/AgentCard/Loading.tsx +13 -0
  53. package/src/pages/market/features/AgentCard/index.tsx +37 -0
  54. package/src/pages/market/features/AgentCard/style.ts +22 -0
  55. package/src/pages/market/features/AgentSearchBar/index.tsx +26 -0
  56. package/src/pages/market/features/Header/Desktop.tsx +15 -4
  57. package/src/pages/market/features/Header/Mobile.tsx +7 -24
  58. package/src/pages/market/features/SideBar/AgentInfo/Comment.tsx +16 -0
  59. package/src/pages/market/features/SideBar/AgentInfo/Header.tsx +68 -0
  60. package/src/pages/market/features/SideBar/AgentInfo/Loading.tsx +61 -0
  61. package/src/pages/market/features/SideBar/AgentInfo/TokenTag.tsx +25 -0
  62. package/src/pages/market/features/SideBar/AgentInfo/index.tsx +68 -0
  63. package/src/pages/market/features/SideBar/AgentInfo/style.ts +46 -0
  64. package/src/pages/market/features/SideBar/Desktop.tsx +70 -0
  65. package/src/pages/market/features/SideBar/Mobile.tsx +36 -0
  66. package/src/pages/market/features/SideBar/index.tsx +23 -0
  67. package/src/pages/market/features/TagList/index.tsx +29 -0
  68. package/src/pages/market/index.page.tsx +11 -2
  69. package/src/pages/market/layout.mobile.tsx +8 -7
  70. package/src/pages/market/layout.tsx +39 -1
  71. package/src/pages/settings/features/Header/Desktop.tsx +2 -4
  72. package/src/pages/settings/features/Header/Mobile.tsx +1 -1
  73. package/src/pages/settings/features/Settings/Common.tsx +1 -2
  74. package/src/pages/settings/features/Settings/index.tsx +17 -4
  75. package/src/services/agentMarket.ts +21 -0
  76. package/src/store/global/initialState.ts +1 -2
  77. package/src/store/market/action.ts +57 -0
  78. package/src/store/market/index.ts +3 -0
  79. package/src/store/market/initialState.ts +17 -0
  80. package/src/store/market/selectors.ts +36 -0
  81. package/src/store/market/store.ts +51 -0
  82. package/src/store/session/slices/session/action.ts +5 -3
  83. package/src/store/session/slices/session/initialState.ts +2 -0
  84. package/src/store/session/slices/session/reducers/session.test.ts +4 -4
  85. package/src/types/market.ts +19 -0
  86. package/src/types/meta.ts +1 -1
  87. package/src/utils/filter.ts +1 -1
  88. package/src/utils/findDuplicates.ts +22 -0
  89. package/scripts/genDefaultLocale.ts +0 -18
  90. package/scripts/genResources.ts +0 -51
  91. package/scripts/toc.ts +0 -7
  92. package/src/components/Empty/index.tsx +0 -63
  93. package/src/components/Empty/style.ts +0 -31
  94. package/src/components/SelectWithImg/index.tsx +0 -97
  95. package/src/components/SelectWithImg/styles.ts +0 -43
  96. package/src/pages/chat/features/Conversation/Input/ActionRight.tsx +0 -31
  97. package/src/pages/chat/features/Conversation/Input/Footer.tsx +0 -49
  98. /package/src/components/{ManifestPreviewer.tsx → ManifestPreviewer/index.tsx} +0 -0
  99. /package/src/pages/chat/features/Conversation/Input/{Token.tsx → ActionBar/Token.tsx} +0 -0
@@ -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}
@@ -26,10 +26,8 @@ const SessionList = memo(() => {
26
26
  <Link
27
27
  href={SESSION_CHAT_URL(id, mobile)}
28
28
  key={id}
29
- // replace onClick with onPointerDown to prevent double click on mobile
30
- onPointerDown={(e) => {
29
+ onClick={(e) => {
31
30
  e.preventDefault();
32
-
33
31
  if (mobile) switchSession(id);
34
32
  else activeSession(id);
35
33
  }}
@@ -1,22 +1,12 @@
1
- import { SearchBar } from '@lobehub/ui';
2
1
  import { ReactNode, memo } from 'react';
3
- import { useTranslation } from 'react-i18next';
4
2
 
5
- import { useSessionStore } from '@/store/session';
3
+ import SessionSearchBar from '../SessionSearchBar';
6
4
 
7
5
  const Sessions = memo<{ children: ReactNode }>(({ children }) => {
8
- const { t } = useTranslation('common');
9
- const [keywords] = useSessionStore((s) => [s.searchKeywords, s.createSession]);
10
6
  return (
11
7
  <>
12
8
  <div style={{ padding: '8px 16px' }}>
13
- <SearchBar
14
- allowClear
15
- onChange={(e) => useSessionStore.setState({ searchKeywords: e.target.value })}
16
- placeholder={t('searchAgentPlaceholder')}
17
- type={'block'}
18
- value={keywords}
19
- />
9
+ <SessionSearchBar />
20
10
  </div>
21
11
  {children}
22
12
  </>
@@ -0,0 +1,26 @@
1
+ import { SearchBar } from '@lobehub/ui';
2
+ import { useResponsive } from 'antd-style';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { useSessionStore } from '@/store/session';
7
+
8
+ const SessionSearchBar = memo(() => {
9
+ const { t } = useTranslation('common');
10
+ const [keywords] = useSessionStore((s) => [s.searchKeywords]);
11
+ const { mobile } = useResponsive();
12
+ return (
13
+ <SearchBar
14
+ allowClear
15
+ enableShortKey={!mobile}
16
+ onChange={(e) => useSessionStore.setState({ searchKeywords: e.target.value })}
17
+ placeholder={t('searchAgentPlaceholder')}
18
+ shortKey={'k'}
19
+ spotlight={!mobile}
20
+ type={mobile ? 'block' : 'ghost'}
21
+ value={keywords}
22
+ />
23
+ );
24
+ });
25
+
26
+ export default SessionSearchBar;
@@ -1,9 +1,10 @@
1
+ import { EmptyCard } from '@lobehub/ui';
1
2
  import { useThemeMode } from 'antd-style';
2
3
  import isEqual from 'fast-deep-equal';
4
+ import { useMemo } from 'react';
3
5
  import { useTranslation } from 'react-i18next';
4
6
  import { Flexbox } from 'react-layout-kit';
5
7
 
6
- import Empty from '@/components/Empty';
7
8
  import { useGlobalStore } from '@/store/global';
8
9
  import { useSessionChatInit, useSessionStore } from '@/store/session';
9
10
  import { topicSelectors } from '@/store/session/selectors';
@@ -15,20 +16,27 @@ export const Topic = () => {
15
16
  const init = useSessionChatInit();
16
17
  const topics = useSessionStore(topicSelectors.currentTopics, isEqual);
17
18
  const { isDarkMode } = useThemeMode();
18
- const [activeTopicId] = useSessionStore((s) => [s.activeTopicId]);
19
+ const [activeTopicId, keywords] = useSessionStore((s) => [
20
+ s.activeTopicId,
21
+ s.topicSearchKeywords,
22
+ ]);
19
23
  const { t } = useTranslation('empty');
20
-
21
24
  const [visible, updateGuideState] = useGlobalStore((s) => [
22
25
  s.preference.guide?.topic,
23
26
  s.updateGuideState,
24
27
  ]);
25
28
 
29
+ const topicsData = useMemo(() => {
30
+ if (!keywords) return topics;
31
+ return topics.filter(({ title }) => title.toLowerCase().includes(keywords.toLowerCase()));
32
+ }, [topics, keywords]);
33
+
26
34
  return !init ? (
27
35
  <SkeletonList />
28
36
  ) : (
29
37
  <Flexbox gap={2} style={{ marginBottom: 12 }}>
30
38
  {topics?.length === 0 && (
31
- <Empty
39
+ <EmptyCard
32
40
  alt={t('topic.desc')}
33
41
  cover={`/images/empty_topic_${isDarkMode ? 'dark' : 'light'}.webp`}
34
42
  desc={t('topic.desc')}
@@ -43,9 +51,13 @@ export const Topic = () => {
43
51
  />
44
52
  )}
45
53
 
46
- <TopicItem active={!activeTopicId} fav={false} title={'默认话题'} />
54
+ <TopicItem
55
+ active={!activeTopicId}
56
+ fav={false}
57
+ title={t('topic.defaultTitle', { ns: 'common' })}
58
+ />
47
59
 
48
- {topics.map(({ id, favorite, title }) => (
60
+ {topicsData.map(({ id, favorite, title }) => (
49
61
  <TopicItem active={activeTopicId === id} fav={favorite} id={id} key={id} title={title} />
50
62
  ))}
51
63
  </Flexbox>
@@ -0,0 +1,24 @@
1
+ import { SearchBar } from '@lobehub/ui';
2
+ import { useResponsive } from 'antd-style';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { useSessionStore } from '@/store/session';
7
+
8
+ const TopicSearchBar = memo(() => {
9
+ const { t } = useTranslation('common');
10
+ const [keywords] = useSessionStore((s) => [s.topicSearchKeywords]);
11
+ const { mobile } = useResponsive();
12
+ return (
13
+ <SearchBar
14
+ allowClear
15
+ onChange={(e) => useSessionStore.setState({ topicSearchKeywords: e.target.value })}
16
+ placeholder={t('topic.searchPlaceholder')}
17
+ spotlight={!mobile}
18
+ type={mobile ? 'block' : 'ghost'}
19
+ value={keywords}
20
+ />
21
+ );
22
+ });
23
+
24
+ export default TopicSearchBar;
@@ -1,29 +1,22 @@
1
- import { SearchBar } from '@lobehub/ui';
2
1
  import { useResponsive } from 'antd-style';
3
2
  import { memo } from 'react';
4
- import { useTranslation } from 'react-i18next';
5
3
  import { Flexbox } from 'react-layout-kit';
6
4
 
7
5
  import Desktop from './Desktop';
8
6
  import Mobile from './Mobile';
9
7
  import { Topic } from './Topic';
8
+ import TopicSearchBar from './TopicSearchBar';
10
9
 
11
10
  const SideBar = memo(() => {
12
11
  const { mobile } = useResponsive();
13
12
 
14
13
  const Render = mobile ? Mobile : Desktop;
15
14
 
16
- const { t } = useTranslation('common');
17
-
18
15
  return (
19
16
  <Render>
20
17
  <Flexbox height={'100%'} style={{ overflow: 'hidden' }}>
21
18
  <Flexbox padding={16}>
22
- <SearchBar
23
- placeholder={t('topic.searchPlaceholder')}
24
- spotlight={!mobile}
25
- type={mobile ? 'block' : 'ghost'}
26
- />
19
+ <TopicSearchBar />
27
20
  </Flexbox>
28
21
  <Flexbox gap={16} paddingInline={16} style={{ overflowY: 'auto', position: 'relative' }}>
29
22
  <Topic />
@@ -0,0 +1,64 @@
1
+ import { Avatar, DivProps } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { memo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import { MetaData } from '@/types/meta';
7
+
8
+ export const useStyles = createStyles(({ css, token }, maskColor: string) => ({
9
+ banner: css`
10
+ position: relative;
11
+
12
+ overflow: hidden;
13
+
14
+ height: 64px;
15
+ margin-bottom: -56px;
16
+
17
+ background: ${token.colorFill};
18
+ `,
19
+ bannerImg: css`
20
+ filter: blur(6px) saturate(1.6);
21
+ `,
22
+ bannerMask: css`
23
+ 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});
31
+ `,
32
+ }));
33
+
34
+ interface AgentCardBannerProps extends DivProps {
35
+ mask?: boolean;
36
+ maskColor?: string;
37
+ meta: MetaData;
38
+ size?: number;
39
+ }
40
+
41
+ const AgentCardBanner = memo<AgentCardBannerProps>(
42
+ ({ meta, className, mask, size = 8, maskColor, ...props }) => {
43
+ const { styles, theme, cx } = useStyles(maskColor);
44
+
45
+ return (
46
+ <Flexbox
47
+ align={'center'}
48
+ className={cx(styles.banner, className)}
49
+ justify={'center'}
50
+ {...props}
51
+ >
52
+ <Avatar
53
+ avatar={meta.avatar}
54
+ background={meta.backgroundColor || theme.colorBgContainer}
55
+ className={styles.bannerImg}
56
+ style={{ transform: `scale(${size})` }}
57
+ />
58
+ {mask && <div className={styles.bannerMask} />}
59
+ </Flexbox>
60
+ );
61
+ },
62
+ );
63
+
64
+ export default AgentCardBanner;
@@ -0,0 +1,57 @@
1
+ import { Avatar, Tag } from '@lobehub/ui';
2
+ import { useHover } from 'ahooks';
3
+ import { Typography } from 'antd';
4
+ import { useThemeMode } from 'antd-style';
5
+ import { startCase } from 'lodash-es';
6
+ import { memo, useRef } from 'react';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import { useMarketStore } from '@/store/market';
10
+ import { AgentsMarketIndexItem } from '@/types/market';
11
+
12
+ import AgentCardBanner from './AgentCardBanner';
13
+ import { useStyles } from './style';
14
+
15
+ const { Paragraph } = Typography;
16
+ const AgentCardItem = memo<AgentsMarketIndexItem>(({ meta, identifier }) => {
17
+ const ref = useRef(null);
18
+ const isHovering = useHover(ref);
19
+ const onAgentCardClick = useMarketStore((s) => s.activateAgent);
20
+ const { avatar, title, description, tags, backgroundColor } = meta;
21
+ const { styles, theme } = useStyles();
22
+ const { isDarkMode } = useThemeMode();
23
+ return (
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
+ />
31
+ <Flexbox className={styles.inner} gap={8} ref={ref}>
32
+ <Avatar
33
+ animation={isHovering}
34
+ avatar={avatar}
35
+ background={backgroundColor || theme.colorFillTertiary}
36
+ className={styles.avatar}
37
+ size={56}
38
+ />
39
+ <Paragraph className={styles.title} ellipsis={{ rows: 1, tooltip: title }}>
40
+ {title}
41
+ </Paragraph>
42
+ <Paragraph className={styles.desc} ellipsis={{ rows: 2, tooltip: description }}>
43
+ {description}
44
+ </Paragraph>
45
+ <Flexbox gap={6} horizontal style={{ flexWrap: 'wrap' }}>
46
+ {(tags as string[]).map((tag: string, index) => (
47
+ <Tag key={index} style={{ margin: 0 }}>
48
+ {startCase(tag).trim()}
49
+ </Tag>
50
+ ))}
51
+ </Flexbox>
52
+ </Flexbox>
53
+ </Flexbox>
54
+ );
55
+ });
56
+
57
+ export default AgentCardItem;
@@ -0,0 +1,13 @@
1
+ import { Skeleton } from 'antd';
2
+ import { memo } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ const Loading = memo(() => {
6
+ return (
7
+ <Flexbox>
8
+ <Skeleton paragraph={{ rows: 8 }} />
9
+ </Flexbox>
10
+ );
11
+ });
12
+
13
+ export default Loading;
@@ -0,0 +1,37 @@
1
+ import { SpotlightCard } from '@lobehub/ui';
2
+ import { memo, useMemo } from 'react';
3
+
4
+ import { agentMarketSelectors, useMarketStore } from '@/store/market';
5
+
6
+ import TagList from '../TagList';
7
+ import AgentCardItem from './AgentCardItem';
8
+ import Loading from './Loading';
9
+
10
+ const gridRender = (item: any) => <AgentCardItem {...item} />;
11
+
12
+ const AgentCard = memo(() => {
13
+ const agentList = useMarketStore(agentMarketSelectors.getAgentList);
14
+
15
+ const [useFetchAgentList, keywords] = useMarketStore((s) => [
16
+ s.useFetchAgentList,
17
+ s.searchKeywords,
18
+ ]);
19
+
20
+ const { isLoading } = useFetchAgentList();
21
+
22
+ const agentListData = useMemo(() => {
23
+ if (!keywords) return agentList;
24
+ return agentList.filter(({ meta }) => JSON.stringify(meta).toLowerCase().includes(keywords));
25
+ }, [agentList, keywords]);
26
+
27
+ if (isLoading || !agentList) return <Loading />;
28
+
29
+ return (
30
+ <>
31
+ <TagList />
32
+ <SpotlightCard items={agentListData} renderItem={gridRender} />
33
+ </>
34
+ );
35
+ });
36
+
37
+ export default AgentCard;
@@ -0,0 +1,22 @@
1
+ import { createStyles } from 'antd-style';
2
+
3
+ export const useStyles = createStyles(({ css, token }) => ({
4
+ avatar: css``,
5
+
6
+ container: css`
7
+ position: relative;
8
+ overflow: hidden;
9
+ border-radius: 11px;
10
+ `,
11
+ desc: css`
12
+ color: ${token.colorTextDescription};
13
+ `,
14
+ inner: css`
15
+ padding: 16px;
16
+ `,
17
+ title: css`
18
+ margin-bottom: 0 !important;
19
+ font-size: 16px;
20
+ font-weight: 600;
21
+ `,
22
+ }));
@@ -0,0 +1,26 @@
1
+ import { SearchBar } from '@lobehub/ui';
2
+ import { useResponsive } from 'antd-style';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { useMarketStore } from '@/store/market';
7
+
8
+ const AgentSearchBar = memo(() => {
9
+ const { t } = useTranslation('market');
10
+ const keywords = useMarketStore((s) => s.searchKeywords);
11
+ const { mobile } = useResponsive();
12
+ return (
13
+ <SearchBar
14
+ allowClear
15
+ enableShortKey={!mobile}
16
+ onChange={(e) => useMarketStore.setState({ searchKeywords: e.target.value })}
17
+ placeholder={t('search.placeholder')}
18
+ shortKey={'k'}
19
+ spotlight={!mobile}
20
+ type={mobile ? 'block' : 'ghost'}
21
+ value={keywords}
22
+ />
23
+ );
24
+ });
25
+
26
+ export default AgentSearchBar;