mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat(community): recommended for home & added discover tab (#11290)
This commit is contained in:
@@ -152,6 +152,8 @@
|
||||
"like": "喜欢",
|
||||
"mcp.categories.all.description": "全部 MCP Servers",
|
||||
"mcp.categories.all.name": "全部",
|
||||
"mcp.categories.discover.description": "推荐和热门的 MCP 服务",
|
||||
"mcp.categories.discover.name": "发现推荐",
|
||||
"mcp.categories.business.description": "商业与企业服务",
|
||||
"mcp.categories.business.name": "商业服务",
|
||||
"mcp.categories.developer.description": "开发相关的技能与服务",
|
||||
@@ -338,6 +340,7 @@
|
||||
"mcp.hero.subTitle": "开源 & 开箱即用",
|
||||
"mcp.hero.title": "面向 AI 的开源 MCP 社区",
|
||||
"mcp.sorts.createdAt": "最近新增",
|
||||
"mcp.sorts.discover": "推荐",
|
||||
"mcp.sorts.installCount": "安装数",
|
||||
"mcp.sorts.isFeatured": "推荐技能",
|
||||
"mcp.sorts.isValidated": "已验证技能",
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
"@lobehub/desktop-ipc-typings": "workspace:*",
|
||||
"@lobehub/editor": "^3.8.0",
|
||||
"@lobehub/icons": "^4.0.2",
|
||||
"@lobehub/market-sdk": "0.28.0",
|
||||
"@lobehub/market-sdk": "0.28.1",
|
||||
"@lobehub/tts": "^4.0.2",
|
||||
"@lobehub/ui": "^4.11.6",
|
||||
"@modelcontextprotocol/sdk": "^1.25.1",
|
||||
@@ -366,7 +366,7 @@
|
||||
"@lobechat/types": "workspace:*",
|
||||
"@lobehub/i18n-cli": "^1.26.0",
|
||||
"@lobehub/lint": "^1.26.3",
|
||||
"@lobehub/market-types": "^1.11.5",
|
||||
"@lobehub/market-types": "^1.12.3",
|
||||
"@lobehub/seo-cli": "^1.7.0",
|
||||
"@next/bundle-analyzer": "^16.1.1",
|
||||
"@next/eslint-plugin-next": "^16.1.1",
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"@lobechat/python-interpreter": "workspace:*",
|
||||
"@lobechat/web-crawler": "workspace:*",
|
||||
"@lobehub/chat-plugin-sdk": "^1.32.4",
|
||||
"@lobehub/market-sdk": "0.28.0",
|
||||
"@lobehub/market-types": "^1.11.4",
|
||||
"@lobehub/market-sdk": "0.28.1",
|
||||
"@lobehub/market-types": "^1.12.3",
|
||||
"model-bank": "workspace:*",
|
||||
"type-fest": "^4.41.0",
|
||||
"zustand": "5.0.4"
|
||||
|
||||
@@ -5,6 +5,7 @@ export enum McpCategory {
|
||||
All = 'all',
|
||||
Business = 'business',
|
||||
Developer = 'developer',
|
||||
Discover = 'discover',
|
||||
GamingEntertainment = 'gaming-entertainment',
|
||||
HealthWellness = 'health-wellness',
|
||||
Lifestyle = 'lifestyle',
|
||||
@@ -26,7 +27,8 @@ export enum McpSorts {
|
||||
IsFeatured = 'isFeatured',
|
||||
IsValidated = 'isValidated',
|
||||
RatingCount = 'ratingCount',
|
||||
UpdatedAt = 'updatedAt',
|
||||
Recommended = 'recommended',
|
||||
UpdatedAt = 'updatedAt'
|
||||
}
|
||||
|
||||
export enum McpNavKey {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useDiscoverStore } from '@/store/discover';
|
||||
import { McpSorts } from '@/types/discover';
|
||||
|
||||
import Title from '../../components/Title';
|
||||
import AssistantList from '../assistant/features/List';
|
||||
@@ -23,6 +24,7 @@ const HomePage = memo(() => {
|
||||
const { data: mcpList, isLoading: pluginLoading } = useMcpList({
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
sort: McpSorts.Recommended,
|
||||
});
|
||||
|
||||
if (assistantLoading || pluginLoading || !assistantList || !mcpList) return <Loading />;
|
||||
|
||||
@@ -125,6 +125,10 @@ const SortButton = memo(() => {
|
||||
}
|
||||
case DiscoverTab.Mcp: {
|
||||
return [
|
||||
{
|
||||
key: McpSorts.Recommended,
|
||||
label: t('mcp.sorts.recommended'),
|
||||
},
|
||||
{
|
||||
key: McpSorts.IsFeatured,
|
||||
label: t('mcp.sorts.isFeatured'),
|
||||
|
||||
@@ -10,13 +10,13 @@ import { withSuspense } from '@/components/withSuspense';
|
||||
import { useCategory } from '@/hooks/useMCPCategory';
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { useDiscoverStore } from '@/store/discover';
|
||||
import { McpCategory } from '@/types/discover';
|
||||
import { McpCategory, McpSorts } from '@/types/discover';
|
||||
|
||||
import CategoryMenu from '../../../../components/CategoryMenu';
|
||||
|
||||
const Category = memo(() => {
|
||||
const useMcpCategories = useDiscoverStore((s) => s.useMcpCategories);
|
||||
const { category = 'all', q } = useQuery() as { category?: McpCategory; q?: string };
|
||||
const { category = McpCategory.Discover, q } = useQuery() as { category?: McpCategory; q?: string };
|
||||
const { data: items = [] } = useMcpCategories({ q });
|
||||
const navigate = useNavigate();
|
||||
const cates = useCategory();
|
||||
@@ -24,7 +24,11 @@ const Category = memo(() => {
|
||||
const genUrl = (key: McpCategory) =>
|
||||
qs.stringifyUrl(
|
||||
{
|
||||
query: { category: key === McpCategory.All ? null : key, q },
|
||||
query: {
|
||||
category: [McpCategory.All, McpCategory.Discover].includes(key) ? null : key,
|
||||
q,
|
||||
sort: key === McpCategory.Discover ? McpSorts.Recommended : null,
|
||||
},
|
||||
url: '/community/mcp',
|
||||
},
|
||||
{ skipNull: true },
|
||||
|
||||
@@ -5,7 +5,7 @@ import { memo } from 'react';
|
||||
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { useDiscoverStore } from '@/store/discover';
|
||||
import { DiscoverTab, type McpQueryParams } from '@/types/discover';
|
||||
import { DiscoverTab, McpSorts, type McpQueryParams } from '@/types/discover';
|
||||
|
||||
import Pagination from '../features/Pagination';
|
||||
import List from './features/List';
|
||||
@@ -20,7 +20,7 @@ const McpPage = memo(() => {
|
||||
page,
|
||||
pageSize: 21,
|
||||
q,
|
||||
sort,
|
||||
sort: sort ?? McpSorts.Recommended,
|
||||
});
|
||||
|
||||
if (isLoading || !data) return <Loading />;
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
BriefcaseIcon,
|
||||
CheckSquareIcon,
|
||||
CloudIcon,
|
||||
CompassIcon,
|
||||
CodeIcon,
|
||||
CoffeeIcon,
|
||||
DollarSignIcon,
|
||||
@@ -25,6 +26,12 @@ export const useCategory = () => {
|
||||
const { t } = useTranslation('discover');
|
||||
return useMemo(
|
||||
() => [
|
||||
{
|
||||
icon: CompassIcon,
|
||||
key: McpCategory.Discover,
|
||||
label: t('mcp.categories.discover.name'),
|
||||
title: t('mcp.categories.discover.description'),
|
||||
},
|
||||
{
|
||||
icon: LayoutPanelTopIcon,
|
||||
key: McpCategory.All,
|
||||
|
||||
@@ -165,6 +165,8 @@ export default {
|
||||
'mcp.categories.business.name': 'Business Services',
|
||||
'mcp.categories.developer.description': 'Developer-related Tools and Services',
|
||||
'mcp.categories.developer.name': 'Developer Tools',
|
||||
'mcp.categories.discover.description': 'Recommended and trending MCP servers',
|
||||
'mcp.categories.discover.name': 'Discover',
|
||||
'mcp.categories.gaming-entertainment.description': 'Games, Entertainment, and Leisure Activities',
|
||||
'mcp.categories.gaming-entertainment.name': 'Gaming & Entertainment',
|
||||
'mcp.categories.health-wellness.description': 'Health, Fitness, and Wellness',
|
||||
@@ -380,6 +382,7 @@ export default {
|
||||
'mcp.sorts.isValidated': 'Validated Skills',
|
||||
'mcp.sorts.promptsCount': 'Number of Prompts',
|
||||
'mcp.sorts.ratingCount': 'Number of Ratings',
|
||||
'mcp.sorts.recommended': 'Recommended',
|
||||
'mcp.sorts.resourcesCount': 'Number of Resources',
|
||||
'mcp.sorts.toolsCount': 'Number of Tools',
|
||||
'mcp.sorts.updatedAt': 'Recently Updated',
|
||||
|
||||
@@ -36,7 +36,7 @@ export const createMCPSlice: StateCreator<
|
||||
);
|
||||
},
|
||||
|
||||
useFetchMcpList: (params: any) => {
|
||||
useFetchMcpList: (params) => {
|
||||
const locale = globalHelpers.getCurrentLanguage();
|
||||
return useClientDataSWR(
|
||||
['mcp-list', locale, ...Object.values(params)].filter(Boolean).join('-'),
|
||||
|
||||
Reference in New Issue
Block a user