diff --git a/src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx b/src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx index 6228dd4ec6413..3e0ccedc011bb 100644 --- a/src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx +++ b/src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx @@ -1,15 +1,14 @@ -import { Checkbox, Tag } from 'antd'; +import { Checkbox } from 'antd'; import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; +import PluginTag from '@/features/PluginStore/PluginItem/PluginTag'; import { useSessionStore } from '@/store/session'; import { agentSelectors } from '@/store/session/selectors'; import { useToolStore } from '@/store/tool'; import { customPluginSelectors } from '@/store/tool/selectors'; const ToolItem = memo<{ identifier: string; label: string }>(({ identifier, label }) => { - const { t } = useTranslation('plugin'); const [checked, togglePlugin] = useSessionStore((s) => [ agentSelectors.currentAgentPlugins(s).includes(identifier), s.togglePlugin, @@ -30,11 +29,7 @@ const ToolItem = memo<{ identifier: string; label: string }>(({ identifier, labe > {label} - {isCustom && ( - - {t('list.item.local.title')} - - )} + {isCustom && } { e.domEvent.preventDefault(); }, }} - placement={'topLeft'} + placement={'top'} trigger={['click']} > diff --git a/src/app/market/features/AgentDetailContent/AgentInfo/index.tsx b/src/app/market/features/AgentDetailContent/AgentInfo/index.tsx index fefecf122dead..41776c9088008 100644 --- a/src/app/market/features/AgentDetailContent/AgentInfo/index.tsx +++ b/src/app/market/features/AgentDetailContent/AgentInfo/index.tsx @@ -55,6 +55,7 @@ const AgentModalInner = memo(() => { }, ]} onChange={setTab} + variant={'compact'} /> diff --git a/src/app/market/features/AgentDetailContent/AgentInfo/style.ts b/src/app/market/features/AgentDetailContent/AgentInfo/style.ts index 8164f0cd2cbcf..7b8fc9411a710 100644 --- a/src/app/market/features/AgentDetailContent/AgentInfo/style.ts +++ b/src/app/market/features/AgentDetailContent/AgentInfo/style.ts @@ -29,15 +29,7 @@ export const useStyles = createStyles(({ css, token, prefixCls, stylish }) => ({ `, markdown: stylish.markdownInChat, nav: css` - padding-top: 4px; - - .${prefixCls}-tabs-tab { - margin: 4px !important; - - + .${prefixCls}-tabs-tab { - margin: 4px !important; - } - } + padding-top: 8px; `, title: css` font-size: 20px; diff --git a/src/components/FileList/FileItem.tsx b/src/components/FileList/FileItem.tsx index 80bb69bcc14cf..eeb99ce47bf33 100644 --- a/src/components/FileList/FileItem.tsx +++ b/src/components/FileList/FileItem.tsx @@ -3,6 +3,7 @@ import { createStyles } from 'antd-style'; import { Trash } from 'lucide-react'; import { CSSProperties, memo, useCallback } from 'react'; +import { usePlatform } from '@/hooks/usePlatform'; import { useFileStore } from '@/store/file'; import { MIN_IMAGE_SIZE } from './style'; @@ -38,6 +39,7 @@ const FileItem = memo(({ editable, id, alwaysShowClose }) => { const IMAGE_SIZE = editable ? MIN_IMAGE_SIZE : '100%'; const { data, isLoading } = useFetchFile(id); const { styles, cx } = useStyles(); + const { isSafari } = usePlatform(); const handleRemoveFile = useCallback( (e: any) => { @@ -62,11 +64,11 @@ const FileItem = memo(({ editable, id, alwaysShowClose }) => { } alt={data?.name || id || ''} alwaysShowActions={alwaysShowClose} - height={'auto'} + height={isSafari ? 'auto' : '100%'} isLoading={isLoading} size={IMAGE_SIZE as any} src={data?.url} - style={{ height: 'auto' }} + style={{ height: isSafari ? 'auto' : '100%' }} wrapperClassName={cx(styles.image, editable && styles.editableImage)} /> ); diff --git a/src/components/HotKeys/index.tsx b/src/components/HotKeys/index.tsx index 60908010ffc97..5e50146a4aadd 100644 --- a/src/components/HotKeys/index.tsx +++ b/src/components/HotKeys/index.tsx @@ -8,7 +8,7 @@ import { memo, useEffect, useState } from 'react'; import { Flexbox } from 'react-layout-kit'; import { CLEAN_MESSAGE_KEY, META_KEY, PREFIX_KEY } from '@/const/hotkeys'; -import { isApplePlatform } from '@/utils/platform'; +import { usePlatform } from '@/hooks/usePlatform'; const useStyles = createStyles( ({ css, token }) => css` @@ -44,12 +44,13 @@ const HotKeys = memo(({ keys, desc }) => { const { styles } = useStyles(); const [keysGroup, setKeysGroup] = useState(keys.split('+')); const visibility = typeof window === 'undefined' ? 'hidden' : 'visible'; + const { isApple } = usePlatform(); useEffect(() => { const mapping: Record = { - [CLEAN_MESSAGE_KEY]: isApplePlatform() ? : 'backspace', - [META_KEY]: isApplePlatform() ? : 'ctrl', - [PREFIX_KEY]: isApplePlatform() ? : 'alt', + [CLEAN_MESSAGE_KEY]: isApple ? : 'backspace', + [META_KEY]: isApple ? : 'ctrl', + [PREFIX_KEY]: isApple ? : 'alt', }; const newValue = keys .split('+') diff --git a/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx b/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx index e51af271ab6e3..dd7943d1b3e92 100644 --- a/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx +++ b/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx @@ -1,6 +1,6 @@ import { Icon } from '@lobehub/ui'; import { Button } from 'antd'; -import { LucideBlocks } from 'lucide-react'; +import { PackagePlus } from 'lucide-react'; import { forwardRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -33,7 +33,7 @@ const AddPluginButton = forwardRef((props, ref) => { open={showModal} /> - + + {isLoading ? ( diff --git a/src/features/PluginStore/PluginItem/Action.tsx b/src/features/PluginStore/PluginItem/Action.tsx index 1bfd6010b9c63..5e8793ca9c183 100644 --- a/src/features/PluginStore/PluginItem/Action.tsx +++ b/src/features/PluginStore/PluginItem/Action.tsx @@ -1,7 +1,7 @@ import { ActionIcon, Icon } from '@lobehub/ui'; import { Button, Dropdown, Popconfirm } from 'antd'; import { useResponsive } from 'antd-style'; -import { InfoIcon, MoreVerticalIcon, Settings2, Trash2 } from 'lucide-react'; +import { InfoIcon, MoreVerticalIcon, Settings, Trash2 } from 'lucide-react'; import { memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; @@ -42,7 +42,7 @@ const Actions = memo(({ identifier, type }) => { {isCustomPlugin && } {hasSettings && ( { setOpen(true); setTab('settings'); diff --git a/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx b/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx index cfd2a3363277e..9282b080f71e6 100644 --- a/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx +++ b/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx @@ -1,6 +1,6 @@ import { ActionIcon } from '@lobehub/ui'; import isEqual from 'fast-deep-equal'; -import { Package2 } from 'lucide-react'; +import { PackageSearch } from 'lucide-react'; import { memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -37,7 +37,7 @@ const EditCustomPlugin = memo<{ identifier: string }>(({ identifier }) => { value={customPlugin} /> { setModal(true); }} diff --git a/src/features/PluginStore/PluginItem/PluginTag.tsx b/src/features/PluginStore/PluginItem/PluginTag.tsx new file mode 100644 index 0000000000000..10924978c2c5f --- /dev/null +++ b/src/features/PluginStore/PluginItem/PluginTag.tsx @@ -0,0 +1,58 @@ +import { Icon, Tag } from '@lobehub/ui'; +import { createStyles } from 'antd-style'; +import { BadgeCheck, CircleUser, Package } from 'lucide-react'; +import { rgba } from 'polished'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { InstallPluginMeta } from '@/types/tool/plugin'; + +const useStyles = createStyles(({ css, token }) => ({ + community: css` + color: ${rgba(token.colorInfo, 0.75)}; + background: ${token.colorInfoBg}; + + &:hover { + color: ${token.colorInfo}; + } + `, + custom: css` + color: ${rgba(token.colorWarning, 0.75)}; + background: ${token.colorWarningBg}; + + &:hover { + color: ${token.colorWarning}; + } + `, + official: css` + color: ${rgba(token.colorSuccess, 0.75)}; + background: ${token.colorSuccessBg}; + + &:hover { + color: ${token.colorSuccess}; + } + `, +})); + +interface PluginTagProps extends Pick { + showIcon?: boolean; + showText?: boolean; +} + +const PluginTag = memo(({ showIcon = true, author, type, showText = true }) => { + const { t } = useTranslation('plugin'); + const { styles, cx } = useStyles(); + const isCustom = type === 'customPlugin'; + const isOfficial = author === 'LobeHub'; + + return ( + } + > + {showText && (author || t(isCustom ? 'store.customPlugin' : 'store.communityPlugin'))} + + ); +}); + +export default PluginTag; diff --git a/src/features/PluginStore/PluginItem/index.tsx b/src/features/PluginStore/PluginItem/index.tsx index 0c5b4efa65fc3..a3407af7f62b3 100644 --- a/src/features/PluginStore/PluginItem/index.tsx +++ b/src/features/PluginStore/PluginItem/index.tsx @@ -1,27 +1,28 @@ -import { Avatar, Icon, Tag } from '@lobehub/ui'; -import { createStyles, useResponsive } from 'antd-style'; -import { BadgeCheck, CircleUser } from 'lucide-react'; +import { Avatar, Tooltip } from '@lobehub/ui'; +import { Typography } from 'antd'; +import { createStyles } from 'antd-style'; import Link from 'next/link'; import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; +import PluginTag from '@/features/PluginStore/PluginItem/PluginTag'; import { InstallPluginMeta } from '@/types/tool/plugin'; import Actions from './Action'; +const { Paragraph } = Typography; + const useStyles = createStyles(({ css, token }) => ({ desc: css` + margin: 0 !important; + font-size: 12px; color: ${token.colorTextDescription}; `, link: css` color: ${token.colorText}; `, - tag: css` - color: ${token.colorTextSecondary}; - `, title: css` - font-size: ${token.fontSizeLG}px; + font-size: 14px; font-weight: bold; line-height: 2; `, @@ -29,39 +30,39 @@ const useStyles = createStyles(({ css, token }) => ({ const PluginItem = memo(({ identifier, homepage, author, type, meta = {} }) => { const { styles } = useStyles(); - const { mobile } = useResponsive(); - const isCustomPlugin = type === 'customPlugin'; - const { t } = useTranslation('plugin'); return ( - - - - + + + + - {homepage ? ( - + + {homepage ? ( + +
{meta.title}
+ + ) : (
{meta.title}
- - ) : ( -
{meta.title}
- )} - - {author && ( - } - > - {author} - - )} - {isCustomPlugin && ( - - {t('store.customPlugin')} - - )} + )} +
+
-
{meta.description}
+ + {meta.description} +
diff --git a/src/hooks/usePlatform.ts b/src/hooks/usePlatform.ts new file mode 100644 index 0000000000000..a06d6d13d5474 --- /dev/null +++ b/src/hooks/usePlatform.ts @@ -0,0 +1,15 @@ +import { useRef } from 'react'; + +import { getBrowser, getPlatform } from '@/utils/platform'; + +export const usePlatform = () => { + const platform = useRef(getPlatform()); + const browser = useRef(getBrowser()); + return { + isApple: platform.current && ['Mac OS', 'iOS'].includes(platform.current), + isChrome: browser.current === 'Chrome', + isIOS: platform.current === 'iOS', + isMacOS: platform.current === 'Mac OS', + isSafari: browser.current === 'Safari', + }; +}; diff --git a/src/locales/default/plugin.ts b/src/locales/default/plugin.ts index 15a94bdef9b55..bd2b7d5306db8 100644 --- a/src/locales/default/plugin.ts +++ b/src/locales/default/plugin.ts @@ -151,6 +151,7 @@ export default { settings: '设置', uninstall: '卸载', }, + communityPlugin: '三方社区', customPlugin: '自定义', empty: '暂无已安装插件', installAllPlugins: '安装全部', diff --git a/src/locales/default/setting.ts b/src/locales/default/setting.ts index 8b95df5bc115f..ec22a1e70a38a 100644 --- a/src/locales/default/setting.ts +++ b/src/locales/default/setting.ts @@ -85,7 +85,7 @@ export default { waitingForMore: '更多模型正在 <1>计划接入 中,敬请期待 ✨', }, plugin: { - addTooltip: '添加自定义插件', + addTooltip: '自定义插件', clearDeprecated: '移除无效插件', empty: '暂无已安装插件,欢迎前往 <1>插件商店 探索', installStatus: { diff --git a/src/store/tool/helpers.ts b/src/store/tool/helpers.ts index b80d9075fadd4..3c293dc46ad11 100644 --- a/src/store/tool/helpers.ts +++ b/src/store/tool/helpers.ts @@ -6,6 +6,8 @@ const getPluginFormList = (list: LobeTool[], id: string) => list?.find((p) => p. const getPluginTitle = (meta?: LobeChatPluginManifest['meta']) => meta?.title; const getPluginDesc = (meta?: LobeChatPluginManifest['meta']) => meta?.description; + +const getPluginTags = (meta?: LobeChatPluginManifest['meta']) => meta?.tags; const getPluginAvatar = (meta?: LobeChatPluginManifest['meta']) => meta?.avatar || '🧩'; const isCustomPlugin = (id: string, pluginList: LobeTool[]) => @@ -18,6 +20,7 @@ export const pluginHelpers = { getPluginAvatar, getPluginDesc, getPluginFormList, + getPluginTags, getPluginTitle, isCustomPlugin, isSettingSchemaNonEmpty, diff --git a/src/styles/antdOverride.ts b/src/styles/antdOverride.ts index 03f59e07aecb9..0dbf11751ea98 100644 --- a/src/styles/antdOverride.ts +++ b/src/styles/antdOverride.ts @@ -4,4 +4,11 @@ export default ({ token }: { prefixCls: string; token: Theme }) => css` .${token.prefixCls}-popover { z-index: 1100; } + .${token.prefixCls}-dropdown { + > ul { + border: 1px solid ${token.colorBorderSecondary}; + border-radius: ${token.borderRadius}px !important; + box-shadow: ${token.boxShadowSecondary}; + } + } `; diff --git a/src/utils/platform.ts b/src/utils/platform.ts index 7adbb79fe7771..447020a72ea14 100644 --- a/src/utils/platform.ts +++ b/src/utils/platform.ts @@ -1,11 +1,14 @@ import UAParser from 'ua-parser-js'; -export const getPlatform = () => { +const getPaser = () => { let ua = navigator.userAgent; - return new UAParser(ua).getOS(); + return new UAParser(ua); +}; + +export const getPlatform = () => { + return getPaser().getOS().name; }; -export const isApplePlatform = () => { - const platform = getPlatform().name; - return platform && ['Mac OS', 'iOS'].includes(platform); +export const getBrowser = () => { + return getPaser().getResult().browser.name; };