@lobehub/chat 0.101.0 → 0.101.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.101.1](https://github.com/lobehub/lobe-chat/compare/v0.101.0...v0.101.1)
6
+
7
+ <sup>Released on **2023-11-14**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix market search (fix.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix market search (fix, closes [#437](https://github.com/lobehub/lobe-chat/issues/437) ([178b742](https://github.com/lobehub/lobe-chat/commit/178b742))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ## [Version 0.101.0](https://github.com/lobehub/lobe-chat/compare/v0.100.5...v0.101.0)
6
31
 
7
32
  <sup>Released on **2023-11-14**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.101.0",
3
+ "version": "0.101.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",
@@ -1,6 +1,6 @@
1
1
  import { DraggablePanel, DraggablePanelBody, DraggablePanelContainer } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
- import { memo, useState } from 'react';
3
+ import { memo, useCallback, useState } from 'react';
4
4
 
5
5
  import SafeSpacing from '@/components/SafeSpacing';
6
6
  import { MARKET_SIDEBAR_WIDTH } from '@/const/layoutTokens';
@@ -31,6 +31,18 @@ const SideBar = memo(() => {
31
31
  s.activateAgent,
32
32
  ]);
33
33
 
34
+ const handleExpandChange = useCallback(
35
+ (show: boolean) => {
36
+ if (!show) {
37
+ setTempId(useMarketStore.getState().currentIdentifier);
38
+ deactivateAgent();
39
+ } else if (tempId) {
40
+ activateAgent(tempId);
41
+ }
42
+ },
43
+ [deactivateAgent, activateAgent, tempId],
44
+ );
45
+
34
46
  return (
35
47
  <DraggablePanel
36
48
  className={styles.drawer}
@@ -40,14 +52,7 @@ const SideBar = memo(() => {
40
52
  expand={showAgentSidebar}
41
53
  minWidth={MARKET_SIDEBAR_WIDTH}
42
54
  mode={'fixed'}
43
- onExpandChange={(show) => {
44
- if (!show) {
45
- setTempId(useMarketStore.getState().currentIdentifier);
46
- deactivateAgent();
47
- } else if (tempId) {
48
- activateAgent(tempId);
49
- }
50
- }}
55
+ onExpandChange={handleExpandChange}
51
56
  placement={'right'}
52
57
  >
53
58
  <DraggablePanelContainer
@@ -6,18 +6,17 @@ import { FC, memo } from 'react';
6
6
 
7
7
  import AgentCard from '@/app/market/features/AgentCard';
8
8
  import ResponsiveIndex from '@/components/ResponsiveIndex';
9
- import { AgentsMarketIndexItem } from '@/types/market';
10
9
 
11
10
  import Index from '../index';
12
11
  import Layout from './layout.desktop';
13
12
 
14
13
  const Mobile: FC = dynamic(() => import('../(mobile)'), { ssr: false }) as FC;
15
14
 
16
- export default memo<{ defaultAgents?: AgentsMarketIndexItem[] }>(({ defaultAgents }) => (
15
+ export default memo(() => (
17
16
  <ResponsiveIndex Mobile={Mobile}>
18
17
  <Layout>
19
18
  <Index />
20
- <AgentCard CardRender={SpotlightCard} defaultAgents={defaultAgents} />
19
+ <AgentCard CardRender={SpotlightCard} />
21
20
  </Layout>
22
21
  </ResponsiveIndex>
23
22
  ));
@@ -3,15 +3,14 @@
3
3
  import { memo } from 'react';
4
4
 
5
5
  import AgentCard from '@/app/market/features/AgentCard';
6
- import { AgentsMarketIndexItem } from '@/types/market';
7
6
 
8
7
  import Index from '../index';
9
8
  import CardRender from './features/AgentCard';
10
9
  import Layout from './layout.mobile';
11
10
 
12
- export default memo<{ defaultAgents?: AgentsMarketIndexItem[] }>(({ defaultAgents }) => (
11
+ export default memo(() => (
13
12
  <Layout>
14
13
  <Index />
15
- <AgentCard CardRender={CardRender} defaultAgents={defaultAgents} mobile />
14
+ <AgentCard CardRender={CardRender} mobile />
16
15
  </Layout>
17
16
  ));
@@ -13,13 +13,16 @@ import AgentCardBanner from './AgentCardBanner';
13
13
  import { useStyles } from './style';
14
14
 
15
15
  const { Paragraph } = Typography;
16
+
16
17
  const AgentCardItem = memo<AgentsMarketIndexItem>(({ meta, identifier }) => {
17
18
  const ref = useRef(null);
18
19
  const isHovering = useHover(ref);
19
20
  const onAgentCardClick = useMarketStore((s) => s.activateAgent);
20
- const { avatar, title, description, tags, backgroundColor } = meta;
21
21
  const { styles, theme } = useStyles();
22
22
  const { isDarkMode } = useThemeMode();
23
+
24
+ const { avatar, title, description, tags, backgroundColor } = meta;
25
+
23
26
  return (
24
27
  <Flexbox className={styles.container} onClick={() => onAgentCardClick(identifier)}>
25
28
  <AgentCardBanner meta={meta} style={{ opacity: isDarkMode ? 0.9 : 0.4 }} />
@@ -1,71 +1,60 @@
1
1
  import { SpotlightCardProps } from '@lobehub/ui';
2
- import { FC, memo, useMemo } from 'react';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { FC, memo, useCallback } from 'react';
3
4
  import { useTranslation } from 'react-i18next';
4
5
  import { Flexbox } from 'react-layout-kit';
5
6
  import LazyLoad from 'react-lazy-load';
6
7
 
7
8
  import { agentMarketSelectors, useMarketStore } from '@/store/market';
8
- import { AgentsMarketIndexItem } from '@/types/market';
9
9
 
10
10
  import TagList from '../TagList';
11
11
  import AgentCardItem from './AgentCardItem';
12
12
  import Loading from './Loading';
13
13
  import { useStyles } from './style';
14
14
 
15
- const gridRender: SpotlightCardProps['renderItem'] = (item) => {
16
- // eslint-disable-next-line react-hooks/rules-of-hooks
17
- const { styles } = useStyles();
18
- return (
19
- <LazyLoad className={styles.lazy}>
20
- <AgentCardItem {...item} />
21
- </LazyLoad>
22
- );
23
- };
24
-
25
15
  export interface AgentCardProps {
26
16
  CardRender: FC<SpotlightCardProps>;
27
- defaultAgents?: AgentsMarketIndexItem[];
28
17
  mobile?: boolean;
29
18
  }
30
19
 
31
- const AgentCard = memo<AgentCardProps>(({ defaultAgents, CardRender, mobile }) => {
20
+ const AgentCard = memo<AgentCardProps>(({ CardRender, mobile }) => {
32
21
  const { t } = useTranslation('market');
33
-
34
- const { styles } = useStyles();
35
-
36
22
  const [useFetchAgentList, keywords] = useMarketStore((s) => [
37
23
  s.useFetchAgentList,
38
24
  s.searchKeywords,
39
25
  ]);
40
- useFetchAgentList();
41
-
42
- const clientAgentList = useMarketStore(agentMarketSelectors.getAgentList);
43
- const agentList = clientAgentList.length === 0 && defaultAgents ? defaultAgents : clientAgentList;
26
+ const { isLoading } = useFetchAgentList();
27
+ const agentList = useMarketStore(agentMarketSelectors.getAgentList, isEqual);
28
+ const { styles } = useStyles();
44
29
 
45
- const agentListData = useMemo(() => {
46
- if (!keywords) return agentList;
47
- return agentList.filter(({ meta }) => JSON.stringify(meta).toLowerCase().includes(keywords));
48
- }, [agentList, keywords]);
30
+ const GridRender: SpotlightCardProps['renderItem'] = useCallback(
31
+ (props: any) => (
32
+ <LazyLoad className={styles.lazy}>
33
+ <AgentCardItem {...props} />
34
+ </LazyLoad>
35
+ ),
36
+ [styles.lazy],
37
+ );
49
38
 
50
- if (agentList.length === 0) return <Loading />;
39
+ if (isLoading) return <Loading />;
51
40
 
52
41
  return (
53
42
  <Flexbox gap={mobile ? 16 : 24}>
54
43
  <TagList />
55
44
  {keywords ? (
56
45
  <CardRender
57
- items={agentListData}
58
- renderItem={gridRender}
46
+ items={agentList}
47
+ renderItem={GridRender}
59
48
  spotlight={mobile ? undefined : false}
60
49
  />
61
50
  ) : (
62
51
  <>
63
52
  <div className={styles.subTitle}>{t('title.recentSubmits')}</div>
64
- <CardRender items={agentListData.slice(0, 3)} renderItem={gridRender} />
53
+ <CardRender items={agentList.slice(0, 3)} renderItem={GridRender} />
65
54
  <div className={styles.subTitle}>{t('title.allAgents')}</div>
66
55
  <CardRender
67
- items={agentListData.slice(3)}
68
- renderItem={gridRender}
56
+ items={agentList.slice(3)}
57
+ renderItem={GridRender}
69
58
  spotlight={mobile ? undefined : false}
70
59
  />
71
60
  </>
@@ -17,7 +17,6 @@ const { Link } = Typography;
17
17
  const Header = memo(() => {
18
18
  const { t } = useTranslation('market');
19
19
  const { styles, theme } = useStyles();
20
-
21
20
  const createSession = useSessionStore((s) => s.createSession);
22
21
  const switchSideBar = useGlobalStore((s) => s.switchSideBar);
23
22
  const agentItem = useMarketStore(agentMarketSelectors.currentAgentItem);
@@ -22,11 +22,10 @@ const AgentModalInner = memo(() => {
22
22
  s.useFetchAgent,
23
23
  s.currentIdentifier,
24
24
  ]);
25
- const { styles } = useStyles();
26
-
27
- const { data, isLoading } = useFetchAgent(currentIdentifier);
28
25
  const { t } = useTranslation('market');
29
26
  const [tab, setTab] = useState<string>(InfoTabs.prompt);
27
+ const { data, isLoading } = useFetchAgent(currentIdentifier);
28
+ const { styles } = useStyles();
30
29
 
31
30
  if (isLoading || !data?.meta) return <Loading />;
32
31
 
@@ -1,22 +1,27 @@
1
1
  import { SearchBar } from '@lobehub/ui';
2
2
  import { useResponsive } from 'antd-style';
3
- import { memo, useState } from 'react';
3
+ import { memo, useCallback, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import { useMarketStore } from '@/store/market';
7
7
 
8
8
  const AgentSearchBar = memo(() => {
9
9
  const { t } = useTranslation('market');
10
- const keywords = useMarketStore((s) => s.searchKeywords);
10
+ const [keywords, setKeywords] = useMarketStore((s) => [s.searchKeywords, s.setSearchKeywords]);
11
11
  const [value, setValue] = useState(keywords);
12
12
  const { mobile } = useResponsive();
13
+
14
+ const handleSearch = useCallback(() => {
15
+ setKeywords(value);
16
+ }, [value, setKeywords]);
17
+
13
18
  return (
14
19
  <SearchBar
15
20
  allowClear
16
21
  enableShortKey={!mobile}
17
22
  onChange={(e) => setValue(e.target.value)}
18
- onPressEnter={() => useMarketStore.setState({ searchKeywords: value })}
19
- onSubmit={() => useMarketStore.setState({ searchKeywords: value })}
23
+ onPressEnter={handleSearch}
24
+ onSubmit={handleSearch}
20
25
  placeholder={t('search.placeholder')}
21
26
  shortKey={'k'}
22
27
  spotlight={!mobile}
@@ -0,0 +1,28 @@
1
+ import { Button } from 'antd';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { startCase } from 'lodash-es';
4
+ import { memo } from 'react';
5
+
6
+ import { agentMarketSelectors, useMarketStore } from '@/store/market';
7
+
8
+ const Inner = memo(() => {
9
+ const agentTagList = useMarketStore(agentMarketSelectors.getAgentTagList, isEqual);
10
+ const [keywords, setSearchKeywords] = useMarketStore((s) => [
11
+ s.searchKeywords,
12
+ s.setSearchKeywords,
13
+ ]);
14
+
15
+ return agentTagList.map((item) => (
16
+ <Button
17
+ key={item}
18
+ onClick={() => setSearchKeywords(item)}
19
+ shape={'round'}
20
+ size={'small'}
21
+ type={keywords === item ? 'primary' : 'default'}
22
+ >
23
+ {startCase(item)}
24
+ </Button>
25
+ ));
26
+ });
27
+
28
+ export default Inner;
@@ -1,32 +1,21 @@
1
- import { Button, Skeleton } from 'antd';
2
- import isEqual from 'fast-deep-equal';
3
- import { startCase } from 'lodash-es';
4
- import { memo } from 'react';
1
+ import { Skeleton } from 'antd';
2
+ import { Suspense, memo } from 'react';
5
3
  import { Flexbox } from 'react-layout-kit';
6
4
 
7
- import { agentMarketSelectors, useMarketStore } from '@/store/market';
5
+ import Inner from '@/app/market/features/TagList/Inner';
8
6
 
9
7
  const TagList = memo(() => {
10
- const agentTagList = useMarketStore(agentMarketSelectors.getAgentTagList, isEqual);
11
- const keywords = useMarketStore((s) => s.searchKeywords);
12
-
13
8
  return (
14
9
  <Flexbox gap={6} horizontal style={{ flexWrap: 'wrap' }}>
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'} />)}
10
+ <Suspense
11
+ fallback={Array.from({ length: 5 })
12
+ .fill('')
13
+ .map((_, index) => (
14
+ <Skeleton.Button key={index} shape={'round'} size={'small'} />
15
+ ))}
16
+ >
17
+ <Inner />
18
+ </Suspense>
30
19
  </Flexbox>
31
20
  );
32
21
  });
@@ -12,6 +12,7 @@ import type { Store } from './store';
12
12
  export interface StoreAction {
13
13
  activateAgent: (identifier: string) => void;
14
14
  deactivateAgent: () => void;
15
+ setSearchKeywords: (keywords: string) => void;
15
16
  updateAgentMap: (key: string, value: AgentsMarketItem) => void;
16
17
  useFetchAgent: (identifier: string) => SWRResponse<AgentsMarketItem>;
17
18
  useFetchAgentList: () => SWRResponse<LobeChatAgentsMarketIndex>;
@@ -29,6 +30,9 @@ export const createMarketAction: StateCreator<
29
30
  deactivateAgent: () => {
30
31
  set({ currentIdentifier: undefined }, false, 'deactivateAgent');
31
32
  },
33
+ setSearchKeywords: (keywords) => {
34
+ set({ searchKeywords: keywords });
35
+ },
32
36
  updateAgentMap: (key, value) => {
33
37
  const { agentMap } = get();
34
38
 
@@ -56,7 +60,11 @@ export const createMarketAction: StateCreator<
56
60
  useFetchAgentList: () =>
57
61
  useSWR<LobeChatAgentsMarketIndex>(getCurrentLanguage(), getAgentList, {
58
62
  onSuccess: (agentMarketIndex) => {
59
- set({ agentList: agentMarketIndex.agents }, false, 'useFetchAgentList');
63
+ set(
64
+ { agentList: agentMarketIndex.agents, tagList: agentMarketIndex.tags },
65
+ false,
66
+ 'useFetchAgentList',
67
+ );
60
68
  },
61
69
  }),
62
70
  });
@@ -7,6 +7,7 @@ export interface StoreState {
7
7
  agentMap: MarketAgentMap;
8
8
  currentIdentifier: string;
9
9
  searchKeywords: string;
10
+ tagList: string[];
10
11
  }
11
12
 
12
13
  export const initialState: StoreState = {
@@ -14,4 +15,5 @@ export const initialState: StoreState = {
14
15
  agentMap: {},
15
16
  currentIdentifier: '',
16
17
  searchKeywords: '',
18
+ tagList: [],
17
19
  };
@@ -1,17 +1,19 @@
1
- import { flatten } from 'lodash-es';
2
-
3
1
  import { DEFAULT_AGENTS_MARKET_ITEM } from '@/const/market';
4
2
  import { AgentsMarketItem } from '@/types/market';
5
- import { findDuplicates } from '@/utils/findDuplicates';
6
3
 
7
4
  import type { Store } from './store';
8
5
 
9
- const getAgentList = (s: Store) => s.agentList;
10
- const getAgentTagList = (s: Store) => {
11
- const agentList = s.agentList;
12
- const rawAgentTagList = flatten(agentList.map((item) => item.meta.tags)) as string[];
13
- return findDuplicates(rawAgentTagList);
6
+ const getAgentList = (s: Store) => {
7
+ const { searchKeywords, agentList } = s;
8
+ if (!searchKeywords) return agentList;
9
+ return agentList.filter(({ meta }) => {
10
+ const checkMeta: string = [meta.tags, meta.title, meta.description, meta.avatar]
11
+ .filter(Boolean)
12
+ .join('');
13
+ return checkMeta.toLowerCase().includes(searchKeywords.toLowerCase());
14
+ });
14
15
  };
16
+ const getAgentTagList = (s: Store) => s.tagList;
15
17
 
16
18
  const getAgentItemById = (d: string) => (s: Store) => s.agentMap[d];
17
19
 
@@ -16,4 +16,5 @@ export type AgentsMarketItem = AgentsMarketIndexItem & LobeAgentSettings;
16
16
  export interface LobeChatAgentsMarketIndex {
17
17
  agents: AgentsMarketIndexItem[];
18
18
  schemaVersion: 1;
19
+ tags: string[];
19
20
  }
@@ -1,22 +0,0 @@
1
- export const findDuplicates = (arr: string[]): string[] => {
2
- const duplicates: { [key: string]: number } = {};
3
-
4
- // 统计每个项目出现的次数
5
- for (const item of arr) {
6
- if (duplicates[item]) {
7
- duplicates[item]++;
8
- } else {
9
- duplicates[item] = 1;
10
- }
11
- }
12
-
13
- // 挑出重复出现 3 次以上的项目
14
- const COUNT = 3;
15
-
16
- const result = Object.keys(duplicates).filter((item) => duplicates[item] >= COUNT);
17
-
18
- // 按重复次数从多到少排序
19
- result.sort((a, b) => duplicates[b] - duplicates[a]);
20
-
21
- return result;
22
- };