feat: youhua qianduan

This commit is contained in:
WangCham
2026-05-09 16:47:23 +08:00
parent fffc862fe6
commit f3d45eeeab
2 changed files with 105 additions and 92 deletions
+53 -51
View File
@@ -116,61 +116,63 @@ function AddExtensionContent() {
}); });
} }
const extensionActions = (
<>
<Button
variant="default"
className="px-4 py-2 cursor-pointer"
onClick={() => navigate('/home/mcp?id=new')}
>
<PlusIcon className="w-4 h-4" />
{t('mcp.addMCPServer')}
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="default" className="px-4 py-2 cursor-pointer">
<PlusIcon className="w-4 h-4" />
{t('skills.addSkill')}
<ChevronDownIcon className="ml-2 w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => navigate('/home/skills?action=create')}>
{t('skills.createManually')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate('/home/skills?action=upload')}>
{t('skills.uploadZip')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate('/home/skills?action=github')}>
{t('skills.importFromGithub')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="default" className="px-4 py-2 cursor-pointer">
<PlusIcon className="w-4 h-4" />
{t('plugins.newPlugin')}
<ChevronDownIcon className="ml-2 w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => navigate('/home/add-plugin?action=github')}>
{t('plugins.installFromGithub')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate('/home/add-plugin?action=upload')}>
{t('plugins.uploadLocal')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
);
return ( return (
<> <>
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div className="flex flex-row justify-end items-center px-[0.8rem] pb-4 flex-shrink-0 gap-2">
<Button
variant="default"
className="px-6 py-4 cursor-pointer"
onClick={() => navigate('/home/mcp?id=new')}
>
<PlusIcon className="w-4 h-4" />
{t('mcp.addMCPServer')}
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="default" className="px-6 py-4 cursor-pointer">
<PlusIcon className="w-4 h-4" />
{t('skills.addSkill')}
<ChevronDownIcon className="ml-2 w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => navigate('/home/skills?action=create')}>
{t('skills.createManually')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate('/home/skills?action=upload')}>
{t('skills.uploadZip')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate('/home/skills?action=github')}>
{t('skills.importFromGithub')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="default" className="px-6 py-4 cursor-pointer">
<PlusIcon className="w-4 h-4" />
{t('plugins.newPlugin')}
<ChevronDownIcon className="ml-2 w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => navigate('/home/add-plugin?action=github')}>
{t('plugins.installFromGithub')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate('/home/add-plugin?action=upload')}>
{t('plugins.uploadLocal')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
<MarketPage installPlugin={handleInstallPlugin} /> <MarketPage installPlugin={handleInstallPlugin} headerActions={extensionActions} />
</div> </div>
</div> </div>
@@ -50,8 +50,10 @@ interface SortOption {
// 内部组件,用于处理搜索参数 // 内部组件,用于处理搜索参数
function MarketPageContent({ function MarketPageContent({
installPlugin, installPlugin,
headerActions,
}: { }: {
installPlugin: (plugin: PluginV4) => void; installPlugin: (plugin: PluginV4) => void;
headerActions?: React.ReactNode;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
@@ -572,9 +574,9 @@ function MarketPageContent({
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
{/* Fixed header with search and sort controls */} {/* Fixed header with search and sort controls */}
<div className="flex-shrink-0 space-y-4 px-3 sm:px-4 py-4 sm:py-6"> <div className="flex-shrink-0 space-y-4 px-3 sm:px-4 py-4 sm:py-6">
{/* Search box */} {/* Search box and actions */}
<div className="flex flex-col lg:flex-row items-stretch lg:items-center justify-center gap-3"> <div className="flex flex-col lg:flex-row items-stretch lg:items-center justify-center gap-3">
<div className="relative w-full lg:max-w-xl"> <div className="relative flex-1 lg:max-w-xl">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
<Input <Input
placeholder={t('market.searchPlaceholder')} placeholder={t('market.searchPlaceholder')}
@@ -598,13 +600,17 @@ function MarketPageContent({
className="pl-10 pr-4 text-sm sm:text-base" className="pl-10 pr-4 text-sm sm:text-base"
/> />
</div> </div>
{headerActions && (
<div className="flex items-center gap-2 flex-shrink-0">
{headerActions}
</div>
)}
</div> </div>
{/* Sort and more filters */} {/* Sort, filters and tags in one row */}
<div className="flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-4 px-3 sm:px-4"> <div className="flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-4 px-0">
{/* Sort dropdown */} {/* Sort dropdown */}
<div className="flex items-center gap-2 sm:gap-3"> <div className="flex items-center gap-2 sm:gap-3 flex-shrink-0">
<span className="text-xs sm:text-sm text-muted-foreground whitespace-nowrap"> <span className="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">
{t('market.sortBy')}: {t('market.sortBy')}:
</span> </span>
@@ -623,7 +629,7 @@ function MarketPageContent({
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button variant="outline" className="relative"> <Button variant="outline" className="relative flex-shrink-0">
<SlidersHorizontal className="h-4 w-4" /> <SlidersHorizontal className="h-4 w-4" />
<span className="hidden sm:inline">{t('market.filters.more')}</span> <span className="hidden sm:inline">{t('market.filters.more')}</span>
{activeAdvancedFilters > 0 && ( {activeAdvancedFilters > 0 && (
@@ -674,40 +680,43 @@ function MarketPageContent({
</PopoverContent> </PopoverContent>
</Popover> </Popover>
</div> </div>
</div>
{/* Quick tag filter buttons */} {/* Separator */}
<div className="mx-auto flex w-full max-w-4xl items-center gap-2 overflow-x-auto pb-1 sm:flex-wrap sm:justify-center sm:overflow-visible"> <div className="hidden sm:block w-px h-6 bg-border flex-shrink-0"></div>
<Button
type="button" {/* Quick tag filter buttons */}
variant={selectedTags.length === 0 ? 'secondary' : 'ghost'} <div className="flex items-center gap-2 overflow-x-auto pb-1 sm:flex-wrap sm:overflow-visible flex-shrink-0">
size="sm" <Button
className="h-8 shrink-0" type="button"
onClick={() => handleTagsChange([])} variant={selectedTags.length === 0 ? 'secondary' : 'ghost'}
> size="sm"
{t('market.allExtensions')} className="h-8 shrink-0"
</Button> onClick={() => handleTagsChange([])}
{availableTags.map((tag) => { >
const selected = selectedTags.includes(tag.tag); {t('market.allExtensions')}
return ( </Button>
<Button {availableTags.map((tag) => {
key={tag.tag} const selected = selectedTags.includes(tag.tag);
type="button" return (
variant={selected ? 'secondary' : 'ghost'} <Button
size="sm" key={tag.tag}
className="h-8 shrink-0" type="button"
onClick={() => { variant={selected ? 'secondary' : 'ghost'}
const newTags = selected size="sm"
? selectedTags.filter((t) => t !== tag.tag) className="h-8 shrink-0"
: [...selectedTags, tag.tag]; onClick={() => {
handleTagsChange(newTags); const newTags = selected
}} ? selectedTags.filter((t) => t !== tag.tag)
> : [...selectedTags, tag.tag];
{tagNames[tag.tag] || tag.tag} handleTagsChange(newTags);
{selected && <X className="h-3.5 w-3.5" />} }}
</Button> >
); {tagNames[tag.tag] || tag.tag}
})} {selected && <X className="h-3.5 w-3.5" />}
</Button>
);
})}
</div>
</div> </div>
{/* Search results stats */} {/* Search results stats */}
@@ -799,8 +808,10 @@ function MarketPageContent({
// 主组件,包装在 Suspense 中 // 主组件,包装在 Suspense 中
export default function MarketPage({ export default function MarketPage({
installPlugin, installPlugin,
headerActions,
}: { }: {
installPlugin: (plugin: PluginV4) => void; installPlugin: (plugin: PluginV4) => void;
headerActions?: React.ReactNode;
}) { }) {
return ( return (
<Suspense <Suspense
@@ -812,7 +823,7 @@ export default function MarketPage({
</div> </div>
} }
> >
<MarketPageContent installPlugin={installPlugin} /> <MarketPageContent installPlugin={installPlugin} headerActions={headerActions} />
</Suspense> </Suspense>
); );
} }