@lobehub/chat 0.73.0 → 0.75.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/.github/workflows/contributor-help.yml +1 -1
  2. package/.github/workflows/sync.yml +43 -0
  3. package/.github/workflows/wiki-sync.yml +1 -0
  4. package/CHANGELOG.md +87 -0
  5. package/README.md +40 -6
  6. package/{README-zh_CN.md → README.zh-CN.md} +38 -4
  7. package/docs/Home.md +6 -0
  8. package/docs/Upstream-Sync.en-US.md +16 -0
  9. package/docs/Upstream-Sync.zh-CN.md +16 -0
  10. package/docs/_Sidebar.md +6 -0
  11. package/locales/en_US/market.json +12 -0
  12. package/locales/en_US/plugin.json +11 -2
  13. package/locales/en_US/setting.json +63 -2
  14. package/locales/ru_RU/market.json +12 -0
  15. package/locales/ru_RU/plugin.json +44 -49
  16. package/locales/ru_RU/setting.json +63 -2
  17. package/locales/zh_CN/common.json +1 -1
  18. package/locales/zh_CN/market.json +12 -0
  19. package/locales/zh_CN/plugin.json +19 -5
  20. package/locales/zh_CN/setting.json +62 -14
  21. package/package.json +5 -4
  22. package/scripts/i18nWorkflow/const.ts +19 -0
  23. package/scripts/i18nWorkflow/genDefaultLocale.ts +19 -0
  24. package/scripts/i18nWorkflow/genDiff.ts +50 -0
  25. package/scripts/i18nWorkflow/genResources.ts +26 -0
  26. package/scripts/i18nWorkflow/index.ts +15 -0
  27. package/scripts/i18nWorkflow/utils.ts +85 -0
  28. package/src/const/layoutTokens.ts +3 -1
  29. package/src/const/market.ts +15 -0
  30. package/src/const/url.ts +29 -4
  31. package/src/features/AgentSetting/AgentConfig/index.tsx +1 -2
  32. package/src/features/AgentSetting/AgentPlugin/MarketSettingModal.tsx +1 -1
  33. package/src/features/AgentSetting/store/initialState.ts +1 -1
  34. package/src/features/MobileTabBar/index.tsx +2 -2
  35. package/src/features/SideBar/TopActions.tsx +2 -2
  36. package/src/locales/default/market.ts +12 -0
  37. package/src/locales/resources/en_US.ts +2 -0
  38. package/src/locales/resources/ru_RU.ts +2 -0
  39. package/src/locales/resources/zh_CN.ts +2 -0
  40. package/src/pages/chat/features/Conversation/Input/{Action.tsx → ActionBar/ActionLeft.tsx} +2 -2
  41. package/src/pages/chat/features/Conversation/Input/ActionBar/ActionRight.tsx +36 -0
  42. package/src/pages/chat/features/Conversation/Input/Desktop.tsx +40 -0
  43. package/src/pages/chat/features/Conversation/Input/Footer/SaveTopic.tsx +27 -0
  44. package/src/pages/chat/features/Conversation/Input/Footer/index.tsx +35 -0
  45. package/src/pages/chat/features/Conversation/Input/Mobile.tsx +12 -0
  46. package/src/pages/chat/features/Conversation/Input/index.tsx +20 -38
  47. package/src/pages/chat/features/SessionList/Header/Desktop.tsx +6 -13
  48. package/src/pages/chat/features/SessionList/Header/Mobile.tsx +1 -1
  49. package/src/pages/chat/features/SessionList/List/AddButton.tsx +1 -1
  50. package/src/pages/chat/features/SessionList/List/Item/index.tsx +4 -3
  51. package/src/pages/chat/features/SessionList/List/index.tsx +1 -3
  52. package/src/pages/chat/features/SessionList/Mobile.tsx +2 -12
  53. package/src/pages/chat/features/SessionSearchBar/index.tsx +26 -0
  54. package/src/pages/chat/features/Sidebar/Topic/index.tsx +18 -6
  55. package/src/pages/chat/features/Sidebar/TopicSearchBar/index.tsx +24 -0
  56. package/src/pages/chat/features/Sidebar/index.tsx +2 -9
  57. package/src/pages/market/features/AgentCard/AgentCardBanner.tsx +64 -0
  58. package/src/pages/market/features/AgentCard/AgentCardItem.tsx +57 -0
  59. package/src/pages/market/features/AgentCard/Loading.tsx +13 -0
  60. package/src/pages/market/features/AgentCard/index.tsx +37 -0
  61. package/src/pages/market/features/AgentCard/style.ts +22 -0
  62. package/src/pages/market/features/AgentSearchBar/index.tsx +26 -0
  63. package/src/pages/market/features/Header/Desktop.tsx +15 -4
  64. package/src/pages/market/features/Header/Mobile.tsx +7 -24
  65. package/src/pages/market/features/SideBar/AgentInfo/Comment.tsx +16 -0
  66. package/src/pages/market/features/SideBar/AgentInfo/Header.tsx +68 -0
  67. package/src/pages/market/features/SideBar/AgentInfo/Loading.tsx +61 -0
  68. package/src/pages/market/features/SideBar/AgentInfo/TokenTag.tsx +25 -0
  69. package/src/pages/market/features/SideBar/AgentInfo/index.tsx +68 -0
  70. package/src/pages/market/features/SideBar/AgentInfo/style.ts +46 -0
  71. package/src/pages/market/features/SideBar/Desktop.tsx +70 -0
  72. package/src/pages/market/features/SideBar/Mobile.tsx +36 -0
  73. package/src/pages/market/features/SideBar/index.tsx +23 -0
  74. package/src/pages/market/features/TagList/index.tsx +29 -0
  75. package/src/pages/market/index.page.tsx +11 -2
  76. package/src/pages/market/layout.mobile.tsx +8 -7
  77. package/src/pages/market/layout.tsx +39 -1
  78. package/src/pages/settings/features/Header/Desktop.tsx +2 -4
  79. package/src/pages/settings/features/Header/Mobile.tsx +1 -1
  80. package/src/pages/settings/features/Settings/Common.tsx +1 -2
  81. package/src/pages/settings/features/Settings/index.tsx +17 -4
  82. package/src/services/agentMarket.ts +21 -0
  83. package/src/store/global/initialState.ts +1 -2
  84. package/src/store/market/action.ts +57 -0
  85. package/src/store/market/index.ts +3 -0
  86. package/src/store/market/initialState.ts +17 -0
  87. package/src/store/market/selectors.ts +36 -0
  88. package/src/store/market/store.ts +51 -0
  89. package/src/store/session/slices/session/action.ts +5 -3
  90. package/src/store/session/slices/session/initialState.ts +2 -0
  91. package/src/store/session/slices/session/reducers/session.test.ts +4 -4
  92. package/src/types/market.ts +19 -0
  93. package/src/types/meta.ts +1 -1
  94. package/src/utils/filter.ts +1 -1
  95. package/src/utils/findDuplicates.ts +22 -0
  96. package/scripts/genDefaultLocale.ts +0 -18
  97. package/scripts/genResources.ts +0 -51
  98. package/scripts/toc.ts +0 -7
  99. package/src/components/Empty/index.tsx +0 -63
  100. package/src/components/Empty/style.ts +0 -31
  101. package/src/components/SelectWithImg/index.tsx +0 -97
  102. package/src/components/SelectWithImg/styles.ts +0 -43
  103. package/src/pages/chat/features/Conversation/Input/ActionRight.tsx +0 -31
  104. package/src/pages/chat/features/Conversation/Input/Footer.tsx +0 -49
  105. /package/src/components/{ManifestPreviewer.tsx → ManifestPreviewer/index.tsx} +0 -0
  106. /package/src/pages/chat/features/Conversation/Input/{Token.tsx → ActionBar/Token.tsx} +0 -0
@@ -0,0 +1,68 @@
1
+ import { Markdown, TabsNav } from '@lobehub/ui';
2
+ import { memo, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import TokenTag from '@/pages/market/features/SideBar/AgentInfo/TokenTag';
7
+ import { useMarketStore } from '@/store/market';
8
+
9
+ import AgentCardBanner from '../../AgentCard/AgentCardBanner';
10
+ import Comment from './Comment';
11
+ import Header from './Header';
12
+ import Loading from './Loading';
13
+ import { useStyles } from './style';
14
+
15
+ enum InfoTabs {
16
+ comment = 'comment',
17
+ prompt = 'prompt',
18
+ }
19
+
20
+ const AgentModalInner = memo(() => {
21
+ const [useFetchAgent, currentIdentifier] = useMarketStore((s) => [
22
+ s.useFetchAgent,
23
+ s.currentIdentifier,
24
+ ]);
25
+ const { styles } = useStyles();
26
+ const { data, isLoading } = useFetchAgent(currentIdentifier);
27
+ const { t } = useTranslation('market');
28
+ const [tab, setTab] = useState<string>(InfoTabs.prompt);
29
+
30
+ if (isLoading || !data?.meta) return <Loading />;
31
+
32
+ const { config, meta, identifier } = data;
33
+ const { systemRole } = config;
34
+
35
+ return (
36
+ <>
37
+ <AgentCardBanner mask meta={meta} size={10} style={{ height: 120, marginBottom: -60 }} />
38
+ <Header />
39
+ <Flexbox align={'center'}>
40
+ <TabsNav
41
+ activeKey={tab}
42
+ className={styles.nav}
43
+ items={[
44
+ {
45
+ key: InfoTabs.prompt,
46
+ label: (
47
+ <Flexbox align={'center'} gap={8} horizontal>
48
+ {t('sidebar.prompt')} <TokenTag systemRole={systemRole} />
49
+ </Flexbox>
50
+ ),
51
+ },
52
+ {
53
+ key: InfoTabs.comment,
54
+ label: t('sidebar.comment'),
55
+ },
56
+ ]}
57
+ onChange={setTab}
58
+ />
59
+ </Flexbox>
60
+ <Flexbox style={{ padding: 16 }}>
61
+ {tab === InfoTabs.prompt && <Markdown fullFeaturedCodeBlock>{systemRole}</Markdown>}
62
+ {tab === InfoTabs.comment && <Comment identifier={identifier} />}
63
+ </Flexbox>
64
+ </>
65
+ );
66
+ });
67
+
68
+ export default AgentModalInner;
@@ -0,0 +1,46 @@
1
+ import { createStyles } from 'antd-style';
2
+
3
+ export const useStyles = createStyles(({ css, token }) => ({
4
+ author: css`
5
+ font-size: 12px;
6
+ `,
7
+
8
+ avatar: css`
9
+ flex: none;
10
+ `,
11
+ container: css`
12
+ position: relative;
13
+ padding: 16px 16px 24px;
14
+ border-bottom: 1px solid ${token.colorBorderSecondary};
15
+ `,
16
+ date: css`
17
+ font-size: 12px;
18
+ color: ${token.colorTextDescription};
19
+ `,
20
+ desc: css`
21
+ color: ${token.colorTextDescription};
22
+ text-align: center;
23
+ `,
24
+ loading: css`
25
+ .ant-skeleton-content {
26
+ display: flex;
27
+ flex-direction: column;
28
+ }
29
+ `,
30
+ nav: css`
31
+ padding-top: 4px;
32
+
33
+ .ant-tabs-tab {
34
+ margin: 4px !important;
35
+
36
+ + .ant-tabs-tab {
37
+ margin: 4px !important;
38
+ }
39
+ }
40
+ `,
41
+ title: css`
42
+ font-size: 20px;
43
+ font-weight: 600;
44
+ text-align: center;
45
+ `,
46
+ }));
@@ -0,0 +1,70 @@
1
+ import { DraggablePanel, DraggablePanelBody, DraggablePanelContainer } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { ReactNode, memo, useState } from 'react';
4
+
5
+ import SafeSpacing from '@/components/SafeSpacing';
6
+ import { MARKET_SIDEBAR_WIDTH } from '@/const/layoutTokens';
7
+ import { agentMarketSelectors, useMarketStore } from '@/store/market';
8
+
9
+ const useStyles = createStyles(({ css, token, stylish }) => ({
10
+ content: css`
11
+ display: flex;
12
+ flex-direction: column;
13
+ `,
14
+ drawer: css`
15
+ background: ${token.colorBgLayout};
16
+ `,
17
+ header: css`
18
+ border-bottom: 1px solid ${token.colorBorder};
19
+ `,
20
+ noScrollbar: stylish.noScrollbar,
21
+ }));
22
+
23
+ const SideBar = memo<{ children: ReactNode }>(({ children }) => {
24
+ const { styles } = useStyles();
25
+ const [tempId, setTempId] = useState<string>('');
26
+ const [showAgentSidebar, deactivateAgent, activateAgent] = useMarketStore((s) => [
27
+ agentMarketSelectors.showSideBar(s),
28
+ s.deactivateAgent,
29
+ s.activateAgent,
30
+ ]);
31
+
32
+ return (
33
+ <DraggablePanel
34
+ className={styles.drawer}
35
+ classNames={{
36
+ content: styles.content,
37
+ }}
38
+ expand={showAgentSidebar}
39
+ minWidth={MARKET_SIDEBAR_WIDTH}
40
+ mode={'fixed'}
41
+ onExpandChange={(show) => {
42
+ if (!show) {
43
+ setTempId(useMarketStore.getState().currentIdentifier);
44
+ deactivateAgent();
45
+ } else if (tempId) {
46
+ activateAgent(tempId);
47
+ }
48
+ }}
49
+ placement={'right'}
50
+ >
51
+ <DraggablePanelContainer
52
+ style={{
53
+ flex: 'none',
54
+ height: '100vh',
55
+ minWidth: MARKET_SIDEBAR_WIDTH,
56
+ }}
57
+ >
58
+ <SafeSpacing />
59
+ <DraggablePanelBody
60
+ className={styles.noScrollbar}
61
+ style={{ padding: 0, position: 'relative' }}
62
+ >
63
+ {children}
64
+ </DraggablePanelBody>
65
+ </DraggablePanelContainer>
66
+ </DraggablePanel>
67
+ );
68
+ });
69
+
70
+ export default SideBar;
@@ -0,0 +1,36 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import { Drawer } from 'antd';
3
+ import { useTheme } from 'antd-style';
4
+ import { X } from 'lucide-react';
5
+ import { PropsWithChildren, memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ import { agentMarketSelectors, useMarketStore } from '@/store/market';
9
+
10
+ const Mobile = memo<PropsWithChildren>(({ children }) => {
11
+ const theme = useTheme();
12
+ const [showAgentSidebar, deactivateAgent] = useMarketStore((s) => [
13
+ agentMarketSelectors.showSideBar(s),
14
+ s.deactivateAgent,
15
+ ]);
16
+
17
+ const { t } = useTranslation('market');
18
+
19
+ return (
20
+ <Drawer
21
+ bodyStyle={{ padding: 0 }}
22
+ closeIcon={<ActionIcon icon={X} size={{ blockSize: 32, fontSize: 20 }} />}
23
+ drawerStyle={{ background: theme.colorBgContainer }}
24
+ headerStyle={{ padding: '8px 4px' }}
25
+ height={'75vh'}
26
+ onClose={deactivateAgent}
27
+ open={showAgentSidebar}
28
+ placement={'bottom'}
29
+ title={t('sidebar.title')}
30
+ >
31
+ {children}
32
+ </Drawer>
33
+ );
34
+ });
35
+
36
+ export default Mobile;
@@ -0,0 +1,23 @@
1
+ import { useResponsive } from 'antd-style';
2
+ import { memo } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import AgentInfo from './AgentInfo';
6
+ import Desktop from './Desktop';
7
+ import Mobile from './Mobile';
8
+
9
+ const SideBar = memo(() => {
10
+ const { mobile } = useResponsive();
11
+
12
+ const Render = mobile ? Mobile : Desktop;
13
+
14
+ return (
15
+ <Render>
16
+ <Flexbox>
17
+ <AgentInfo />
18
+ </Flexbox>
19
+ </Render>
20
+ );
21
+ });
22
+
23
+ export default SideBar;
@@ -0,0 +1,29 @@
1
+ import { Button } from 'antd';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { startCase } from 'lodash-es';
4
+ import { memo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { agentMarketSelectors, useMarketStore } from '@/store/market';
8
+
9
+ const TagList = memo(() => {
10
+ const agentTagList = useMarketStore(agentMarketSelectors.getAgentTagList, isEqual);
11
+ const keywords = useMarketStore((s) => s.searchKeywords);
12
+
13
+ return (
14
+ <Flexbox gap={8} horizontal style={{ flexWrap: 'wrap' }}>
15
+ {agentTagList.map((item) => (
16
+ <Button
17
+ key={item}
18
+ onClick={() => useMarketStore.setState({ searchKeywords: item })}
19
+ size={'small'}
20
+ type={keywords === item ? 'primary' : 'default'}
21
+ >
22
+ {startCase(item)}
23
+ </Button>
24
+ ))}
25
+ </Flexbox>
26
+ );
27
+ });
28
+
29
+ export default TagList;
@@ -1,10 +1,13 @@
1
1
  import { useResponsive } from 'antd-style';
2
2
  import Head from 'next/head';
3
- import { memo } from 'react';
3
+ import { memo, useEffect } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
+ import { useMarketStore } from '@/store/market';
6
7
  import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
7
8
 
9
+ import AgentCard from './features/AgentCard';
10
+ import AgentSearchBar from './features/AgentSearchBar';
8
11
  import DesktopLayout from './layout';
9
12
  import MobileLayout from './layout.mobile';
10
13
 
@@ -13,6 +16,11 @@ const Market = memo(() => {
13
16
  const pageTitle = genSiteHeadTitle(t('tab.market'));
14
17
  const { mobile } = useResponsive();
15
18
 
19
+ useEffect(() => {
20
+ // refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated
21
+ useMarketStore.persist.rehydrate();
22
+ }, []);
23
+
16
24
  const RenderLayout = mobile ? MobileLayout : DesktopLayout;
17
25
 
18
26
  return (
@@ -21,7 +29,8 @@ const Market = memo(() => {
21
29
  <title>{pageTitle}</title>
22
30
  </Head>
23
31
  <RenderLayout>
24
- <h1>🤯 Coming Soon</h1>
32
+ <AgentSearchBar />
33
+ <AgentCard />
25
34
  </RenderLayout>
26
35
  </>
27
36
  );
@@ -1,19 +1,20 @@
1
1
  import { ReactNode, memo } from 'react';
2
- import { Center } from 'react-layout-kit';
2
+ import { Flexbox } from 'react-layout-kit';
3
3
 
4
4
  import AppMobileLayout from '@/layout/AppMobileLayout';
5
- import { useSwitchSideBarOnInit } from '@/store/global';
5
+ import SideBar from '@/pages/market/features/SideBar';
6
6
 
7
7
  import Header from './features/Header';
8
8
 
9
- const ChatMobileLayout = memo<{ children: ReactNode }>(({ children }) => {
10
- useSwitchSideBarOnInit('chat');
11
-
9
+ const MarketLayout = memo<{ children: ReactNode }>(({ children }) => {
12
10
  return (
13
11
  <AppMobileLayout navBar={<Header />} showTabBar>
14
- <Center flex={1}>{children}</Center>
12
+ <Flexbox flex={1} gap={16} style={{ padding: 16 }}>
13
+ {children}
14
+ </Flexbox>
15
+ <SideBar />
15
16
  </AppMobileLayout>
16
17
  );
17
18
  });
18
19
 
19
- export default ChatMobileLayout;
20
+ export default MarketLayout;
@@ -1,10 +1,30 @@
1
+ import { GridBackground } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
1
3
  import { PropsWithChildren, memo } from 'react';
2
4
  import { Center, Flexbox } from 'react-layout-kit';
3
5
 
6
+ import SafeSpacing from '@/components/SafeSpacing';
7
+ import { MAX_WIDTH } from '@/const/layoutTokens';
8
+
4
9
  import AppLayout from '../../layout/AppLayout';
5
10
  import Header from './features/Header';
11
+ import SideBar from './features/SideBar';
12
+
13
+ const useStyles = createStyles(({ css }) => ({
14
+ background: css`
15
+ width: 80%;
16
+ margin: -60px 0 -20px;
17
+ `,
18
+ title: css`
19
+ z-index: 2;
20
+ margin-top: 24px;
21
+ font-size: 56px;
22
+ font-weight: 800;
23
+ `,
24
+ }));
6
25
 
7
26
  const MarketLayout = memo<PropsWithChildren>(({ children }) => {
27
+ const { theme, styles } = useStyles();
8
28
  return (
9
29
  <AppLayout>
10
30
  <Flexbox
@@ -14,7 +34,25 @@ const MarketLayout = memo<PropsWithChildren>(({ children }) => {
14
34
  style={{ position: 'relative' }}
15
35
  >
16
36
  <Header />
17
- <Center flex={1}>{children}</Center>
37
+ <Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
38
+ <Flexbox align={'center'} flex={1} style={{ overflow: 'auto', padding: 16 }}>
39
+ <SafeSpacing />
40
+
41
+ <Flexbox gap={16} style={{ maxWidth: MAX_WIDTH, position: 'relative', width: '100%' }}>
42
+ <Center>
43
+ <h1 className={styles.title}>Find & Use The Best Agents</h1>
44
+ <GridBackground
45
+ animation
46
+ className={styles.background}
47
+ colorFront={theme.colorText}
48
+ random
49
+ />
50
+ </Center>
51
+ {children}
52
+ </Flexbox>
53
+ </Flexbox>
54
+ <SideBar />
55
+ </Flexbox>
18
56
  </Flexbox>
19
57
  </AppLayout>
20
58
  );
@@ -1,17 +1,15 @@
1
1
  import { ChatHeader, ChatHeaderTitle } from '@lobehub/ui';
2
+ import Router from 'next/router';
2
3
  import { memo } from 'react';
3
4
  import { useTranslation } from 'react-i18next';
4
5
 
5
- import { useSessionStore } from '@/store/session';
6
-
7
6
  const Index = memo(() => {
8
7
  const { t } = useTranslation('setting');
9
- const [switchBackToChat] = useSessionStore((s) => [s.switchBackToChat]);
10
8
 
11
9
  return (
12
10
  <ChatHeader
13
11
  left={<ChatHeaderTitle title={t('header.global')} />}
14
- onBackClick={switchBackToChat}
12
+ onBackClick={() => Router.back()}
15
13
  showBackButton
16
14
  />
17
15
  );
@@ -9,7 +9,7 @@ const Header = memo(() => {
9
9
  return (
10
10
  <MobileNavBar
11
11
  center={<MobileNavBarTitle title={t('header.global')} />}
12
- onBackClick={() => Router.push('/chat')}
12
+ onBackClick={() => Router.back()}
13
13
  showBackButton
14
14
  />
15
15
  );
@@ -1,4 +1,4 @@
1
- import { Form, type ItemGroup, SliderWithInput } from '@lobehub/ui';
1
+ import { Form, type ItemGroup, SelectWithImg, SliderWithInput } from '@lobehub/ui';
2
2
  import { Form as AntForm, App, Button, Input, Select } from 'antd';
3
3
  import isEqual from 'fast-deep-equal';
4
4
  import { changeLanguage } from 'i18next';
@@ -7,7 +7,6 @@ import { AppWindow, Monitor, Moon, Palette, Sun } from 'lucide-react';
7
7
  import { memo, useCallback, useMemo } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
- import SelectWithImg from '@/components/SelectWithImg';
11
10
  import { FORM_STYLE } from '@/const/layoutTokens';
12
11
  import { DEFAULT_SETTINGS } from '@/const/settings';
13
12
  import AvatarWithUpload from '@/features/AvatarWithUpload';
@@ -2,7 +2,7 @@ import { GridBackground, Icon, Logo, TabsNav } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import { PackageCheck } from 'lucide-react';
4
4
  import { rgba } from 'polished';
5
- import { memo } from 'react';
5
+ import { memo, useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Center, Flexbox } from 'react-layout-kit';
8
8
 
@@ -64,6 +64,21 @@ const Settings = memo(() => {
64
64
 
65
65
  const [tab, setTab] = useGlobalStore((s) => [s.settingsTab, s.switchSettingTabs]);
66
66
 
67
+ const content = useMemo(() => {
68
+ switch (tab) {
69
+ case 'llm': {
70
+ return <LLM />;
71
+ }
72
+ case 'agent': {
73
+ return <Agent />;
74
+ }
75
+ default:
76
+ case 'common': {
77
+ return <Common />;
78
+ }
79
+ }
80
+ }, [tab]);
81
+
67
82
  return (
68
83
  <Center gap={16} width={'100%'}>
69
84
  <Flexbox align={'flex-end'} className={styles.tabs} horizontal justify={'center'}>
@@ -91,9 +106,7 @@ const Settings = memo(() => {
91
106
  <div>{`${pkg.version}`}</div>
92
107
  </Flexbox>
93
108
  </Flexbox>
94
- {tab === 'common' && <Common />}
95
- {tab === 'llm' && <LLM />}
96
- {tab === 'agent' && <Agent />}
109
+ {content}
97
110
  </Center>
98
111
  );
99
112
  });
@@ -0,0 +1,21 @@
1
+ import { getAgentIndexJSON, getAgentJSON } from '@/const/url';
2
+ import { useGlobalStore } from '@/store/global';
3
+
4
+ /**
5
+ * 请求助手列表
6
+ */
7
+ export const getAgentList = async () => {
8
+ const res = await fetch(getAgentIndexJSON(useGlobalStore.getState().settings.language));
9
+
10
+ return res.json();
11
+ };
12
+
13
+ /**
14
+ * 请求助手 manifest
15
+ */
16
+ export const getAgentManifest = async (identifier?: string) => {
17
+ if (!identifier) return;
18
+ const res = await fetch(getAgentJSON(identifier, useGlobalStore.getState().settings.language));
19
+
20
+ return res.json();
21
+ };
@@ -21,7 +21,7 @@ export interface GlobalState {
21
21
  * 用户设置
22
22
  */
23
23
  settings: GlobalSettings;
24
- settingsTab: SettingsTabs;
24
+ settingsTab?: SettingsTabs;
25
25
  sidebarKey: SidebarTabKey;
26
26
  }
27
27
 
@@ -44,6 +44,5 @@ export const initialState: GlobalState = {
44
44
  showSessionPanel: true,
45
45
  },
46
46
  settings: DEFAULT_SETTINGS,
47
- settingsTab: 'common',
48
47
  sidebarKey: 'chat',
49
48
  };
@@ -0,0 +1,57 @@
1
+ import isEqual from 'fast-deep-equal';
2
+ import { produce } from 'immer';
3
+ import useSWR, { SWRResponse } from 'swr';
4
+ import type { StateCreator } from 'zustand/vanilla';
5
+
6
+ import { getAgentList, getAgentManifest } from '@/services/agentMarket';
7
+ import { AgentsMarketItem, LobeChatAgentsMarketIndex } from '@/types/market';
8
+
9
+ import type { Store } from './store';
10
+
11
+ export interface StoreAction {
12
+ activateAgent: (identifier: string) => void;
13
+ deactivateAgent: () => void;
14
+ updateAgentMap: (key: string, value: AgentsMarketItem) => void;
15
+ useFetchAgent: (identifier: string) => SWRResponse<AgentsMarketItem>;
16
+ useFetchAgentList: () => SWRResponse<LobeChatAgentsMarketIndex>;
17
+ }
18
+
19
+ export const createMarketAction: StateCreator<
20
+ Store,
21
+ [['zustand/devtools', never]],
22
+ [],
23
+ StoreAction
24
+ > = (set, get) => ({
25
+ activateAgent: (identifier) => {
26
+ set({ currentIdentifier: identifier });
27
+ },
28
+ deactivateAgent: () => {
29
+ set({ currentIdentifier: undefined }, false, 'deactivateAgent');
30
+ },
31
+ updateAgentMap: (key, value) => {
32
+ const { agentMap } = get();
33
+
34
+ const nextAgentMap = produce(agentMap, (draft) => {
35
+ draft[key] = value;
36
+ });
37
+
38
+ if (isEqual(nextAgentMap, agentMap)) return;
39
+
40
+ set({ agentMap: nextAgentMap }, false, `setAgentMap/${key}`);
41
+ },
42
+ useFetchAgent: (identifier) =>
43
+ useSWR<AgentsMarketItem>(identifier, getAgentManifest, {
44
+ onError: () => {
45
+ get().deactivateAgent();
46
+ },
47
+ onSuccess: (data, key) => {
48
+ get().updateAgentMap(key, data);
49
+ },
50
+ }),
51
+ useFetchAgentList: () =>
52
+ useSWR<LobeChatAgentsMarketIndex>('fetchAgentList', getAgentList, {
53
+ onSuccess: (agentMarketIndex) => {
54
+ set({ agentList: agentMarketIndex.agents }, false, 'useFetchAgentList');
55
+ },
56
+ }),
57
+ });
@@ -0,0 +1,3 @@
1
+ export { agentMarketSelectors } from './selectors';
2
+ export { useMarketStore } from './store';
3
+ export { type Store } from './store';
@@ -0,0 +1,17 @@
1
+ import { AgentsMarketIndexItem, AgentsMarketItem } from '@/types/market';
2
+
3
+ export type MarketAgentMap = Record<string, AgentsMarketItem>;
4
+
5
+ export interface StoreState {
6
+ agentList: AgentsMarketIndexItem[];
7
+ agentMap: MarketAgentMap;
8
+ currentIdentifier: string;
9
+ searchKeywords: string;
10
+ }
11
+
12
+ export const initialState: StoreState = {
13
+ agentList: [],
14
+ agentMap: {},
15
+ currentIdentifier: '',
16
+ searchKeywords: '',
17
+ };
@@ -0,0 +1,36 @@
1
+ import { flatten } from 'lodash-es';
2
+
3
+ import { DEFAULT_AGENTS_MARKET_ITEM } from '@/const/market';
4
+ import { AgentsMarketItem } from '@/types/market';
5
+ import { findDuplicates } from '@/utils/findDuplicates';
6
+
7
+ import type { Store } from './store';
8
+
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);
14
+ };
15
+
16
+ const getAgentItemById = (d: string) => (s: Store) => s.agentMap[d];
17
+
18
+ const currentAgentItem = (s: Store) => getAgentItemById(s.currentIdentifier)(s);
19
+
20
+ const currentAgentItemSafe = (s: Store): AgentsMarketItem => {
21
+ const item = getAgentItemById(s.currentIdentifier)(s);
22
+ if (!item) return DEFAULT_AGENTS_MARKET_ITEM;
23
+
24
+ return item;
25
+ };
26
+
27
+ const showSideBar = (s: Store) => !!s.currentIdentifier;
28
+
29
+ export const agentMarketSelectors = {
30
+ currentAgentItem,
31
+ currentAgentItemSafe,
32
+ getAgentItemById,
33
+ getAgentList,
34
+ getAgentTagList,
35
+ showSideBar,
36
+ };