@lobehub/chat 1.141.4 → 1.141.5
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 +25 -0
- package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +41 -10
- package/apps/desktop/src/main/controllers/__tests__/BrowserWindowsCtr.test.ts +14 -7
- package/apps/desktop/src/main/core/browser/BrowserManager.ts +56 -18
- package/changelog/v1.json +9 -0
- package/package.json +3 -1
- package/packages/electron-client-ipc/src/events/index.ts +2 -0
- package/packages/electron-client-ipc/src/events/windows.ts +32 -19
- package/packages/utils/src/server/responsive.ts +1 -1
- package/playwright.config.ts +1 -1
- package/src/app/(backend)/trpc/desktop/[trpc]/route.ts +5 -0
- package/src/app/[variants]/(main)/discover/(detail)/_layout/DetailLayout.tsx +22 -0
- package/src/app/[variants]/(main)/discover/(detail)/_layout/Mobile/Header.tsx +6 -7
- package/src/app/[variants]/(main)/discover/(detail)/assistant/AssistantDetailPage.tsx +47 -0
- package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Header.tsx +10 -9
- package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Sidebar/Related/index.tsx +3 -3
- package/src/app/[variants]/(main)/discover/(detail)/components/NotFound.tsx +23 -0
- package/src/app/[variants]/(main)/discover/(detail)/features/Back.tsx +2 -2
- package/src/app/[variants]/(main)/discover/(detail)/features/Breadcrumb.tsx +3 -4
- package/src/app/[variants]/(main)/discover/(detail)/mcp/McpDetailPage.tsx +51 -0
- package/src/app/[variants]/(main)/discover/(detail)/mcp/[slug]/features/Sidebar/Related/index.tsx +3 -3
- package/src/app/[variants]/(main)/discover/(detail)/model/ModelDetailPage.tsx +44 -0
- package/src/app/[variants]/(main)/discover/(detail)/model/[...slugs]/features/Sidebar/Related/index.tsx +3 -3
- package/src/app/[variants]/(main)/discover/(detail)/provider/ProviderDetailPage.tsx +44 -0
- package/src/app/[variants]/(main)/discover/(detail)/provider/[...slugs]/features/Sidebar/ActionButton/ProviderConfig.tsx +16 -2
- package/src/app/[variants]/(main)/discover/(detail)/provider/[...slugs]/features/Sidebar/Related/index.tsx +3 -3
- package/src/app/[variants]/(main)/discover/(list)/(home)/HomePage.tsx +45 -0
- package/src/app/[variants]/(main)/discover/(list)/_layout/Desktop/Nav.tsx +7 -8
- package/src/app/[variants]/(main)/discover/(list)/_layout/ListLayout.tsx +22 -0
- package/src/app/[variants]/(main)/discover/(list)/_layout/Mobile/Nav.tsx +4 -5
- package/src/app/[variants]/(main)/discover/(list)/assistant/AssistantLayout.tsx +21 -0
- package/src/app/[variants]/(main)/discover/(list)/assistant/AssistantPage.tsx +44 -0
- package/src/app/[variants]/(main)/discover/(list)/assistant/features/Category/index.tsx +5 -6
- package/src/app/[variants]/(main)/discover/(list)/assistant/features/List/Item.tsx +8 -8
- package/src/app/[variants]/(main)/discover/(list)/features/Pagination.tsx +7 -8
- package/src/app/[variants]/(main)/discover/(list)/mcp/McpLayout.tsx +21 -0
- package/src/app/[variants]/(main)/discover/(list)/mcp/McpPage.tsx +44 -0
- package/src/app/[variants]/(main)/discover/(list)/mcp/features/Category/index.tsx +5 -6
- package/src/app/[variants]/(main)/discover/(list)/mcp/features/List/Item.tsx +12 -8
- package/src/app/[variants]/(main)/discover/(list)/model/ModelLayout.tsx +21 -0
- package/src/app/[variants]/(main)/discover/(list)/model/ModelPage.tsx +44 -0
- package/src/app/[variants]/(main)/discover/(list)/model/_layout/Desktop.tsx +1 -1
- package/src/app/[variants]/(main)/discover/(list)/model/features/Category/index.tsx +5 -6
- package/src/app/[variants]/(main)/discover/(list)/model/features/List/Item.tsx +5 -6
- package/src/app/[variants]/(main)/discover/(list)/provider/ProviderPage.tsx +43 -0
- package/src/app/[variants]/(main)/discover/(list)/provider/features/List/Item.tsx +16 -11
- package/src/app/[variants]/(main)/discover/DiscoverRouter.tsx +167 -0
- package/src/app/[variants]/(main)/discover/[[...path]]/page.tsx +11 -0
- package/src/app/[variants]/(main)/discover/_layout/Desktop/Header.tsx +2 -2
- package/src/app/[variants]/(main)/discover/_layout/DiscoverLayout.tsx +22 -0
- package/src/app/[variants]/(main)/discover/components/Title.tsx +5 -2
- package/src/app/[variants]/(main)/discover/features/Title.tsx +35 -12
- package/src/app/[variants]/(main)/discover/features/useNav.tsx +11 -12
- package/src/app/[variants]/(main)/labs/components/LabCard.tsx +6 -7
- package/src/layout/GlobalProvider/Query.tsx +7 -2
- package/vercel.json +1 -1
- package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/page.tsx +0 -110
- package/src/app/[variants]/(main)/discover/(detail)/layout.tsx +0 -12
- package/src/app/[variants]/(main)/discover/(detail)/mcp/[slug]/page.tsx +0 -103
- package/src/app/[variants]/(main)/discover/(detail)/model/[...slugs]/page.tsx +0 -104
- package/src/app/[variants]/(main)/discover/(detail)/provider/[...slugs]/page.tsx +0 -103
- package/src/app/[variants]/(main)/discover/(list)/(home)/page.tsx +0 -44
- package/src/app/[variants]/(main)/discover/(list)/assistant/layout.tsx +0 -12
- package/src/app/[variants]/(main)/discover/(list)/assistant/page.tsx +0 -46
- package/src/app/[variants]/(main)/discover/(list)/layout.tsx +0 -12
- package/src/app/[variants]/(main)/discover/(list)/mcp/layout.tsx +0 -12
- package/src/app/[variants]/(main)/discover/(list)/mcp/page.tsx +0 -46
- package/src/app/[variants]/(main)/discover/(list)/model/layout.tsx +0 -12
- package/src/app/[variants]/(main)/discover/(list)/model/page.tsx +0 -44
- package/src/app/[variants]/(main)/discover/(list)/provider/page.tsx +0 -44
- package/src/app/[variants]/(main)/discover/layout.tsx +0 -12
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
import { Pagination as Page } from 'antd';
|
|
4
4
|
import { createStyles } from 'antd-style';
|
|
5
5
|
import { memo } from 'react';
|
|
6
|
-
import
|
|
6
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
7
7
|
|
|
8
8
|
import { SCROLL_PARENT_ID } from '@/app/[variants]/(main)/discover/features/const';
|
|
9
9
|
import { useQuery } from '@/hooks/useQuery';
|
|
10
|
-
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
11
10
|
import { DiscoverTab } from '@/types/discover';
|
|
12
11
|
|
|
13
12
|
const useStyles = createStyles(({ css, token, prefixCls }) => {
|
|
@@ -36,14 +35,14 @@ interface PaginationProps {
|
|
|
36
35
|
const Pagination = memo<PaginationProps>(({ tab, currentPage, total, pageSize }) => {
|
|
37
36
|
const { styles } = useStyles();
|
|
38
37
|
const { page } = useQuery();
|
|
39
|
-
const
|
|
38
|
+
const navigate = useNavigate();
|
|
39
|
+
const location = useLocation();
|
|
40
40
|
|
|
41
41
|
const handlePageChange = (newPage: number) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
});
|
|
42
|
+
const searchParams = new URLSearchParams(location.search);
|
|
43
|
+
searchParams.set('page', String(newPage));
|
|
44
|
+
navigate(`/${tab}?${searchParams.toString()}`);
|
|
45
|
+
|
|
47
46
|
const scrollableElement = document?.querySelector(`#${SCROLL_PARENT_ID}`);
|
|
48
47
|
if (!scrollableElement) return;
|
|
49
48
|
scrollableElement.scrollTo({ behavior: 'smooth', top: 0 });
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo, PropsWithChildren } from 'react';
|
|
4
|
+
|
|
5
|
+
import Desktop from './_layout/Desktop';
|
|
6
|
+
import Mobile from './_layout/Mobile';
|
|
7
|
+
|
|
8
|
+
interface McpLayoutProps extends PropsWithChildren {
|
|
9
|
+
mobile?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const McpLayout = memo<McpLayoutProps>(({ children, mobile }) => {
|
|
13
|
+
if (mobile) {
|
|
14
|
+
return <Mobile>{children}</Mobile>;
|
|
15
|
+
}
|
|
16
|
+
return <Desktop>{children}</Desktop>;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
McpLayout.displayName = 'McpLayout';
|
|
20
|
+
|
|
21
|
+
export default McpLayout;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { withSuspense } from '@/components/withSuspense';
|
|
7
|
+
import { useQuery } from '@/hooks/useQuery';
|
|
8
|
+
import { useDiscoverStore } from '@/store/discover';
|
|
9
|
+
import { DiscoverTab, McpQueryParams } from '@/types/discover';
|
|
10
|
+
|
|
11
|
+
import Pagination from '../features/Pagination';
|
|
12
|
+
import List from './features/List';
|
|
13
|
+
import Loading from './loading';
|
|
14
|
+
|
|
15
|
+
const McpPage = memo<{ mobile?: boolean }>(() => {
|
|
16
|
+
const { q, page, category, sort, order } = useQuery() as McpQueryParams;
|
|
17
|
+
const useMcpList = useDiscoverStore((s) => s.useFetchMcpList);
|
|
18
|
+
const { data, isLoading } = useMcpList({
|
|
19
|
+
category,
|
|
20
|
+
order,
|
|
21
|
+
page,
|
|
22
|
+
pageSize: 21,
|
|
23
|
+
q,
|
|
24
|
+
sort,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (isLoading || !data) return <Loading />;
|
|
28
|
+
|
|
29
|
+
const { items, currentPage, pageSize, totalCount } = data;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Flexbox gap={32} width={'100%'}>
|
|
33
|
+
<List data={items} />
|
|
34
|
+
<Pagination
|
|
35
|
+
currentPage={currentPage}
|
|
36
|
+
pageSize={pageSize}
|
|
37
|
+
tab={DiscoverTab.Mcp}
|
|
38
|
+
total={totalCount}
|
|
39
|
+
/>
|
|
40
|
+
</Flexbox>
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export default withSuspense(McpPage);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Icon, Tag } from '@lobehub/ui';
|
|
4
|
-
import Link from '
|
|
5
|
-
import { useRouter } from 'nextjs-toploader/app';
|
|
4
|
+
import { Link, useNavigate } from 'react-router-dom';
|
|
6
5
|
import qs from 'query-string';
|
|
7
6
|
import { memo, useMemo } from 'react';
|
|
8
7
|
|
|
@@ -19,20 +18,20 @@ const Category = memo(() => {
|
|
|
19
18
|
const useMcpCategories = useDiscoverStore((s) => s.useMcpCategories);
|
|
20
19
|
const { category = 'all', q } = useQuery() as { category?: McpCategory; q?: string };
|
|
21
20
|
const { data: items = [] } = useMcpCategories({ q });
|
|
22
|
-
const
|
|
21
|
+
const navigate = useNavigate();
|
|
23
22
|
const cates = useCategory();
|
|
24
23
|
|
|
25
24
|
const genUrl = (key: McpCategory) =>
|
|
26
25
|
qs.stringifyUrl(
|
|
27
26
|
{
|
|
28
27
|
query: { category: key === McpCategory.All ? null : key, q },
|
|
29
|
-
url: '/
|
|
28
|
+
url: '/mcp',
|
|
30
29
|
},
|
|
31
30
|
{ skipNull: true },
|
|
32
31
|
);
|
|
33
32
|
|
|
34
33
|
const handleClick = (key: McpCategory) => {
|
|
35
|
-
|
|
34
|
+
navigate(genUrl(key));
|
|
36
35
|
const scrollableElement = document?.querySelector(`#${SCROLL_PARENT_ID}`);
|
|
37
36
|
if (!scrollableElement) return;
|
|
38
37
|
scrollableElement.scrollTo({ behavior: 'smooth', top: 0 });
|
|
@@ -70,7 +69,7 @@ const Category = memo(() => {
|
|
|
70
69
|
),
|
|
71
70
|
...item,
|
|
72
71
|
icon: <Icon icon={item.icon} size={18} />,
|
|
73
|
-
label: <Link
|
|
72
|
+
label: <Link to={genUrl(item.key)}>{item.label}</Link>,
|
|
74
73
|
};
|
|
75
74
|
})}
|
|
76
75
|
mode={'inline'}
|
|
@@ -5,11 +5,10 @@ import { ActionIcon, Avatar, Block, Icon, Tag, Text, Tooltip } from '@lobehub/ui
|
|
|
5
5
|
import { Spotlight } from '@lobehub/ui/awesome';
|
|
6
6
|
import { createStyles } from 'antd-style';
|
|
7
7
|
import { ClockIcon } from 'lucide-react';
|
|
8
|
-
import Link from 'next/link';
|
|
9
|
-
import { useRouter } from 'nextjs-toploader/app';
|
|
10
8
|
import { memo } from 'react';
|
|
11
9
|
import { useTranslation } from 'react-i18next';
|
|
12
10
|
import { Flexbox } from 'react-layout-kit';
|
|
11
|
+
import { Link, useNavigate } from 'react-router-dom';
|
|
13
12
|
import urlJoin from 'url-join';
|
|
14
13
|
|
|
15
14
|
import InstallationIcon from '@/components/MCPDepsIcon';
|
|
@@ -78,14 +77,14 @@ const McpItem = memo<DiscoverMcpItem>(
|
|
|
78
77
|
}) => {
|
|
79
78
|
const { t } = useTranslation('discover');
|
|
80
79
|
const { styles, theme } = useStyles();
|
|
81
|
-
const
|
|
82
|
-
const link = urlJoin('/
|
|
80
|
+
const navigate = useNavigate();
|
|
81
|
+
const link = urlJoin('/mcp', identifier);
|
|
83
82
|
return (
|
|
84
83
|
<Block
|
|
85
84
|
clickable
|
|
86
85
|
height={'100%'}
|
|
87
86
|
onClick={() => {
|
|
88
|
-
|
|
87
|
+
navigate(link);
|
|
89
88
|
}}
|
|
90
89
|
style={{
|
|
91
90
|
overflow: 'hidden',
|
|
@@ -128,7 +127,7 @@ const McpItem = memo<DiscoverMcpItem>(
|
|
|
128
127
|
overflow: 'hidden',
|
|
129
128
|
}}
|
|
130
129
|
>
|
|
131
|
-
<Link
|
|
130
|
+
<Link style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
|
132
131
|
<Text as={'h2'} className={styles.title} ellipsis>
|
|
133
132
|
{name}
|
|
134
133
|
</Text>
|
|
@@ -145,9 +144,14 @@ const McpItem = memo<DiscoverMcpItem>(
|
|
|
145
144
|
<Flexbox align={'center'} gap={4} horizontal>
|
|
146
145
|
{installationMethods && <InstallationIcon type={installationMethods} />}
|
|
147
146
|
{github && (
|
|
148
|
-
<
|
|
147
|
+
<a
|
|
148
|
+
href={github.url}
|
|
149
|
+
onClick={(e) => e.stopPropagation()}
|
|
150
|
+
rel="noopener noreferrer"
|
|
151
|
+
target={'_blank'}
|
|
152
|
+
>
|
|
149
153
|
<ActionIcon fill={theme.colorTextDescription} icon={Github} />
|
|
150
|
-
</
|
|
154
|
+
</a>
|
|
151
155
|
)}
|
|
152
156
|
</Flexbox>
|
|
153
157
|
</Flexbox>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo, PropsWithChildren } from 'react';
|
|
4
|
+
|
|
5
|
+
import Desktop from './_layout/Desktop';
|
|
6
|
+
import Mobile from './_layout/Mobile';
|
|
7
|
+
|
|
8
|
+
interface ModelLayoutProps extends PropsWithChildren {
|
|
9
|
+
mobile?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ModelLayout = memo<ModelLayoutProps>(({ children, mobile }) => {
|
|
13
|
+
if (mobile) {
|
|
14
|
+
return <Mobile>{children}</Mobile>;
|
|
15
|
+
}
|
|
16
|
+
return <Desktop>{children}</Desktop>;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
ModelLayout.displayName = 'ModelLayout';
|
|
20
|
+
|
|
21
|
+
export default ModelLayout;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { withSuspense } from '@/components/withSuspense';
|
|
7
|
+
import { useQuery } from '@/hooks/useQuery';
|
|
8
|
+
import { useDiscoverStore } from '@/store/discover';
|
|
9
|
+
import { DiscoverTab, ModelQueryParams } from '@/types/discover';
|
|
10
|
+
|
|
11
|
+
import Pagination from '../features/Pagination';
|
|
12
|
+
import List from './features/List';
|
|
13
|
+
import Loading from './loading';
|
|
14
|
+
|
|
15
|
+
const ModelPage = memo<{ mobile?: boolean }>(() => {
|
|
16
|
+
const { q, page, category, sort, order } = useQuery() as ModelQueryParams;
|
|
17
|
+
const useModelList = useDiscoverStore((s) => s.useModelList);
|
|
18
|
+
const { data, isLoading } = useModelList({
|
|
19
|
+
category,
|
|
20
|
+
order,
|
|
21
|
+
page,
|
|
22
|
+
pageSize: 21,
|
|
23
|
+
q,
|
|
24
|
+
sort,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (isLoading || !data) return <Loading />;
|
|
28
|
+
|
|
29
|
+
const { items, currentPage, pageSize, totalCount } = data;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Flexbox gap={32} width={'100%'}>
|
|
33
|
+
<List data={items} />
|
|
34
|
+
<Pagination
|
|
35
|
+
currentPage={currentPage}
|
|
36
|
+
pageSize={pageSize}
|
|
37
|
+
tab={DiscoverTab.Models}
|
|
38
|
+
total={totalCount}
|
|
39
|
+
/>
|
|
40
|
+
</Flexbox>
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export default withSuspense(ModelPage);
|
|
@@ -4,7 +4,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
4
4
|
import CategoryContainer from '../../../components/CategoryContainer';
|
|
5
5
|
import Category from '../features/Category';
|
|
6
6
|
|
|
7
|
-
const Layout =
|
|
7
|
+
const Layout = ({ children }: PropsWithChildren) => {
|
|
8
8
|
return (
|
|
9
9
|
<Flexbox gap={24} horizontal style={{ position: 'relative' }} width={'100%'}>
|
|
10
10
|
<CategoryContainer>
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Icon, Tag } from '@lobehub/ui';
|
|
4
|
-
import Link from '
|
|
5
|
-
import { useRouter } from 'nextjs-toploader/app';
|
|
4
|
+
import { Link, useNavigate } from 'react-router-dom';
|
|
6
5
|
import qs from 'query-string';
|
|
7
6
|
import { memo, useMemo } from 'react';
|
|
8
7
|
|
|
@@ -18,20 +17,20 @@ const Category = memo(() => {
|
|
|
18
17
|
const useModelCategories = useDiscoverStore((s) => s.useModelCategories);
|
|
19
18
|
const { category = 'all', q } = useQuery() as { category?: string; q?: string };
|
|
20
19
|
const { data: items = [] } = useModelCategories({ q });
|
|
21
|
-
const
|
|
20
|
+
const navigate = useNavigate();
|
|
22
21
|
const cates = useCategory();
|
|
23
22
|
|
|
24
23
|
const genUrl = (key: string) =>
|
|
25
24
|
qs.stringifyUrl(
|
|
26
25
|
{
|
|
27
26
|
query: { category: key === 'all' ? null : key, q },
|
|
28
|
-
url: '/
|
|
27
|
+
url: '/model',
|
|
29
28
|
},
|
|
30
29
|
{ skipNull: true },
|
|
31
30
|
);
|
|
32
31
|
|
|
33
32
|
const handleClick = (key: string) => {
|
|
34
|
-
|
|
33
|
+
navigate(genUrl(key));
|
|
35
34
|
const scrollableElement = document?.querySelector(`#${SCROLL_PARENT_ID}`);
|
|
36
35
|
if (!scrollableElement) return;
|
|
37
36
|
scrollableElement.scrollTo({ behavior: 'smooth', top: 0 });
|
|
@@ -69,7 +68,7 @@ const Category = memo(() => {
|
|
|
69
68
|
),
|
|
70
69
|
...item,
|
|
71
70
|
icon: <Icon icon={item.icon} size={18} />,
|
|
72
|
-
label: <Link
|
|
71
|
+
label: <Link to={genUrl(item.key)}>{item.label}</Link>,
|
|
73
72
|
};
|
|
74
73
|
})}
|
|
75
74
|
mode={'inline'}
|
|
@@ -6,11 +6,10 @@ import { Popover } from 'antd';
|
|
|
6
6
|
import { createStyles } from 'antd-style';
|
|
7
7
|
import dayjs from 'dayjs';
|
|
8
8
|
import { ClockIcon } from 'lucide-react';
|
|
9
|
-
import Link from 'next/link';
|
|
10
|
-
import { useRouter } from 'nextjs-toploader/app';
|
|
11
9
|
import { memo } from 'react';
|
|
12
10
|
import { useTranslation } from 'react-i18next';
|
|
13
11
|
import { Flexbox } from 'react-layout-kit';
|
|
12
|
+
import { Link, useNavigate } from 'react-router-dom';
|
|
14
13
|
import urlJoin from 'url-join';
|
|
15
14
|
|
|
16
15
|
import { ModelInfoTags } from '@/components/ModelSelect';
|
|
@@ -57,14 +56,14 @@ const ModelItem = memo<DiscoverModelItem>(
|
|
|
57
56
|
({ identifier, displayName, contextWindowTokens, releasedAt, type, abilities, providers }) => {
|
|
58
57
|
const { t } = useTranslation(['models', 'discover']);
|
|
59
58
|
const { styles } = useStyles();
|
|
60
|
-
const
|
|
61
|
-
const link = urlJoin('/
|
|
59
|
+
const navigate = useNavigate();
|
|
60
|
+
const link = urlJoin('/model', identifier);
|
|
62
61
|
return (
|
|
63
62
|
<Block
|
|
64
63
|
clickable
|
|
65
64
|
height={'100%'}
|
|
66
65
|
onClick={() => {
|
|
67
|
-
|
|
66
|
+
navigate(link);
|
|
68
67
|
}}
|
|
69
68
|
style={{
|
|
70
69
|
overflow: 'hidden',
|
|
@@ -106,7 +105,7 @@ const ModelItem = memo<DiscoverModelItem>(
|
|
|
106
105
|
overflow: 'hidden',
|
|
107
106
|
}}
|
|
108
107
|
>
|
|
109
|
-
<Link
|
|
108
|
+
<Link style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
|
110
109
|
<Text as={'h2'} className={styles.title} ellipsis>
|
|
111
110
|
{displayName}
|
|
112
111
|
</Text>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { withSuspense } from '@/components/withSuspense';
|
|
7
|
+
import { useQuery } from '@/hooks/useQuery';
|
|
8
|
+
import { useDiscoverStore } from '@/store/discover';
|
|
9
|
+
import { DiscoverTab, ProviderQueryParams } from '@/types/discover';
|
|
10
|
+
|
|
11
|
+
import Pagination from '../features/Pagination';
|
|
12
|
+
import List from './features/List';
|
|
13
|
+
import Loading from './loading';
|
|
14
|
+
|
|
15
|
+
const ProviderPage = memo<{ mobile?: boolean }>(() => {
|
|
16
|
+
const { q, page, sort, order } = useQuery() as ProviderQueryParams;
|
|
17
|
+
const useProviderList = useDiscoverStore((s) => s.useProviderList);
|
|
18
|
+
const { data, isLoading } = useProviderList({
|
|
19
|
+
order,
|
|
20
|
+
page,
|
|
21
|
+
pageSize: 21,
|
|
22
|
+
q,
|
|
23
|
+
sort,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (isLoading || !data) return <Loading />;
|
|
27
|
+
|
|
28
|
+
const { items, currentPage, pageSize, totalCount } = data;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Flexbox gap={32} width={'100%'}>
|
|
32
|
+
<List data={items} />
|
|
33
|
+
<Pagination
|
|
34
|
+
currentPage={currentPage}
|
|
35
|
+
pageSize={pageSize}
|
|
36
|
+
tab={DiscoverTab.Providers}
|
|
37
|
+
total={totalCount}
|
|
38
|
+
/>
|
|
39
|
+
</Flexbox>
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export default withSuspense(ProviderPage);
|
|
@@ -2,11 +2,10 @@ import { Github, ModelTag, ProviderCombine } from '@lobehub/icons';
|
|
|
2
2
|
import { ActionIcon, Block, MaskShadow, Text } from '@lobehub/ui';
|
|
3
3
|
import { createStyles } from 'antd-style';
|
|
4
4
|
import { GlobeIcon } from 'lucide-react';
|
|
5
|
-
import Link from 'next/link';
|
|
6
|
-
import { useRouter } from 'nextjs-toploader/app';
|
|
7
5
|
import { memo } from 'react';
|
|
8
6
|
import { useTranslation } from 'react-i18next';
|
|
9
7
|
import { Flexbox } from 'react-layout-kit';
|
|
8
|
+
import { Link, useNavigate } from 'react-router-dom';
|
|
10
9
|
import urlJoin from 'url-join';
|
|
11
10
|
|
|
12
11
|
import { DiscoverProviderItem } from '@/types/discover';
|
|
@@ -48,8 +47,8 @@ const useStyles = createStyles(({ css, token }) => {
|
|
|
48
47
|
const ProviderItem = memo<DiscoverProviderItem>(
|
|
49
48
|
({ url, name, description, identifier, models }) => {
|
|
50
49
|
const { styles, theme } = useStyles();
|
|
51
|
-
const
|
|
52
|
-
const link = urlJoin('/
|
|
50
|
+
const navigate = useNavigate();
|
|
51
|
+
const link = urlJoin('/provider', identifier);
|
|
53
52
|
const { t } = useTranslation(['discover', 'providers']);
|
|
54
53
|
|
|
55
54
|
return (
|
|
@@ -57,7 +56,7 @@ const ProviderItem = memo<DiscoverProviderItem>(
|
|
|
57
56
|
clickable
|
|
58
57
|
height={'100%'}
|
|
59
58
|
onClick={() => {
|
|
60
|
-
|
|
59
|
+
navigate(link);
|
|
61
60
|
}}
|
|
62
61
|
style={{
|
|
63
62
|
overflow: 'hidden',
|
|
@@ -80,22 +79,28 @@ const ProviderItem = memo<DiscoverProviderItem>(
|
|
|
80
79
|
}}
|
|
81
80
|
title={identifier}
|
|
82
81
|
>
|
|
83
|
-
<Link
|
|
82
|
+
<Link style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
|
84
83
|
<ProviderCombine provider={identifier} size={28} style={{ flex: 'none' }} />
|
|
85
84
|
</Link>
|
|
86
85
|
<div className={styles.author}>@{name}</div>
|
|
87
86
|
</Flexbox>
|
|
88
87
|
<Flexbox align={'center'} horizontal>
|
|
89
|
-
<
|
|
88
|
+
<a
|
|
89
|
+
href={url}
|
|
90
|
+
onClick={(e) => e.stopPropagation()}
|
|
91
|
+
rel="noopener noreferrer"
|
|
92
|
+
target={'_blank'}
|
|
93
|
+
>
|
|
90
94
|
<ActionIcon color={theme.colorTextDescription} icon={GlobeIcon} />
|
|
91
|
-
</
|
|
92
|
-
<
|
|
95
|
+
</a>
|
|
96
|
+
<a
|
|
93
97
|
href={`https://github.com/lobehub/lobe-chat/blob/main/src/config/modelProviders/${identifier}.ts`}
|
|
94
98
|
onClick={(e) => e.stopPropagation()}
|
|
99
|
+
rel="noopener noreferrer"
|
|
95
100
|
target={'_blank'}
|
|
96
101
|
>
|
|
97
102
|
<ActionIcon fill={theme.colorTextDescription} icon={Github} />
|
|
98
|
-
</
|
|
103
|
+
</a>
|
|
99
104
|
</Flexbox>
|
|
100
105
|
</Flexbox>
|
|
101
106
|
<Flexbox flex={1} gap={12} paddingInline={16}>
|
|
@@ -122,7 +127,7 @@ const ProviderItem = memo<DiscoverProviderItem>(
|
|
|
122
127
|
.slice(0, 6)
|
|
123
128
|
.filter(Boolean)
|
|
124
129
|
.map((tag: string) => (
|
|
125
|
-
<Link
|
|
130
|
+
<Link key={tag} to={urlJoin('/model', tag)}>
|
|
126
131
|
<ModelTag model={tag} style={{ margin: 0 }} />
|
|
127
132
|
</Link>
|
|
128
133
|
))}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo, useEffect } from 'react';
|
|
4
|
+
import { MemoryRouter, Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
|
5
|
+
import { useMediaQuery } from 'react-responsive';
|
|
6
|
+
|
|
7
|
+
import DiscoverLayout from './_layout/DiscoverLayout';
|
|
8
|
+
import ListLayout from './(list)/_layout/ListLayout';
|
|
9
|
+
import DetailLayout from './(detail)/_layout/DetailLayout';
|
|
10
|
+
import HomePage from './(list)/(home)/HomePage';
|
|
11
|
+
import AssistantPage from './(list)/assistant/AssistantPage';
|
|
12
|
+
import AssistantLayout from './(list)/assistant/AssistantLayout';
|
|
13
|
+
import McpPage from './(list)/mcp/McpPage';
|
|
14
|
+
import McpLayout from './(list)/mcp/McpLayout';
|
|
15
|
+
import ModelPage from './(list)/model/ModelPage';
|
|
16
|
+
import ModelLayout from './(list)/model/ModelLayout';
|
|
17
|
+
import ProviderPage from './(list)/provider/ProviderPage';
|
|
18
|
+
import AssistantDetailPage from './(detail)/assistant/AssistantDetailPage';
|
|
19
|
+
import McpDetailPage from './(detail)/mcp/McpDetailPage';
|
|
20
|
+
import ModelDetailPage from './(detail)/model/ModelDetailPage';
|
|
21
|
+
import ProviderDetailPage from './(detail)/provider/ProviderDetailPage';
|
|
22
|
+
|
|
23
|
+
// Get initial path from URL
|
|
24
|
+
const getInitialPath = () => {
|
|
25
|
+
if (typeof window === 'undefined') return '/';
|
|
26
|
+
const fullPath = window.location.pathname;
|
|
27
|
+
const searchParams = window.location.search;
|
|
28
|
+
const discoverIndex = fullPath.indexOf('/discover');
|
|
29
|
+
|
|
30
|
+
if (discoverIndex !== -1) {
|
|
31
|
+
const pathAfterDiscover = fullPath.slice(discoverIndex + '/discover'.length) || '/';
|
|
32
|
+
return pathAfterDiscover + searchParams;
|
|
33
|
+
}
|
|
34
|
+
return '/';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Helper component to sync URL with MemoryRouter
|
|
38
|
+
const UrlSynchronizer = () => {
|
|
39
|
+
const location = useLocation();
|
|
40
|
+
const navigate = useNavigate();
|
|
41
|
+
|
|
42
|
+
// Sync initial URL
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const fullPath = window.location.pathname;
|
|
45
|
+
const searchParams = window.location.search;
|
|
46
|
+
const discoverIndex = fullPath.indexOf('/discover');
|
|
47
|
+
|
|
48
|
+
if (discoverIndex !== -1) {
|
|
49
|
+
const pathAfterDiscover = fullPath.slice(discoverIndex + '/discover'.length) || '/';
|
|
50
|
+
const targetPath = pathAfterDiscover + searchParams;
|
|
51
|
+
|
|
52
|
+
if (location.pathname + location.search !== targetPath) {
|
|
53
|
+
navigate(targetPath, { replace: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
// Update browser URL when location changes
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
const newUrl = `/discover${location.pathname}${location.search}`;
|
|
61
|
+
if (window.location.pathname + window.location.search !== newUrl) {
|
|
62
|
+
window.history.replaceState({}, '', newUrl);
|
|
63
|
+
}
|
|
64
|
+
}, [location.pathname, location.search]);
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const DiscoverRouter = memo(() => {
|
|
70
|
+
const mobile = useMediaQuery({ maxWidth: 768 });
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<MemoryRouter initialEntries={[getInitialPath()]} initialIndex={0}>
|
|
74
|
+
<UrlSynchronizer />
|
|
75
|
+
<DiscoverLayout mobile={mobile}>
|
|
76
|
+
<Routes>
|
|
77
|
+
{/* List routes with ListLayout */}
|
|
78
|
+
<Route
|
|
79
|
+
element={
|
|
80
|
+
<ListLayout mobile={mobile}>
|
|
81
|
+
<HomePage mobile={mobile} />
|
|
82
|
+
</ListLayout>
|
|
83
|
+
}
|
|
84
|
+
path="/"
|
|
85
|
+
/>
|
|
86
|
+
<Route
|
|
87
|
+
element={
|
|
88
|
+
<ListLayout mobile={mobile}>
|
|
89
|
+
<AssistantLayout mobile={mobile}>
|
|
90
|
+
<AssistantPage mobile={mobile} />
|
|
91
|
+
</AssistantLayout>
|
|
92
|
+
</ListLayout>
|
|
93
|
+
}
|
|
94
|
+
path="/assistant"
|
|
95
|
+
/>
|
|
96
|
+
<Route
|
|
97
|
+
element={
|
|
98
|
+
<ListLayout mobile={mobile}>
|
|
99
|
+
<ModelLayout mobile={mobile}>
|
|
100
|
+
<ModelPage mobile={mobile} />
|
|
101
|
+
</ModelLayout>
|
|
102
|
+
</ListLayout>
|
|
103
|
+
}
|
|
104
|
+
path="/model"
|
|
105
|
+
/>
|
|
106
|
+
<Route
|
|
107
|
+
element={
|
|
108
|
+
<ListLayout mobile={mobile}>
|
|
109
|
+
<ProviderPage mobile={mobile} />
|
|
110
|
+
</ListLayout>
|
|
111
|
+
}
|
|
112
|
+
path="/provider"
|
|
113
|
+
/>
|
|
114
|
+
<Route
|
|
115
|
+
element={
|
|
116
|
+
<ListLayout mobile={mobile}>
|
|
117
|
+
<McpLayout mobile={mobile}>
|
|
118
|
+
<McpPage mobile={mobile} />
|
|
119
|
+
</McpLayout>
|
|
120
|
+
</ListLayout>
|
|
121
|
+
}
|
|
122
|
+
path="/mcp"
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
{/* Detail routes with DetailLayout */}
|
|
126
|
+
<Route
|
|
127
|
+
element={
|
|
128
|
+
<DetailLayout mobile={mobile}>
|
|
129
|
+
<AssistantDetailPage mobile={mobile} />
|
|
130
|
+
</DetailLayout>
|
|
131
|
+
}
|
|
132
|
+
path="/assistant/*"
|
|
133
|
+
/>
|
|
134
|
+
<Route
|
|
135
|
+
element={
|
|
136
|
+
<DetailLayout mobile={mobile}>
|
|
137
|
+
<ModelDetailPage mobile={mobile} />
|
|
138
|
+
</DetailLayout>
|
|
139
|
+
}
|
|
140
|
+
path="/model/*"
|
|
141
|
+
/>
|
|
142
|
+
<Route
|
|
143
|
+
element={
|
|
144
|
+
<DetailLayout mobile={mobile}>
|
|
145
|
+
<ProviderDetailPage mobile={mobile} />
|
|
146
|
+
</DetailLayout>
|
|
147
|
+
}
|
|
148
|
+
path="/provider/*"
|
|
149
|
+
/>
|
|
150
|
+
<Route
|
|
151
|
+
element={
|
|
152
|
+
<DetailLayout mobile={mobile}>
|
|
153
|
+
<McpDetailPage mobile={mobile} />
|
|
154
|
+
</DetailLayout>
|
|
155
|
+
}
|
|
156
|
+
path="/mcp/*"
|
|
157
|
+
/>
|
|
158
|
+
|
|
159
|
+
{/* Fallback */}
|
|
160
|
+
<Route element={<Navigate replace to="/" />} path="*" />
|
|
161
|
+
</Routes>
|
|
162
|
+
</DiscoverLayout>
|
|
163
|
+
</MemoryRouter>
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
export default DiscoverRouter;
|