fix(web): distinct extension-format icons (plugin/mcp/skill)

The format filter used Wrench/AudioWaveform/Book for plugin/mcp/skill,
which collided with the plugin-component icons (Tool/EventListener/
KnowledgeEngine) shown right below. Switch formats to Puzzle/Server/
Sparkles — matching the canonical getTypeIcon used by the detail badges
— across the market filter, installed filter, install-queue map and
install-progress dialog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Junyan Qin
2026-06-04 21:34:23 +08:00
parent c967a2aa82
commit 3b4c455813
4 changed files with 23 additions and 17 deletions
@@ -11,7 +11,7 @@ import {
Download, Download,
Package, Package,
Server, Server,
BookOpen, Sparkles,
CheckCircle2, CheckCircle2,
XCircle, XCircle,
Loader2, Loader2,
@@ -176,7 +176,7 @@ function TaskProgressContent({ task }: { task: PluginInstallTask }) {
// MCP / Skill don't have the plugin's download + dependency-install stages; // MCP / Skill don't have the plugin's download + dependency-install stages;
// show a single "installing → done/failed" row instead of plugin steps. // show a single "installing → done/failed" row instead of plugin steps.
const isPlugin = task.extensionType === 'plugin'; const isPlugin = task.extensionType === 'plugin';
const simpleIcon = task.extensionType === 'mcp' ? Server : BookOpen; const simpleIcon = task.extensionType === 'mcp' ? Server : Sparkles;
const simpleInstallingLabel = const simpleInstallingLabel =
task.extensionType === 'mcp' task.extensionType === 'mcp'
? t('addExtension.installStage.mcpInstalling') ? t('addExtension.installStage.mcpInstalling')
@@ -9,9 +9,9 @@ import {
Loader2, Loader2,
X, X,
ListTodo, ListTodo,
Wrench, Puzzle,
AudioWaveform, Server,
Book, Sparkles,
} from 'lucide-react'; } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
@@ -35,9 +35,9 @@ const STAGE_ICONS: Record<string, React.ElementType> = {
}; };
const EXTENSION_TYPE_ICONS: Record<string, React.ElementType> = { const EXTENSION_TYPE_ICONS: Record<string, React.ElementType> = {
plugin: Wrench, plugin: Puzzle,
mcp: AudioWaveform, mcp: Server,
skill: Book, skill: Sparkles,
}; };
function TaskQueueItem({ function TaskQueueItem({
@@ -54,7 +54,7 @@ function TaskQueueItem({
const isError = task.stage === InstallStage.ERROR; const isError = task.stage === InstallStage.ERROR;
const isRunning = !isDone && !isError; const isRunning = !isDone && !isError;
const StageIcon = STAGE_ICONS[task.stage] || Download; const StageIcon = STAGE_ICONS[task.stage] || Download;
const TypeIcon = EXTENSION_TYPE_ICONS[task.extensionType] || Wrench; const TypeIcon = EXTENSION_TYPE_ICONS[task.extensionType] || Puzzle;
const getTypeBadgeClass = () => { const getTypeBadgeClass = () => {
switch (task.extensionType) { switch (task.extensionType) {
@@ -21,8 +21,7 @@ import { extractI18nObject } from '@/i18n/I18nProvider';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useAsyncTask, AsyncTaskStatus } from '@/hooks/useAsyncTask'; import { useAsyncTask, AsyncTaskStatus } from '@/hooks/useAsyncTask';
import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext'; import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext';
import { Loader2, Puzzle } from 'lucide-react'; import { Loader2, Puzzle, Server, Sparkles } from 'lucide-react';
import { Wrench, AudioWaveform, Book } from 'lucide-react';
export interface PluginInstalledComponentRef { export interface PluginInstalledComponentRef {
refreshPluginList: () => void; refreshPluginList: () => void;
@@ -44,14 +43,18 @@ export const FilterOptions = [
{ {
value: 'plugin' as FilterType, value: 'plugin' as FilterType,
labelKey: 'market.typePlugin', labelKey: 'market.typePlugin',
icon: Wrench, icon: Puzzle,
}, },
{ {
value: 'mcp' as FilterType, value: 'mcp' as FilterType,
labelKey: 'market.typeMCP', labelKey: 'market.typeMCP',
icon: AudioWaveform, icon: Server,
},
{
value: 'skill' as FilterType,
labelKey: 'market.typeSkill',
icon: Sparkles,
}, },
{ value: 'skill' as FilterType, labelKey: 'market.typeSkill', icon: Book },
]; ];
interface PluginInstalledComponentProps { interface PluginInstalledComponentProps {
@@ -17,6 +17,9 @@ import { Separator } from '@/components/ui/separator';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { import {
Search, Search,
Puzzle,
Server,
Sparkles,
Wrench, Wrench,
AudioWaveform, AudioWaveform,
Hash, Hash,
@@ -88,9 +91,9 @@ function MarketPageContent({
const extensionTypeOptions = [ const extensionTypeOptions = [
{ value: 'all', label: t('market.filters.allFormats'), icon: null }, { value: 'all', label: t('market.filters.allFormats'), icon: null },
{ value: 'plugin', label: t('market.typePlugin'), icon: Wrench }, { value: 'plugin', label: t('market.typePlugin'), icon: Puzzle },
{ value: 'mcp', label: t('market.typeMCP'), icon: AudioWaveform }, { value: 'mcp', label: t('market.typeMCP'), icon: Server },
{ value: 'skill', label: t('market.typeSkill'), icon: Book }, { value: 'skill', label: t('market.typeSkill'), icon: Sparkles },
]; ];
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');