style: replace plugin icon with skill icon (#13252)

chore: replace plugin icon  with skill icon
This commit is contained in:
Rdmclin2
2026-03-25 18:21:36 +08:00
committed by GitHub
parent 11daf645e9
commit e8a948cfaf
9 changed files with 122 additions and 97 deletions

View File

@@ -0,0 +1,34 @@
'use client';
import { Center } from '@lobehub/ui';
import { SkillsIcon } from '@lobehub/ui/icons';
import { type CSSProperties, memo } from 'react';
interface SkillAvatarProps {
className?: string;
size?: number;
style?: CSSProperties;
}
const SkillAvatar = memo<SkillAvatarProps>(({ size = 40, className, style }) => {
return (
<Center
className={className}
flex={'none'}
style={{
borderRadius: Math.floor(size * 0.1),
color: '#000',
height: size,
overflow: 'hidden',
width: size,
...style,
}}
>
<SkillsIcon color={'#000'} size={size} style={{ transform: 'scale(0.75)' }} />
</Center>
);
});
SkillAvatar.displayName = 'SkillAvatar';
export default SkillAvatar;

View File

@@ -2,7 +2,7 @@
import { type SkillResourceTreeNode } from '@lobechat/types';
import { Github } from '@lobehub/icons';
import { ActionIcon, Avatar, Flexbox, Icon } from '@lobehub/ui';
import { ActionIcon, Flexbox, Icon } from '@lobehub/ui';
import { Skeleton } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import { DotIcon, ExternalLinkIcon } from 'lucide-react';
@@ -10,6 +10,7 @@ import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import PublishedTime from '@/components/PublishedTime';
import SkillAvatar from '@/components/SkillAvatar';
import { useToolStore } from '@/store/tool';
import ContentViewer from './ContentViewer';
@@ -96,7 +97,7 @@ const AgentSkillDetail = memo<AgentSkillDetailProps>(({ skillId }) => {
{skillDetail && (
<div className={styles.meta}>
<Flexbox horizontal align={'center'} gap={12}>
<Avatar avatar={'🧩'} shape={'square'} size={40} />
<SkillAvatar size={40} />
<Flexbox flex={1} gap={4} style={{ overflow: 'hidden' }}>
<Flexbox horizontal align={'center'} gap={8} justify={'space-between'}>
<Flexbox horizontal align={'center'} className={styles.description} gap={4}>

View File

@@ -6,12 +6,11 @@ import {
} from '@lobechat/const';
import { type ItemType } from '@lobehub/ui';
import { Avatar, Icon } from '@lobehub/ui';
import { McpIcon, SkillsIcon } from '@lobehub/ui/icons';
import isEqual from 'fast-deep-equal';
import { ToyBrick } from 'lucide-react';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import PluginAvatar from '@/components/Plugins/PluginAvatar';
import { useCheckPluginsIsInstalled } from '@/hooks/useCheckPluginsIsInstalled';
import { useFetchInstalledPlugins } from '@/hooks/useFetchInstalledPlugins';
import { useAgentStore } from '@/store/agent';
@@ -235,13 +234,10 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
const builtinAgentSkillItems = useMemo(
() =>
installedBuiltinSkills.map((skill) => ({
icon: (
<Avatar
avatar={skill.avatar || '🧩'}
shape={'square'}
size={SKILL_ICON_SIZE}
style={{ flex: 'none' }}
/>
icon: skill.avatar ? (
<Avatar avatar={skill.avatar} shape={'square'} size={SKILL_ICON_SIZE} />
) : (
<Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />
),
key: skill.identifier,
label: (
@@ -264,9 +260,7 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
const marketAgentSkillItems = useMemo(
() =>
marketAgentSkills.map((skill) => ({
icon: (
<Avatar avatar={'🧩'} shape={'square'} size={SKILL_ICON_SIZE} style={{ flex: 'none' }} />
),
icon: <Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />,
key: skill.identifier,
label: (
<ToolItem
@@ -288,9 +282,7 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
const userAgentSkillItems = useMemo(
() =>
userAgentSkills.map((skill) => ({
icon: (
<Avatar avatar={'🧩'} shape={'square'} size={SKILL_ICON_SIZE} style={{ flex: 'none' }} />
),
icon: <Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />,
key: skill.identifier,
label: (
<ToolItem
@@ -331,11 +323,12 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
// Function to map plugins to list items
const mapPluginToItem = (item: (typeof list)[0]) => ({
icon: item?.avatar ? (
<PluginAvatar avatar={item.avatar} size={SKILL_ICON_SIZE} />
) : (
<Icon icon={ToyBrick} size={SKILL_ICON_SIZE} />
),
icon:
item?.avatar === 'MCP_AVATAR' || !item?.avatar ? (
<Icon icon={McpIcon} size={SKILL_ICON_SIZE} />
) : (
<Avatar avatar={item.avatar} shape={'square'} size={SKILL_ICON_SIZE} />
),
key: item.identifier,
label: (
<ToolItem
@@ -458,13 +451,10 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
const enabledBuiltinAgentSkillItems = installedBuiltinSkills
.filter((skill) => checked.includes(skill.identifier))
.map((skill) => ({
icon: (
<Avatar
avatar={skill.avatar || '🧩'}
shape={'square'}
size={SKILL_ICON_SIZE}
style={{ flex: 'none' }}
/>
icon: skill.avatar ? (
<Avatar avatar={skill.avatar} shape={'square'} size={SKILL_ICON_SIZE} />
) : (
<Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />
),
key: skill.identifier,
label: (
@@ -508,11 +498,12 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
const enabledCommunityPlugins = communityPlugins
.filter((item) => checked.includes(item.identifier))
.map((item) => ({
icon: item?.avatar ? (
<PluginAvatar avatar={item.avatar} size={SKILL_ICON_SIZE} />
) : (
<Icon icon={ToyBrick} size={SKILL_ICON_SIZE} />
),
icon:
item?.avatar === 'MCP_AVATAR' || !item?.avatar ? (
<Icon icon={McpIcon} size={SKILL_ICON_SIZE} />
) : (
<Avatar avatar={item.avatar} shape={'square'} size={SKILL_ICON_SIZE} />
),
key: item.identifier,
label: (
<ToolItem
@@ -532,11 +523,12 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
const enabledCustomPlugins = customPlugins
.filter((item) => checked.includes(item.identifier))
.map((item) => ({
icon: item?.avatar ? (
<PluginAvatar avatar={item.avatar} size={SKILL_ICON_SIZE} />
) : (
<Icon icon={ToyBrick} size={SKILL_ICON_SIZE} />
),
icon:
item?.avatar === 'MCP_AVATAR' || !item?.avatar ? (
<Icon icon={McpIcon} size={SKILL_ICON_SIZE} />
) : (
<Avatar avatar={item.avatar} shape={'square'} size={SKILL_ICON_SIZE} />
),
key: item.identifier,
label: (
<ToolItem
@@ -556,9 +548,7 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
const enabledMarketAgentSkillItems = marketAgentSkills
.filter((skill) => checked.includes(skill.identifier))
.map((skill) => ({
icon: (
<Avatar avatar={'🧩'} shape={'square'} size={SKILL_ICON_SIZE} style={{ flex: 'none' }} />
),
icon: <Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />,
key: skill.identifier,
label: (
<ToolItem
@@ -589,9 +579,7 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
const enabledUserAgentSkillItems = userAgentSkills
.filter((skill) => checked.includes(skill.identifier))
.map((skill) => ({
icon: (
<Avatar avatar={'🧩'} shape={'square'} size={SKILL_ICON_SIZE} style={{ flex: 'none' }} />
),
icon: <Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />,
key: skill.identifier,
label: (
<ToolItem

View File

@@ -3,13 +3,13 @@
import { KLAVIS_SERVER_TYPES, LOBEHUB_SKILL_PROVIDERS } from '@lobechat/const';
import { type ItemType } from '@lobehub/ui';
import { Avatar, Button, Flexbox, Icon } from '@lobehub/ui';
import { McpIcon, SkillsIcon } from '@lobehub/ui/icons';
import { cssVar } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { PlusIcon, ToyBrick } from 'lucide-react';
import { PlusIcon } from 'lucide-react';
import React, { memo, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import PluginAvatar from '@/components/Plugins/PluginAvatar';
import ActionDropdown from '@/features/ChatInput/ActionBar/components/ActionDropdown';
import KlavisServerItem from '@/features/ChatInput/ActionBar/Tools/KlavisServerItem';
import KlavisSkillIcon, {
@@ -320,12 +320,10 @@ const AgentTool = memo<AgentToolProps>(
const builtinAgentSkillItems = useMemo(
() =>
installedBuiltinSkills.map((skill) => ({
icon: (
<Avatar
avatar={skill.avatar || '🧩'}
size={SKILL_ICON_SIZE}
style={{ marginInlineEnd: 0 }}
/>
icon: skill.avatar ? (
<Avatar avatar={skill.avatar} size={SKILL_ICON_SIZE} style={{ marginInlineEnd: 0 }} />
) : (
<Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />
),
key: skill.identifier,
label: (
@@ -348,7 +346,7 @@ const AgentTool = memo<AgentToolProps>(
const marketAgentSkillItems = useMemo(
() =>
marketAgentSkills.map((skill) => ({
icon: <Avatar avatar={'🧩'} size={SKILL_ICON_SIZE} style={{ marginInlineEnd: 0 }} />,
icon: <Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />,
key: skill.identifier,
label: (
<ToolItem
@@ -370,7 +368,7 @@ const AgentTool = memo<AgentToolProps>(
const userAgentSkillItems = useMemo(
() =>
userAgentSkills.map((skill) => ({
icon: <Avatar avatar={'🧩'} size={SKILL_ICON_SIZE} style={{ marginInlineEnd: 0 }} />,
icon: <Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />,
key: skill.identifier,
label: (
<ToolItem
@@ -438,15 +436,12 @@ const AgentTool = memo<AgentToolProps>(
// Function to generate plugin list items
const mapPluginToItem = useCallback(
(item: (typeof installedPluginList)[0]) => ({
icon: item?.avatar ? (
<PluginAvatar
avatar={item.avatar}
size={SKILL_ICON_SIZE}
style={{ marginInlineEnd: 0 }}
/>
) : (
<Icon icon={ToyBrick} size={SKILL_ICON_SIZE} />
),
icon:
item?.avatar === 'MCP_AVATAR' || !item?.avatar ? (
<Icon icon={McpIcon} size={SKILL_ICON_SIZE} />
) : (
<Avatar avatar={item.avatar} shape={'square'} size={SKILL_ICON_SIZE} />
),
key: item.identifier,
label: (
<ToolItem
@@ -572,12 +567,10 @@ const AgentTool = memo<AgentToolProps>(
const enabledBuiltinAgentSkillItems = installedBuiltinSkills
.filter((skill) => isToolEnabled(skill.identifier))
.map((skill) => ({
icon: (
<Avatar
avatar={skill.avatar || '🧩'}
size={SKILL_ICON_SIZE}
style={{ marginInlineEnd: 0 }}
/>
icon: skill.avatar ? (
<Avatar avatar={skill.avatar} size={SKILL_ICON_SIZE} style={{ marginInlineEnd: 0 }} />
) : (
<Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />
),
key: skill.identifier,
label: (
@@ -615,11 +608,12 @@ const AgentTool = memo<AgentToolProps>(
const enabledCommunityPlugins = communityPlugins
.filter((item) => plugins.includes(item.identifier))
.map((item) => ({
icon: item?.avatar ? (
<PluginAvatar avatar={item.avatar} size={SKILL_ICON_SIZE} />
) : (
<Icon icon={ToyBrick} size={SKILL_ICON_SIZE} />
),
icon:
item?.avatar === 'MCP_AVATAR' || !item?.avatar ? (
<Icon icon={McpIcon} size={SKILL_ICON_SIZE} />
) : (
<Avatar avatar={item.avatar} shape={'square'} size={SKILL_ICON_SIZE} />
),
key: item.identifier,
label: (
<ToolItem
@@ -639,7 +633,7 @@ const AgentTool = memo<AgentToolProps>(
const enabledMarketAgentSkillItems = marketAgentSkills
.filter((skill) => isToolEnabled(skill.identifier))
.map((skill) => ({
icon: <Avatar avatar={'🧩'} size={SKILL_ICON_SIZE} style={{ marginInlineEnd: 0 }} />,
icon: <Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />,
key: skill.identifier,
label: (
<ToolItem
@@ -670,11 +664,12 @@ const AgentTool = memo<AgentToolProps>(
const enabledCustomPlugins = customPlugins
.filter((item) => plugins.includes(item.identifier))
.map((item) => ({
icon: item?.avatar ? (
<PluginAvatar avatar={item.avatar} size={SKILL_ICON_SIZE} />
) : (
<Icon icon={ToyBrick} size={SKILL_ICON_SIZE} />
),
icon:
item?.avatar === 'MCP_AVATAR' || !item?.avatar ? (
<Icon icon={McpIcon} size={SKILL_ICON_SIZE} />
) : (
<Avatar avatar={item.avatar} shape={'square'} size={SKILL_ICON_SIZE} />
),
key: item.identifier,
label: (
<ToolItem
@@ -694,7 +689,7 @@ const AgentTool = memo<AgentToolProps>(
const enabledUserAgentSkillItems = userAgentSkills
.filter((skill) => isToolEnabled(skill.identifier))
.map((skill) => ({
icon: <Avatar avatar={'🧩'} size={SKILL_ICON_SIZE} style={{ marginInlineEnd: 0 }} />,
icon: <Icon icon={SkillsIcon} size={SKILL_ICON_SIZE} />,
key: skill.identifier,
label: (
<ToolItem

View File

@@ -1,12 +1,14 @@
'use client';
import { ActionIcon, Avatar, Block, DropdownMenu, Flexbox, Icon, Modal, Tag } from '@lobehub/ui';
import { ActionIcon, Block, DropdownMenu, Flexbox, Icon, Modal, Tag } from '@lobehub/ui';
import { SkillsIcon } from '@lobehub/ui/icons';
import { App } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import { DownloadIcon, MoreVerticalIcon, PackageSearch, PuzzleIcon, Trash2 } from 'lucide-react';
import { DownloadIcon, MoreVerticalIcon, PackageSearch, Trash2 } from 'lucide-react';
import { lazy, memo, Suspense, useState } from 'react';
import { useTranslation } from 'react-i18next';
import SkillAvatar from '@/components/SkillAvatar';
import { agentSkillService } from '@/services/skill';
import { useToolStore } from '@/store/tool';
import { type SkillListItem } from '@/types/index';
@@ -85,13 +87,13 @@ const AgentSkillItem = memo<AgentSkillItemProps>(({ skill }) => {
paddingInline={12}
variant={'outlined'}
>
<Avatar avatar={'🧩'} shape={'square'} size={40} />
<SkillAvatar size={40} />
<Flexbox flex={1} gap={4} style={{ minWidth: 0, overflow: 'hidden' }}>
<Flexbox horizontal align="center" gap={8}>
<span className={styles.title} onClick={() => setDetailOpen(true)}>
{skill.name}
</span>
<Tag icon={<Icon icon={PuzzleIcon} />} size={'small'} />
<Tag icon={<Icon icon={SkillsIcon} />} size={'small'} />
</Flexbox>
{skill.description && (
<span className={itemStyles.description}>{skill.description}</span>

View File

@@ -1,7 +1,7 @@
import { isDesktop } from '@lobechat/const';
import { Avatar } from '@lobehub/ui';
import { SkillsIcon } from '@lobehub/ui/icons';
import {
Blocks,
Brain,
BrainCircuit,
ChartColumnBigIcon,
@@ -138,7 +138,7 @@ export const useCategory = () => {
label: t('tab.serviceModel'),
},
{
icon: Blocks,
icon: SkillsIcon,
key: SettingsTabs.Skill,
label: t('tab.skill'),
},

View File

@@ -11,8 +11,9 @@ import {
stopPropagation,
Tag,
} from '@lobehub/ui';
import { SkillsIcon } from '@lobehub/ui/icons';
import { App, Space } from 'antd';
import { DownloadIcon, MoreHorizontalIcon, Plus, PuzzleIcon, Trash2 } from 'lucide-react';
import { DownloadIcon, MoreHorizontalIcon, Plus, Trash2 } from 'lucide-react';
import { lazy, memo, Suspense, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -57,7 +58,7 @@ const AgentSkillItem = memo<AgentSkillItemProps>(({ skill }) => {
? t(`tools.builtins.${skill.identifier}.title`, { defaultValue: skill.name })
: skill.name;
const avatar = isBuiltin ? skill.avatar : '🧩';
const avatar = isBuiltin ? skill.avatar : undefined;
// ===== Handlers =====
@@ -218,7 +219,7 @@ const AgentSkillItem = memo<AgentSkillItemProps>(({ skill }) => {
onClick={handleOpenDetail}
>
<div className={`${styles.icon} ${showDisconnected ? styles.disconnectedIcon : ''}`}>
<Avatar avatar={avatar} size={32} />
{avatar ? <Avatar avatar={avatar} size={32} /> : <Icon icon={SkillsIcon} size={28} />}
</div>
<Flexbox gap={4} style={{ overflow: 'hidden' }}>
<Flexbox horizontal align="center" gap={8}>
@@ -227,7 +228,7 @@ const AgentSkillItem = memo<AgentSkillItemProps>(({ skill }) => {
>
{title}
</span>
{!isBuiltin && <Tag icon={<Icon icon={PuzzleIcon} />} size={'small'} />}
{!isBuiltin && <Tag icon={<Icon icon={SkillsIcon} />} size={'small'} />}
<SkillSourceTag source={skill.source} />
</Flexbox>
{showDisconnected && renderStatus()}

View File

@@ -1,11 +1,11 @@
'use client';
import { Flexbox, Modal } from '@lobehub/ui';
import { Avatar, Flexbox, Icon, Modal } from '@lobehub/ui';
import { McpIcon } from '@lobehub/ui/icons';
import { memo, Suspense, useState } from 'react';
import { useTranslation } from 'react-i18next';
import MCPTag from '@/components/Plugins/MCPTag';
import PluginAvatar from '@/components/Plugins/PluginAvatar';
import PluginTag from '@/components/Plugins/PluginTag';
import SkillSourceTag from '@/components/SkillSourceTag';
import McpDetail from '@/features/MCP/MCPDetail';
@@ -48,7 +48,11 @@ const McpSkillItem = memo<McpSkillItemProps>(
>
<Flexbox horizontal align="center" gap={12} style={{ flex: 1, overflow: 'hidden' }}>
<div className={styles.icon}>
<PluginAvatar avatar={avatar} size={32} />
{avatar && avatar !== 'MCP_AVATAR' ? (
<Avatar avatar={avatar} shape={'square'} size={32} />
) : (
<Icon icon={McpIcon} size={28} />
)}
</div>
<Flexbox horizontal align="center" gap={8} style={{ overflow: 'hidden' }}>
<span className={styles.title} onClick={() => setDetailOpen(true)}>

View File

@@ -12,10 +12,10 @@ import {
} from '@lobechat/const';
import { type BuiltinSkill, type LobeBuiltinTool } from '@lobechat/types';
import { Center, Empty } from '@lobehub/ui';
import { SkillsIcon } from '@lobehub/ui/icons';
import { Divider } from 'antd';
import { createStaticStyles } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { BlocksIcon } from 'lucide-react';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -291,7 +291,7 @@ const SkillList = memo(() => {
if (!hasAnySkills) {
return (
<Center className={styles.container} paddingBlock={48}>
<Empty description={t('tab.skillDesc')} icon={BlocksIcon} title={t('tab.skillEmpty')} />
<Empty description={t('tab.skillDesc')} icon={SkillsIcon} title={t('tab.skillEmpty')} />
<AddSkillButton />
</Center>
);