mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
feat(web): show recommendation lists in plugin market; mixed-type icons
The marketplace recommendation lists (curated rows from Space) were never mounted in the plugin market page. Wire them in: - fetch recommendation lists on mount and render them above the extension grid, only when no search/filter is active. Recommendation lists now mix plugins, MCPs and skills, so resolve each card's icon by type (plugin / mcp / skill marketplace icon URL) instead of always using the plugin icon endpoint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,8 @@ import {
|
||||
} from 'lucide-react';
|
||||
import PluginMarketCardComponent from './plugin-market-card/PluginMarketCardComponent';
|
||||
import { PluginMarketCardVO } from './plugin-market-card/PluginMarketCardVO';
|
||||
import { RecommendationLists } from './RecommendationLists';
|
||||
import type { RecommendationList } from './RecommendationLists';
|
||||
import { getCloudServiceClientSync } from '@/app/infra/http';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PluginV4, PluginV4Status } from '@/app/infra/entities/plugin';
|
||||
@@ -78,6 +80,9 @@ function MarketPageContent({
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||
const [availableTags, setAvailableTags] = useState<PluginTag[]>([]);
|
||||
const [tagNames, setTagNames] = useState<Record<string, string>>({});
|
||||
const [recommendationLists, setRecommendationLists] = useState<
|
||||
RecommendationList[]
|
||||
>([]);
|
||||
const [plugins, setPlugins] = useState<PluginMarketCardVO[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
||||
@@ -250,9 +255,21 @@ function MarketPageContent({
|
||||
useEffect(() => {
|
||||
fetchPlugins(1, false, true);
|
||||
fetchAvailableTags();
|
||||
fetchRecommendationLists();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// 获取推荐列表(精选,混合插件/MCP/Skill)
|
||||
const fetchRecommendationLists = async () => {
|
||||
try {
|
||||
const { lists } =
|
||||
await getCloudServiceClientSync().getRecommendationLists();
|
||||
setRecommendationLists(lists || []);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch recommendation lists:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取可用标签
|
||||
const fetchAvailableTags = async () => {
|
||||
try {
|
||||
@@ -701,6 +718,18 @@ function MarketPageContent({
|
||||
ref={scrollContainerRef}
|
||||
className="flex-1 overflow-y-auto px-3 sm:px-4 pb-6 container mx-auto"
|
||||
>
|
||||
{/* 推荐列表(仅在无搜索/筛选时展示,混合插件/MCP/Skill) */}
|
||||
{!searchQuery &&
|
||||
typeFilter === 'all' &&
|
||||
componentFilter === 'all' &&
|
||||
selectedTags.length === 0 && (
|
||||
<RecommendationLists
|
||||
lists={recommendationLists}
|
||||
tagNames={tagNames}
|
||||
onInstall={handleInstallPlugin}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<LoadingSpinner text={t('market.loading')} />
|
||||
|
||||
@@ -22,6 +22,15 @@ function pluginToVO(
|
||||
plugin: PluginV4,
|
||||
t: (key: string) => string,
|
||||
): PluginMarketCardVO {
|
||||
const cloudClient = getCloudServiceClientSync();
|
||||
// Recommendation lists are mixed-type; resolve the icon per extension type.
|
||||
const iconURL =
|
||||
plugin.type === 'mcp'
|
||||
? cloudClient.getMCPMarketplaceIconURL(plugin.author, plugin.name)
|
||||
: plugin.type === 'skill'
|
||||
? cloudClient.getSkillMarketplaceIconURL(plugin.author, plugin.name)
|
||||
: cloudClient.getPluginIconURL(plugin.author, plugin.name);
|
||||
|
||||
return new PluginMarketCardVO({
|
||||
pluginId: plugin.author + ' / ' + plugin.name,
|
||||
author: plugin.author,
|
||||
@@ -30,10 +39,7 @@ function pluginToVO(
|
||||
description:
|
||||
extractI18nObject(plugin.description) || t('market.noDescription'),
|
||||
installCount: plugin.install_count,
|
||||
iconURL: getCloudServiceClientSync().getPluginIconURL(
|
||||
plugin.author,
|
||||
plugin.name,
|
||||
),
|
||||
iconURL,
|
||||
githubURL: plugin.repository,
|
||||
version: plugin.latest_version,
|
||||
components: plugin.components,
|
||||
|
||||
Reference in New Issue
Block a user