@lobehub/chat 0.151.11 → 0.152.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +8 -8
  3. package/README.zh-CN.md +2 -2
  4. package/package.json +1 -1
  5. package/src/app/(main)/market/{_layout/Desktop/AgentDetail.tsx → @detail/_layout/Desktop.tsx} +16 -11
  6. package/src/app/(main)/market/{(mobile)/features/AgentDetail.tsx → @detail/_layout/Mobile.tsx} +8 -6
  7. package/src/app/(main)/market/@detail/default.tsx +21 -0
  8. package/src/app/(main)/market/{features/AgentDetailContent/AgentInfo → @detail/features/AgentDetailContent}/Header.tsx +30 -21
  9. package/src/app/(main)/market/{features/AgentDetailContent/AgentInfo → @detail/features/AgentDetailContent}/index.tsx +10 -4
  10. package/src/app/(main)/market/{features/AgentDetailContent/AgentInfo → @detail/features/AgentDetailContent}/style.ts +7 -11
  11. package/src/app/(main)/market/_layout/Desktop/Header.tsx +3 -6
  12. package/src/app/(main)/market/_layout/Desktop/Hero.tsx +39 -0
  13. package/src/app/(main)/market/_layout/Desktop/index.tsx +23 -44
  14. package/src/app/(main)/market/_layout/Mobile/Header.tsx +2 -2
  15. package/src/app/(main)/market/_layout/Mobile/index.tsx +15 -9
  16. package/src/app/(main)/market/_layout/type.ts +6 -0
  17. package/src/app/(main)/market/features/AgentCard/AgentCardBanner.tsx +10 -9
  18. package/src/app/(main)/market/features/AgentCard/index.tsx +133 -58
  19. package/src/app/(main)/market/features/AgentList.tsx +94 -0
  20. package/src/app/(main)/market/features/AgentSearchBar.tsx +42 -0
  21. package/src/app/(main)/market/features/ShareAgentButton/Inner.tsx +9 -7
  22. package/src/app/(main)/market/features/ShareAgentButton/index.tsx +22 -15
  23. package/src/app/(main)/market/features/TagList.tsx +65 -0
  24. package/src/app/(main)/market/layout.tsx +11 -0
  25. package/src/app/(main)/market/loading.tsx +2 -2
  26. package/src/app/(main)/market/page.tsx +22 -12
  27. package/src/const/url.ts +1 -0
  28. package/src/features/ChatInput/STT/browser.tsx +1 -1
  29. package/src/features/ChatInput/STT/openai.tsx +1 -1
  30. package/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx +2 -2
  31. package/src/hooks/useImportConfig.ts +20 -1
  32. package/src/layout/GlobalProvider/StoreInitialization.tsx +10 -1
  33. package/src/locales/resources.ts +1 -0
  34. package/src/server/translation.ts +20 -0
  35. package/src/services/__tests__/chat.test.ts +1 -1
  36. package/src/services/__tests__/share.test.ts +50 -5
  37. package/src/services/share.ts +29 -2
  38. package/src/store/chat/slices/share/action.test.ts +9 -11
  39. package/src/store/chat/slices/share/action.ts +2 -2
  40. package/src/store/market/action.ts +1 -1
  41. package/src/store/session/slices/session/action.ts +1 -1
  42. package/src/utils/difference.ts +2 -1
  43. package/src/app/(main)/market/(desktop)/index.tsx +0 -20
  44. package/src/app/(main)/market/(mobile)/features/AgentCard.tsx +0 -31
  45. package/src/app/(main)/market/(mobile)/index.tsx +0 -25
  46. package/src/app/(main)/market/components/Loading.tsx +0 -13
  47. package/src/app/(main)/market/features/AgentCard/AgentCardItem.tsx +0 -54
  48. package/src/app/(main)/market/features/AgentCard/style.ts +0 -33
  49. package/src/app/(main)/market/features/AgentDetailContent/index.tsx +0 -12
  50. package/src/app/(main)/market/features/AgentSearchBar/index.tsx +0 -40
  51. package/src/app/(main)/market/features/PageTitle/index.tsx +0 -13
  52. package/src/app/(main)/market/features/TagList/Inner.tsx +0 -28
  53. package/src/app/(main)/market/features/TagList/index.tsx +0 -23
  54. package/src/app/(main)/market/layout.ts +0 -8
  55. /package/src/app/(main)/market/{features/AgentDetailContent/AgentInfo → @detail/features/AgentDetailContent}/Comment.tsx +0 -0
  56. /package/src/app/(main)/market/{features/AgentDetailContent/AgentInfo → @detail/features/AgentDetailContent}/Loading.tsx +0 -0
  57. /package/src/app/(main)/market/{features/AgentDetailContent/AgentInfo → @detail/features/AgentDetailContent}/TokenTag.tsx +0 -0
@@ -3,8 +3,6 @@ import { createStyles } from 'antd-style';
3
3
  import { memo } from 'react';
4
4
  import { Flexbox } from 'react-layout-kit';
5
5
 
6
- import { MetaData } from '@/types/meta';
7
-
8
6
  export const useStyles = createStyles(({ css, token }) => ({
9
7
  banner: css`
10
8
  position: relative;
@@ -15,25 +13,22 @@ export const useStyles = createStyles(({ css, token }) => ({
15
13
  margin-bottom: -56px;
16
14
 
17
15
  background: ${token.colorFillSecondary};
18
-
19
- mask-image: linear-gradient(to bottom, #fff, transparent);
20
16
  `,
21
17
  bannerImg: css`
22
18
  position: absolute;
23
- top: -50%;
24
- filter: blur(50px) saturate(2);
19
+ filter: blur(40px) saturate(1.5);
25
20
  `,
26
21
  }));
27
22
 
28
23
  interface AgentCardBannerProps extends DivProps {
24
+ avatar?: string;
29
25
  mask?: boolean;
30
26
  maskColor?: string;
31
- meta: MetaData;
32
27
  size?: number;
33
28
  }
34
29
 
35
30
  const AgentCardBanner = memo<AgentCardBannerProps>(
36
- ({ meta, className, size = 200, children, ...props }) => {
31
+ ({ avatar, className, size = 600, children, ...props }) => {
37
32
  const { styles, cx } = useStyles();
38
33
 
39
34
  return (
@@ -43,7 +38,13 @@ const AgentCardBanner = memo<AgentCardBannerProps>(
43
38
  justify={'center'}
44
39
  {...props}
45
40
  >
46
- <Avatar alt={'banner'} avatar={meta.avatar} className={styles.bannerImg} size={size} />
41
+ <Avatar
42
+ alt={'banner'}
43
+ avatar={avatar}
44
+ className={styles.bannerImg}
45
+ shape={'square'}
46
+ size={size}
47
+ />
47
48
  {children}
48
49
  </Flexbox>
49
50
  );
@@ -1,62 +1,137 @@
1
- import { SpotlightCardProps } from '@lobehub/ui';
2
- import isEqual from 'fast-deep-equal';
3
- import { FC, memo, useCallback } from 'react';
4
- import { useTranslation } from 'react-i18next';
5
- import { Flexbox } from 'react-layout-kit';
6
- import LazyLoad from 'react-lazy-load';
7
-
8
- import { agentMarketSelectors, useMarketStore } from '@/store/market';
9
-
10
- import Loading from '../../components/Loading';
11
- import TagList from '../TagList';
12
- import AgentCardItem from './AgentCardItem';
13
- import { useStyles } from './style';
14
-
15
- export interface AgentCardProps {
16
- CardRender: FC<SpotlightCardProps>;
17
- mobile?: boolean;
1
+ import { Avatar, Tag } from '@lobehub/ui';
2
+ import { Typography } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import { startCase } from 'lodash-es';
5
+ import Link from 'next/link';
6
+ import { memo } from 'react';
7
+ import { Center, 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
+
14
+ const { Paragraph, Title } = Typography;
15
+
16
+ const useStyles = createStyles(({ css, token, isDarkMode }) => ({
17
+ banner: css`
18
+ opacity: ${isDarkMode ? 0.9 : 0.4};
19
+ `,
20
+ container: css`
21
+ cursor: pointer;
22
+
23
+ position: relative;
24
+
25
+ overflow: hidden;
26
+
27
+ height: 100%;
28
+
29
+ background: ${token.colorBgContainer};
30
+ border-radius: ${token.borderRadiusLG}px;
31
+ box-shadow: 0 0 1px 1px ${token.colorFillQuaternary} inset;
32
+
33
+ transition: box-shadow 0.2s ${token.motionEaseInOut};
34
+
35
+ &:hover {
36
+ box-shadow: 0 0 1px 1px ${token.colorFillSecondary} inset;
37
+ }
38
+ `,
39
+ desc: css`
40
+ min-height: 44px;
41
+ margin-bottom: 0 !important;
42
+ color: ${token.colorTextDescription};
43
+ `,
44
+ inner: css`
45
+ padding: 16px;
46
+ `,
47
+ time: css`
48
+ color: ${token.colorTextDescription};
49
+ `,
50
+ title: css`
51
+ margin-bottom: 0 !important;
52
+ font-size: 18px !important;
53
+ `,
54
+ }));
55
+
56
+ interface AgentCardProps extends AgentsMarketIndexItem {
57
+ variant?: 'default' | 'compact';
18
58
  }
19
59
 
20
- const AgentCard = memo<AgentCardProps>(({ CardRender, mobile }) => {
21
- const { t } = useTranslation('market');
22
- const [useFetchAgentList, keywords] = useMarketStore((s) => [
23
- s.useFetchAgentList,
24
- s.searchKeywords,
25
- ]);
26
- const { isLoading } = useFetchAgentList();
27
- const agentList = useMarketStore(agentMarketSelectors.getAgentList, isEqual);
28
- const { styles } = useStyles();
29
-
30
- const GridRender: SpotlightCardProps['renderItem'] = useCallback(
31
- (props: any) => (
32
- <LazyLoad className={styles.lazy}>
33
- <AgentCardItem {...props} />
34
- </LazyLoad>
35
- ),
36
- [styles.lazy],
37
- );
38
-
39
- if (isLoading) return <Loading />;
40
-
41
- return (
42
- <Flexbox gap={mobile ? 16 : 24}>
43
- <TagList />
44
- {keywords ? (
45
- <CardRender
46
- items={agentList}
47
- renderItem={GridRender}
48
- spotlight={mobile ? undefined : false}
49
- />
50
- ) : (
51
- <>
52
- <div className={styles.subTitle}>{t('title.recentSubmits')}</div>
53
- <CardRender items={agentList.slice(0, 3)} renderItem={GridRender} />
54
- <div className={styles.subTitle}>{t('title.allAgents')}</div>
55
- <CardRender items={agentList.slice(3)} renderItem={GridRender} />
56
- </>
57
- )}
58
- </Flexbox>
59
- );
60
- });
60
+ const AgentCard = memo<AgentCardProps>(
61
+ ({ meta, createAt, author, homepage, identifier, variant }) => {
62
+ const { avatar, title, description, tags } = meta;
63
+
64
+ const onAgentCardClick = useMarketStore((s) => s.activateAgent);
65
+ const { styles, theme } = useStyles();
66
+ const isCompact = variant === 'compact';
67
+
68
+ return (
69
+ <Flexbox
70
+ className={styles.container}
71
+ gap={24}
72
+ key={identifier}
73
+ onClick={() => onAgentCardClick(identifier)}
74
+ >
75
+ {!isCompact && <AgentCardBanner avatar={avatar} />}
76
+ <Flexbox className={styles.inner} gap={12}>
77
+ <Flexbox align={'flex-end'} gap={16} horizontal justify={'space-between'} width={'100%'}>
78
+ <Title className={styles.title} ellipsis={{ rows: 1, tooltip: title }} level={3}>
79
+ {title}
80
+ </Title>
81
+ {isCompact ? (
82
+ <Avatar
83
+ alt={title}
84
+ avatar={avatar}
85
+ size={40}
86
+ style={{ alignSelf: 'flex-end' }}
87
+ title={title}
88
+ />
89
+ ) : (
90
+ <Center
91
+ flex={'none'}
92
+ height={64}
93
+ style={{
94
+ background: theme.colorBgContainer,
95
+ borderRadius: '50%',
96
+ marginTop: -6,
97
+ overflow: 'hidden',
98
+ zIndex: 2,
99
+ }}
100
+ width={64}
101
+ >
102
+ <Avatar alt={title} avatar={avatar} size={56} title={title} />
103
+ </Center>
104
+ )}
105
+ </Flexbox>
106
+ {!isCompact && (
107
+ <Flexbox gap={12} horizontal style={{ fontSize: 12 }}>
108
+ <Link aria-label={author} href={homepage} target={'_blank'}>
109
+ @{author}
110
+ </Link>
111
+ <time className={styles.time} dateTime={new Date(createAt).toISOString()}>
112
+ {createAt}
113
+ </time>
114
+ </Flexbox>
115
+ )}
116
+ <Paragraph className={styles.desc} ellipsis={{ rows: 2 }}>
117
+ {description}
118
+ </Paragraph>
119
+ <Flexbox gap={6} horizontal style={{ flexWrap: 'wrap' }}>
120
+ {(tags as string[])
121
+ .slice(0, 4)
122
+ .filter(Boolean)
123
+ .map((tag: string, index) => (
124
+ <Tag key={index} style={{ margin: 0 }}>
125
+ {startCase(tag).trim()}
126
+ </Tag>
127
+ ))}
128
+ </Flexbox>
129
+ </Flexbox>
130
+ </Flexbox>
131
+ );
132
+ },
133
+ );
134
+
135
+ AgentCard.displayName = 'AgentCard';
61
136
 
62
137
  export default AgentCard;
@@ -0,0 +1,94 @@
1
+ 'use client';
2
+
3
+ import { Grid, SpotlightCardProps } from '@lobehub/ui';
4
+ import { Empty, Skeleton } from 'antd';
5
+ import { createStyles } from 'antd-style';
6
+ import isEqual from 'fast-deep-equal';
7
+ import { memo, useCallback, useLayoutEffect } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import LazyLoad from 'react-lazy-load';
10
+
11
+ import { agentMarketSelectors, useMarketStore } from '@/store/market';
12
+
13
+ import AgentCard from './AgentCard';
14
+
15
+ const useStyles = createStyles(({ css, responsive }) => ({
16
+ compactLazy: css`
17
+ min-height: 120px;
18
+ `,
19
+ lazy: css`
20
+ min-height: 332px;
21
+ `,
22
+ title: css`
23
+ margin-top: 0.5em;
24
+ font-size: 24px;
25
+ font-weight: 600;
26
+ ${responsive.mobile} {
27
+ font-size: 20px;
28
+ }
29
+ `,
30
+ }));
31
+
32
+ export interface AgentListProps {
33
+ mobile?: boolean;
34
+ }
35
+
36
+ const AgentList = memo<AgentListProps>(({ mobile }) => {
37
+ const { t } = useTranslation('market');
38
+ const [searchKeywords, useFetchAgentList] = useMarketStore((s) => [
39
+ s.searchKeywords,
40
+ s.useFetchAgentList,
41
+ ]);
42
+ const { isLoading } = useFetchAgentList();
43
+ const agentList = useMarketStore(agentMarketSelectors.getAgentList, isEqual);
44
+ const { styles } = useStyles();
45
+ const recentLength = mobile ? 3 : 6;
46
+
47
+ useLayoutEffect(() => {
48
+ // refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated
49
+ useMarketStore.persist.rehydrate();
50
+ }, []);
51
+
52
+ const GridCompactRender: SpotlightCardProps['renderItem'] = useCallback(
53
+ (props: any) => (
54
+ <LazyLoad className={styles.compactLazy} offset={332}>
55
+ <AgentCard variant={'compact'} {...props} />
56
+ </LazyLoad>
57
+ ),
58
+ [],
59
+ );
60
+
61
+ if (isLoading) return <Skeleton paragraph={{ rows: 8 }} title={false} />;
62
+
63
+ if (searchKeywords) {
64
+ if (agentList.length === 0) return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />;
65
+ return (
66
+ <Grid rows={3}>
67
+ {agentList.map((item) => (
68
+ <GridCompactRender key={item.identifier} {...item} />
69
+ ))}
70
+ </Grid>
71
+ );
72
+ }
73
+
74
+ return (
75
+ <>
76
+ <h2 className={styles.title}>{t('title.recentSubmits')}</h2>
77
+ <Grid rows={3}>
78
+ {agentList.slice(0, recentLength).map((item) => (
79
+ <AgentCard key={item.identifier} {...item} />
80
+ ))}
81
+ </Grid>
82
+ <h2 className={styles.title}>{t('title.allAgents')}</h2>
83
+ <Grid rows={3}>
84
+ {agentList.slice(recentLength).map((item) => (
85
+ <GridCompactRender key={item.identifier} {...item} />
86
+ ))}
87
+ </Grid>
88
+ </>
89
+ );
90
+ });
91
+
92
+ AgentList.displayName = 'AgentList';
93
+
94
+ export default AgentList;
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { SearchBar } from '@lobehub/ui';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import useControlledState from 'use-merge-value';
7
+
8
+ import { useMarketStore } from '@/store/market';
9
+
10
+ interface AgentSearchBarProps {
11
+ mobile?: boolean;
12
+ }
13
+
14
+ const AgentSearchBar = memo<AgentSearchBarProps>(({ mobile }) => {
15
+ const { t } = useTranslation('market');
16
+ const [searchKeywords, setSearchKeywords] = useMarketStore((s) => [
17
+ s.searchKeywords,
18
+ s.setSearchKeywords,
19
+ ]);
20
+
21
+ const [keyword, setKeyword] = useControlledState(searchKeywords, {
22
+ onChange: setSearchKeywords,
23
+ value: searchKeywords,
24
+ });
25
+
26
+ return (
27
+ <SearchBar
28
+ allowClear
29
+ enableShortKey={!mobile}
30
+ onChange={(e) => {
31
+ setKeyword(e.target.value);
32
+ }}
33
+ placeholder={t('search.placeholder')}
34
+ shortKey={'k'}
35
+ spotlight={!mobile}
36
+ type={mobile ? 'block' : 'ghost'}
37
+ value={keyword}
38
+ />
39
+ );
40
+ });
41
+
42
+ export default AgentSearchBar;
@@ -1,4 +1,4 @@
1
- import { Icon } from '@lobehub/ui';
1
+ import { Icon, Typography } from '@lobehub/ui';
2
2
  import { Button, Divider, Tag } from 'antd';
3
3
  import { Github, Settings, Share2 } from 'lucide-react';
4
4
  import Image from 'next/image';
@@ -10,7 +10,7 @@ import { AGENTS_INDEX_GITHUB, imageUrl } from '@/const/url';
10
10
  const Inner = memo(() => {
11
11
  const { t } = useTranslation('market');
12
12
  return (
13
- <>
13
+ <Typography fontSize={14} headerMultiple={0.5} marginMultiple={0.4}>
14
14
  <Image
15
15
  alt={'banner'}
16
16
  height={602}
@@ -23,12 +23,14 @@ const Inner = memo(() => {
23
23
  <span>{t('guide.func1.title')}</span>
24
24
  </h3>
25
25
  <p>
26
- <Icon icon={Settings} />
27
- {' - '}
26
+ <kbd>
27
+ <Icon icon={Settings} />
28
+ </kbd>
28
29
  {t('guide.func1.desc1')}
29
30
  <br />
30
- <Icon icon={Share2} />
31
- {' - '}
31
+ <kbd>
32
+ <Icon icon={Share2} />
33
+ </kbd>
32
34
  {t('guide.func1.desc2')}
33
35
  </p>
34
36
  <Divider />
@@ -45,7 +47,7 @@ const Inner = memo(() => {
45
47
  >
46
48
  {t('guide.func2.button')}
47
49
  </Button>
48
- </>
50
+ </Typography>
49
51
  );
50
52
  });
51
53
 
@@ -1,34 +1,41 @@
1
1
  import { ActionIcon, Icon, Modal } from '@lobehub/ui';
2
- import { Button } from 'antd';
3
- import { Rss } from 'lucide-react';
2
+ import { Button, Skeleton } from 'antd';
3
+ import { useResponsive } from 'antd-style';
4
+ import { Upload } from 'lucide-react';
5
+ import dynamic from 'next/dynamic';
4
6
  import { memo, useState } from 'react';
5
7
  import { useTranslation } from 'react-i18next';
6
8
 
7
9
  import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
8
10
 
9
- import Inner from './Inner';
11
+ const Inner = dynamic(() => import('./Inner'), {
12
+ loading: () => <Skeleton paragraph={{ rows: 8 }} title={false} />,
13
+ });
10
14
 
11
15
  const ShareAgentButton = memo<{ mobile?: boolean }>(({ mobile }) => {
16
+ const { mobile: resMobile } = useResponsive();
12
17
  const { t } = useTranslation('market');
13
18
  const [isModalOpen, setIsModalOpen] = useState(false);
14
19
 
15
- const buttonContent = mobile ? (
16
- <ActionIcon
17
- icon={Rss}
18
- onClick={() => setIsModalOpen(true)}
19
- size={MOBILE_HEADER_ICON_SIZE}
20
- title={t('submitAgent')}
21
- />
22
- ) : (
23
- <Button icon={<Icon icon={Rss} />} onClick={() => setIsModalOpen(true)}>
24
- {t('submitAgent')}
25
- </Button>
26
- );
20
+ const buttonContent =
21
+ mobile || resMobile ? (
22
+ <ActionIcon
23
+ icon={Upload}
24
+ onClick={() => setIsModalOpen(true)}
25
+ size={MOBILE_HEADER_ICON_SIZE}
26
+ title={t('submitAgent')}
27
+ />
28
+ ) : (
29
+ <Button icon={<Icon icon={Upload} />} onClick={() => setIsModalOpen(true)}>
30
+ {t('submitAgent')}
31
+ </Button>
32
+ );
27
33
 
28
34
  return (
29
35
  <>
30
36
  {buttonContent}
31
37
  <Modal
38
+ allowFullscreen
32
39
  footer={null}
33
40
  onCancel={() => setIsModalOpen(false)}
34
41
  open={isModalOpen}
@@ -0,0 +1,65 @@
1
+ 'use client';
2
+
3
+ import { Button } from 'antd';
4
+ import { createStyles, useResponsive } from 'antd-style';
5
+ import isEqual from 'fast-deep-equal';
6
+ import { startCase } from 'lodash-es';
7
+ import { memo } from 'react';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ import { agentMarketSelectors, useMarketStore } from '@/store/market';
11
+
12
+ const useStyles = createStyles(({ css, token }) => ({
13
+ active: css`
14
+ color: ${token.colorBgLayout};
15
+ background: ${token.colorPrimary};
16
+
17
+ &:hover {
18
+ color: ${token.colorBgLayout} !important;
19
+ background: ${token.colorPrimary} !important;
20
+ }
21
+ `,
22
+ tag: css`
23
+ background: ${token.colorBgContainer};
24
+ border: none;
25
+
26
+ &:hover {
27
+ background: ${token.colorBgElevated} !important;
28
+ }
29
+ `,
30
+ }));
31
+
32
+ const TagList = memo(() => {
33
+ const { cx, styles } = useStyles();
34
+ const { md = true } = useResponsive();
35
+ const [searchKeywords, setSearchKeywords] = useMarketStore((s) => [
36
+ s.searchKeywords,
37
+ s.setSearchKeywords,
38
+ ]);
39
+ const agentTagList = useMarketStore(agentMarketSelectors.getAgentTagList, isEqual);
40
+
41
+ const list = md ? agentTagList : agentTagList.slice(0, 20);
42
+
43
+ return (
44
+ <Flexbox gap={6} horizontal style={{ flexWrap: 'wrap' }}>
45
+ {list.map((item) => {
46
+ const isActive = searchKeywords === item;
47
+ return (
48
+ <Button
49
+ className={cx(styles.tag, isActive && styles.active)}
50
+ key={item}
51
+ onClick={() => {
52
+ setSearchKeywords(isActive ? '' : item);
53
+ }}
54
+ shape={'round'}
55
+ size={'small'}
56
+ >
57
+ {startCase(item)}
58
+ </Button>
59
+ );
60
+ })}
61
+ </Flexbox>
62
+ );
63
+ });
64
+
65
+ export default TagList;
@@ -0,0 +1,11 @@
1
+ import ServerLayout from '@/components/server/ServerLayout';
2
+
3
+ import Desktop from './_layout/Desktop';
4
+ import Mobile from './_layout/Mobile';
5
+ import { LayoutProps } from './_layout/type';
6
+
7
+ const MainLayout = ServerLayout<LayoutProps>({ Desktop, Mobile });
8
+
9
+ MainLayout.displayName = 'MarketLayout';
10
+
11
+ export default MainLayout;
@@ -1,3 +1,3 @@
1
- import Load from './components/Loading';
1
+ import { Skeleton } from 'antd';
2
2
 
3
- export default () => <Load />;
3
+ export default () => <Skeleton paragraph={{ rows: 8 }} style={{ marginBlock: 24 }} title={false} />;
@@ -1,25 +1,35 @@
1
- import { Metadata } from 'next';
1
+ import { Flexbox } from 'react-layout-kit';
2
2
 
3
3
  import { getCanonicalUrl } from '@/const/url';
4
+ import { translation } from '@/server/translation';
4
5
  import { isMobileDevice } from '@/utils/responsive';
5
6
 
6
- import DesktopPage from './(desktop)';
7
- import MobilePage from './(mobile)';
8
- import PageTitle from './features/PageTitle';
7
+ import AgentList from './features/AgentList';
8
+ import AgentSearchBar from './features/AgentSearchBar';
9
+ import TagList from './features/TagList';
9
10
 
10
- export default () => {
11
- const mobile = isMobileDevice();
11
+ export const generateMetadata = async () => {
12
+ const { t } = await translation('common');
13
+ return {
14
+ alternates: { canonical: getCanonicalUrl('/market') },
15
+ title: t('tab.market'),
16
+ };
17
+ };
12
18
 
13
- const Page = mobile ? MobilePage : DesktopPage;
19
+ const Page = () => {
20
+ const mobile = isMobileDevice();
14
21
 
15
22
  return (
16
23
  <>
17
- <PageTitle />
18
- <Page />
24
+ <AgentSearchBar mobile={mobile} />
25
+ <Flexbox gap={mobile ? 16 : 24}>
26
+ <TagList />
27
+ <AgentList mobile={mobile} />
28
+ </Flexbox>
19
29
  </>
20
30
  );
21
31
  };
22
32
 
23
- export const metadata: Metadata = {
24
- alternates: { canonical: getCanonicalUrl('/market') },
25
- };
33
+ Page.DisplayName = 'Market';
34
+
35
+ export default Page;
package/src/const/url.ts CHANGED
@@ -43,6 +43,7 @@ export const SESSION_CHAT_URL = (id: string = INBOX_SESSION_ID, mobile?: boolean
43
43
 
44
44
  export const imageUrl = (filename: string) => withBasePath(`/images/${filename}`);
45
45
 
46
+ export const LOBE_URL_IMPORT_NAME = 'settings';
46
47
  export const EMAIL_SUPPORT = 'support@lobehub.com';
47
48
  export const EMAIL_BUSINESS = 'hello@lobehub.com';
48
49
 
@@ -1,5 +1,5 @@
1
1
  import { SpeechRecognitionOptions, useSpeechRecognition } from '@lobehub/tts/react';
2
- import { isEqual } from 'lodash';
2
+ import isEqual from 'fast-deep-equal';
3
3
  import { memo, useCallback, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { SWRConfiguration } from 'swr';
@@ -1,6 +1,6 @@
1
1
  import { getRecordMineType } from '@lobehub/tts';
2
2
  import { OpenAISTTOptions, useOpenAISTT } from '@lobehub/tts/react';
3
- import { isEqual } from 'lodash';
3
+ import isEqual from 'fast-deep-equal';
4
4
  import { memo, useCallback, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { SWRConfiguration } from 'swr';
@@ -10,7 +10,7 @@ import { memo, useMemo, useState } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import { Flexbox } from 'react-layout-kit';
12
12
 
13
- import { agentMarketSelectors, useMarketStore } from '@/store/market';
13
+ import { useMarketStore } from '@/store/market';
14
14
 
15
15
  const { Paragraph } = Typography;
16
16
 
@@ -53,7 +53,7 @@ const AgentsSuggest = memo(() => {
53
53
  const [sliceStart, setSliceStart] = useState(0);
54
54
  const useFetchAgentList = useMarketStore((s) => s.useFetchAgentList);
55
55
  const { isLoading } = useFetchAgentList();
56
- const agentList = useMarketStore((s) => agentMarketSelectors.getAgentList(s), isEqual);
56
+ const agentList = useMarketStore((s) => s.agentList, isEqual);
57
57
  const { styles } = useStyles();
58
58
 
59
59
  const loadingCards = Array.from({ length: 4 }).map((_, index) => (