From 4e5dddde905ebd7ee8a78b9ea2219b8d1d4250d4 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sat, 31 Jan 2026 15:48:18 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E4=B8=BA=E6=8F=92=E4=BB=B6=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=B7=BB=E5=8A=A0=E4=B8=BB=E9=A1=B5=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E5=B1=95=E7=A4=BA=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本次更新在 PluginPackageJson 接口及相关类型中新增了一个可选的 `homepage` 字段,允许插件指定其主页 URL。插件展示组件已更新,新增了一个指向主页的 GitHub 链接按钮,以提升用户对插件资源的访问便捷性。此外,PluginConfigModal 中新增了一个问题反馈按钮,该按钮直接链接到插件的主页,从而优化了用户支持与反馈机制。 --- .../napcat-onebot/network/plugin/types.ts | 1 + .../napcat-webui-backend/src/api/Plugin.ts | 2 + .../src/components/display_card/container.tsx | 4 +- .../components/display_card/plugin_card.tsx | 16 ++-- .../display_card/plugin_store_card.tsx | 88 ++++++++++--------- .../src/controllers/plugin_manager.ts | 2 + .../src/pages/dashboard/plugin.tsx | 35 +++++++- .../pages/dashboard/plugin_config_modal.tsx | 21 ++++- 8 files changed, 113 insertions(+), 56 deletions(-) diff --git a/packages/napcat-onebot/network/plugin/types.ts b/packages/napcat-onebot/network/plugin/types.ts index 448b276b..5469816d 100644 --- a/packages/napcat-onebot/network/plugin/types.ts +++ b/packages/napcat-onebot/network/plugin/types.ts @@ -13,6 +13,7 @@ export interface PluginPackageJson { main?: string; description?: string; author?: string; + homepage?: string; } // ==================== 插件配置 Schema ==================== diff --git a/packages/napcat-webui-backend/src/api/Plugin.ts b/packages/napcat-webui-backend/src/api/Plugin.ts index 0367ba2b..d1ca0b98 100644 --- a/packages/napcat-webui-backend/src/api/Plugin.ts +++ b/packages/napcat-webui-backend/src/api/Plugin.ts @@ -72,6 +72,7 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: string; description: string; author: string; + homepage?: string; status: string; hasConfig: boolean; hasPages: boolean; @@ -109,6 +110,7 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: p.version || '0.0.0', description: p.packageJson?.description || '', author: p.packageJson?.author || '', + homepage: p.packageJson?.homepage, status, hasConfig: !!(p.runtime.module?.plugin_config_schema || p.runtime.module?.plugin_config_ui), hasPages diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index 790d09b9..806bbe07 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -22,6 +22,7 @@ export interface DisplayCardProps { const DisplayCardContainer: React.FC = ({ title: _title, + tag, action, enableSwitch, children, @@ -45,7 +46,8 @@ const DisplayCardContainer: React.FC = ({ -
{enableSwitch}
+ {tag &&
{tag}
} + {enableSwitch &&
{enableSwitch}
} {children} {action} diff --git a/packages/napcat-webui-frontend/src/components/display_card/plugin_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/plugin_card.tsx index e79ab221..d884dc92 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/plugin_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/plugin_card.tsx @@ -1,6 +1,7 @@ import { Button } from '@heroui/button'; import { Switch } from '@heroui/switch'; import { Chip } from '@heroui/chip'; +import { Tooltip } from '@heroui/tooltip'; import { useState } from 'react'; import { MdDeleteForever, MdSettings } from 'react-icons/md'; @@ -41,28 +42,27 @@ const PluginDisplayCard: React.FC = ({ -
+
+ -
+ {hasConfig && ( + + )} {installStatus === 'installed' && ( = ({ } > -
-
- - 作者 - -
- {author || '未知'} -
+
+ {/* 作者和包名 */} +
+ 作者: {author || '未知'} + · + + {id} +
-
- - 版本 - -
- v{version} -
-
-
- - 描述 - -
+ + {/* 描述 */} +
+
{description || '暂无描述'}
- {id && ( -
- - 包名 - -
- {id || '包名'} -
-
- )} - {tags && tags.length > 0 && ( -
- - 标签 - -
- {tags.slice(0, 2).join(' · ')} -
-
- )} + + {/* 标签 */} +
+ {tags && tags.length > 0 ? ( + tags.slice(0, 3).map((tag, index) => ( + + {tag} + + )) + ) : ( + 暂无标签 + )} +
); diff --git a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts index 1e36a917..d805ad3d 100644 --- a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts +++ b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts @@ -16,6 +16,8 @@ export interface PluginItem { description: string; /** 作者 */ author: string; + /** 主页链接 */ + homepage?: string; /** 状态: active-运行中, disabled-已禁用, stopped-已停止 */ status: PluginStatus; /** 是否有配置项 */ diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx index 667ab4b6..3857fecb 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx @@ -1,5 +1,5 @@ import { Button } from '@heroui/button'; -import { useEffect, useState, useRef } from 'react'; +import { useEffect, useState, useRef, useMemo } from 'react'; import toast from 'react-hot-toast'; import { IoMdRefresh } from 'react-icons/io'; import { FiUpload } from 'react-icons/fi'; @@ -10,9 +10,11 @@ import PluginDisplayCard from '@/components/display_card/plugin_card'; import PluginManager, { PluginItem } from '@/controllers/plugin_manager'; import useDialog from '@/hooks/use-dialog'; import PluginConfigModal from '@/pages/dashboard/plugin_config_modal'; +import { PluginStoreItem } from '@/types/plugin-store'; export default function PluginPage () { const [plugins, setPlugins] = useState([]); + const [storePlugins, setStorePlugins] = useState([]); const [loading, setLoading] = useState(false); const [pluginManagerNotFound, setPluginManagerNotFound] = useState(false); const dialog = useDialog(); @@ -25,7 +27,11 @@ export default function PluginPage () { setLoading(true); setPluginManagerNotFound(false); try { - const listResult = await PluginManager.getPluginList(); + // 并行加载本地插件列表和商店插件列表 + const [listResult, storeResult] = await Promise.all([ + PluginManager.getPluginList(), + PluginManager.getPluginStoreList().catch(() => ({ plugins: [] })) + ]); if (listResult.pluginManagerNotFound) { setPluginManagerNotFound(true); @@ -33,6 +39,7 @@ export default function PluginPage () { } else { setPlugins(listResult.plugins); } + setStorePlugins(storeResult.plugins || []); } catch (e: any) { toast.error(e.message); } finally { @@ -40,6 +47,25 @@ export default function PluginPage () { } }; + // 创建一个 Map 用于快速查找商店插件的 homepage + const storeHomepageMap = useMemo(() => { + const map = new Map(); + for (const plugin of storePlugins) { + if (plugin.homepage) { + map.set(plugin.id, plugin.homepage); + } + } + return map; + }, [storePlugins]); + + // 合并本地插件和商店数据中的 homepage + const pluginsWithHomepage = useMemo(() => { + return plugins.map(plugin => ({ + ...plugin, + homepage: plugin.homepage || storeHomepageMap.get(plugin.id) + })); + }, [plugins, storeHomepageMap]); + useEffect(() => { loadPlugins(); }, []); @@ -172,6 +198,7 @@ export default function PluginPage () { isOpen={isOpen} onOpenChange={onOpenChange} pluginId={currentPluginId} + homepage={storeHomepageMap.get(currentPluginId)} />
@@ -211,11 +238,11 @@ export default function PluginPage () { 插件管理器未加载,请检查 plugins 目录是否存在

- ) : plugins.length === 0 ? ( + ) : pluginsWithHomepage.length === 0 ? (
暂时没有安装插件
) : (
- {plugins.map(plugin => ( + {pluginsWithHomepage.map(plugin => ( void; /** 插件包名 (id) */ pluginId: string; + /** 插件主页 URL */ + homepage?: string; } /** Schema 更新事件类型 */ @@ -25,7 +29,7 @@ interface SchemaUpdateEvent { afterKey?: string; } -export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId }: Props) { +export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId, homepage }: Props) { const [loading, setLoading] = useState(false); const [schema, setSchema] = useState([]); const [config, setConfig] = useState>({}); @@ -373,6 +377,21 @@ export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId }: P )} + {homepage && ( + + + + )} From 8a0912b5b92045b1e5c76d36a16c5ef1c39ad263 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sat, 31 Jan 2026 16:01:16 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E5=8C=BA=E5=9F=9F=E6=A0=B7=E5=BC=8F=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E9=95=BF=E6=A0=87=E9=A2=98=E6=AD=A3=E7=A1=AE=E6=88=AA=E6=96=AD?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9C=81=E7=95=A5=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/display_card/container.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index 806bbe07..54743776 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -40,8 +40,9 @@ const DisplayCardContainer: React.FC = ({ >
-
- + {/* 限制标题区域最大宽度并隐藏溢出,确保 long title 能正确截断显示省略号 */} +
+ {_title}
From 1d22f19fa6d9929fa31b59d43f0228fe5060d1f5 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sat, 31 Jan 2026 16:54:10 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E4=B8=AD=E7=9A=84=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E4=B8=BB=E9=A1=B5=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B1=95=E7=A4=BA=E7=BB=84=E4=BB=B6=E4=BB=A5?= =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84=E3=80=82?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=E6=8F=92=E4=BB=B6=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E7=9A=84=E6=A0=B7=E5=BC=8F=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E6=9B=B4=E5=A5=BD=E7=9A=84=E7=94=A8=E6=88=B7=E4=BD=93?= =?UTF-8?q?=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../napcat-onebot/network/plugin/types.ts | 1 - .../napcat-webui-backend/src/api/Plugin.ts | 2 - .../src/components/display_card/container.tsx | 2 +- .../display_card/plugin_store_card.tsx | 88 ++++++++++--------- .../src/controllers/plugin_manager.ts | 2 - .../src/pages/dashboard/plugin.tsx | 35 +------- .../pages/dashboard/plugin_config_modal.tsx | 21 +---- 7 files changed, 53 insertions(+), 98 deletions(-) diff --git a/packages/napcat-onebot/network/plugin/types.ts b/packages/napcat-onebot/network/plugin/types.ts index 5469816d..448b276b 100644 --- a/packages/napcat-onebot/network/plugin/types.ts +++ b/packages/napcat-onebot/network/plugin/types.ts @@ -13,7 +13,6 @@ export interface PluginPackageJson { main?: string; description?: string; author?: string; - homepage?: string; } // ==================== 插件配置 Schema ==================== diff --git a/packages/napcat-webui-backend/src/api/Plugin.ts b/packages/napcat-webui-backend/src/api/Plugin.ts index d1ca0b98..0367ba2b 100644 --- a/packages/napcat-webui-backend/src/api/Plugin.ts +++ b/packages/napcat-webui-backend/src/api/Plugin.ts @@ -72,7 +72,6 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: string; description: string; author: string; - homepage?: string; status: string; hasConfig: boolean; hasPages: boolean; @@ -110,7 +109,6 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: p.version || '0.0.0', description: p.packageJson?.description || '', author: p.packageJson?.author || '', - homepage: p.packageJson?.homepage, status, hasConfig: !!(p.runtime.module?.plugin_config_schema || p.runtime.module?.plugin_config_ui), hasPages diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index 54743776..99d66630 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -4,7 +4,7 @@ import clsx from 'clsx'; import key from '@/const/key'; export interface ContainerProps { - title: string; + title: React.ReactNode; tag?: React.ReactNode; action: React.ReactNode; enableSwitch: React.ReactNode; diff --git a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx index 0ea60016..53fd3ba4 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx @@ -2,8 +2,8 @@ import { Button } from '@heroui/button'; import { Chip } from '@heroui/chip'; import { Tooltip } from '@heroui/tooltip'; import { useState } from 'react'; +import clsx from 'clsx'; import { IoMdDownload, IoMdRefresh, IoMdCheckmarkCircle } from 'react-icons/io'; -import { FaGithub } from 'react-icons/fa'; import DisplayCardContainer from './container'; import { PluginStoreItem } from '@/types/plugin-store'; @@ -59,28 +59,32 @@ const PluginStoreCard: React.FC = ({ return ( + {homepage ? ( + + window.open(homepage, '_blank')} + > + {name} + + + ) : ( + {name} + )} + v{version} +
+ } tag={
- {homepage && ( - - - - )} {installStatus === 'installed' && ( } + className="h-6 text-[10px] bg-success-50 dark:bg-success-500/10 text-success-600" + startContent={} > 已安装 @@ -90,17 +94,11 @@ const PluginStoreCard: React.FC = ({ color="warning" size="sm" variant="flat" + className="h-6 text-[10px] bg-warning-50 dark:bg-warning-500/10 text-warning-600" > 可更新 )} - - v{version} -
} enableSwitch={undefined} @@ -119,38 +117,46 @@ const PluginStoreCard: React.FC = ({ } > -
+
{/* 作者和包名 */} -
- 作者: {author || '未知'} - · +
+ + 作者: {author || '未知'} + + / - {id} + {id}
{/* 描述 */} -
-
- {description || '暂无描述'} + +
+
+ {description || '暂无描述'} +
-
+ - {/* 标签 */} -
+ {/* 标签栏 - 优化后的极简风格 */} +
{tags && tags.length > 0 ? ( - tags.slice(0, 3).map((tag, index) => ( - ( +
+ {tag} - +
)) ) : ( - 暂无标签 + no tags )}
diff --git a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts index d805ad3d..1e36a917 100644 --- a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts +++ b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts @@ -16,8 +16,6 @@ export interface PluginItem { description: string; /** 作者 */ author: string; - /** 主页链接 */ - homepage?: string; /** 状态: active-运行中, disabled-已禁用, stopped-已停止 */ status: PluginStatus; /** 是否有配置项 */ diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx index 3857fecb..667ab4b6 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx @@ -1,5 +1,5 @@ import { Button } from '@heroui/button'; -import { useEffect, useState, useRef, useMemo } from 'react'; +import { useEffect, useState, useRef } from 'react'; import toast from 'react-hot-toast'; import { IoMdRefresh } from 'react-icons/io'; import { FiUpload } from 'react-icons/fi'; @@ -10,11 +10,9 @@ import PluginDisplayCard from '@/components/display_card/plugin_card'; import PluginManager, { PluginItem } from '@/controllers/plugin_manager'; import useDialog from '@/hooks/use-dialog'; import PluginConfigModal from '@/pages/dashboard/plugin_config_modal'; -import { PluginStoreItem } from '@/types/plugin-store'; export default function PluginPage () { const [plugins, setPlugins] = useState([]); - const [storePlugins, setStorePlugins] = useState([]); const [loading, setLoading] = useState(false); const [pluginManagerNotFound, setPluginManagerNotFound] = useState(false); const dialog = useDialog(); @@ -27,11 +25,7 @@ export default function PluginPage () { setLoading(true); setPluginManagerNotFound(false); try { - // 并行加载本地插件列表和商店插件列表 - const [listResult, storeResult] = await Promise.all([ - PluginManager.getPluginList(), - PluginManager.getPluginStoreList().catch(() => ({ plugins: [] })) - ]); + const listResult = await PluginManager.getPluginList(); if (listResult.pluginManagerNotFound) { setPluginManagerNotFound(true); @@ -39,7 +33,6 @@ export default function PluginPage () { } else { setPlugins(listResult.plugins); } - setStorePlugins(storeResult.plugins || []); } catch (e: any) { toast.error(e.message); } finally { @@ -47,25 +40,6 @@ export default function PluginPage () { } }; - // 创建一个 Map 用于快速查找商店插件的 homepage - const storeHomepageMap = useMemo(() => { - const map = new Map(); - for (const plugin of storePlugins) { - if (plugin.homepage) { - map.set(plugin.id, plugin.homepage); - } - } - return map; - }, [storePlugins]); - - // 合并本地插件和商店数据中的 homepage - const pluginsWithHomepage = useMemo(() => { - return plugins.map(plugin => ({ - ...plugin, - homepage: plugin.homepage || storeHomepageMap.get(plugin.id) - })); - }, [plugins, storeHomepageMap]); - useEffect(() => { loadPlugins(); }, []); @@ -198,7 +172,6 @@ export default function PluginPage () { isOpen={isOpen} onOpenChange={onOpenChange} pluginId={currentPluginId} - homepage={storeHomepageMap.get(currentPluginId)} />
@@ -238,11 +211,11 @@ export default function PluginPage () { 插件管理器未加载,请检查 plugins 目录是否存在

- ) : pluginsWithHomepage.length === 0 ? ( + ) : plugins.length === 0 ? (
暂时没有安装插件
) : (
- {pluginsWithHomepage.map(plugin => ( + {plugins.map(plugin => ( void; /** 插件包名 (id) */ pluginId: string; - /** 插件主页 URL */ - homepage?: string; } /** Schema 更新事件类型 */ @@ -29,7 +25,7 @@ interface SchemaUpdateEvent { afterKey?: string; } -export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId, homepage }: Props) { +export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId }: Props) { const [loading, setLoading] = useState(false); const [schema, setSchema] = useState([]); const [config, setConfig] = useState>({}); @@ -377,21 +373,6 @@ export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId, hom )} - {homepage && ( - - - - )} From 0301421bc81cd817329186cddf411e474a9a2bf4 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sat, 31 Jan 2026 17:15:15 +0800 Subject: [PATCH 4/9] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20PluginStoreCard=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=96=B0=E5=A2=9E=20displayId=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8C=85=E5=90=8D=E5=B1=95=E7=A4=BA=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E8=B0=83=E6=95=B4=E5=8D=A1=E7=89=87=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=BB=A5=E6=8F=90=E5=8D=87=E5=93=8D=E5=BA=94=E5=BC=8F=E8=A1=A8?= =?UTF-8?q?=E7=8E=B0=E3=80=82=E6=9B=B4=E6=96=B0=E4=B8=8D=E5=90=8C=E5=B1=8F?= =?UTF-8?q?=E5=B9=95=E5=B0=BA=E5=AF=B8=E7=9A=84=E6=9C=80=E5=A4=A7=E5=AE=BD?= =?UTF-8?q?=E5=BA=A6=E8=AE=BE=E7=BD=AE=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=8C=85?= =?UTF-8?q?=E5=90=8D=E6=88=AA=E6=96=AD=E6=98=BE=E7=A4=BA=E4=B8=94=E6=82=AC?= =?UTF-8?q?=E5=81=9C=E5=8F=AF=E6=9F=A5=E7=9C=8B=E5=AE=8C=E6=95=B4=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/display_card/plugin_store_card.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx index 53fd3ba4..01b75d1e 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx @@ -24,6 +24,7 @@ const PluginStoreCard: React.FC = ({ }) => { const { name, version, author, description, tags, id, homepage } = data; const [processing, setProcessing] = useState(false); + const displayId = id?.replace(/^napcat-plugin-/, '') || id; const handleInstall = () => { setProcessing(true); @@ -58,7 +59,7 @@ const PluginStoreCard: React.FC = ({ return ( {homepage ? ( @@ -125,7 +126,7 @@ const PluginStoreCard: React.FC = ({ / - {id} + {displayId}
From ce9187b16fc4fcabe74536ff0bcf125996edbc7c Mon Sep 17 00:00:00 2001 From: Qiao Date: Sun, 1 Feb 2026 10:44:38 +0800 Subject: [PATCH 5/9] =?UTF-8?q?Revert=20"=E4=BF=AE=E6=94=B9=20PluginStoreC?= =?UTF-8?q?ard=20=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=96=B0=E5=A2=9E=20displayId?= =?UTF-8?q?=20=E4=BC=98=E5=8C=96=E5=8C=85=E5=90=8D=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E8=B0=83=E6=95=B4=E5=8D=A1=E7=89=87=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=BB=A5=E6=8F=90=E5=8D=87=E5=93=8D=E5=BA=94=E5=BC=8F?= =?UTF-8?q?=E8=A1=A8=E7=8E=B0=E3=80=82=E6=9B=B4=E6=96=B0=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E5=B1=8F=E5=B9=95=E5=B0=BA=E5=AF=B8=E7=9A=84=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6=E8=AE=BE=E7=BD=AE=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E5=8C=85=E5=90=8D=E6=88=AA=E6=96=AD=E6=98=BE=E7=A4=BA=E4=B8=94?= =?UTF-8?q?=E6=82=AC=E5=81=9C=E5=8F=AF=E6=9F=A5=E7=9C=8B=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E3=80=82"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0301421bc81cd817329186cddf411e474a9a2bf4. --- .../src/components/display_card/plugin_store_card.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx index 01b75d1e..53fd3ba4 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx @@ -24,7 +24,6 @@ const PluginStoreCard: React.FC = ({ }) => { const { name, version, author, description, tags, id, homepage } = data; const [processing, setProcessing] = useState(false); - const displayId = id?.replace(/^napcat-plugin-/, '') || id; const handleInstall = () => { setProcessing(true); @@ -59,7 +58,7 @@ const PluginStoreCard: React.FC = ({ return ( {homepage ? ( @@ -126,7 +125,7 @@ const PluginStoreCard: React.FC = ({ / - {displayId} + {id}
From a28ae49960902b53337767062f6d5c72dbee1716 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sun, 1 Feb 2026 10:44:38 +0800 Subject: [PATCH 6/9] =?UTF-8?q?Revert=20"=E7=A7=BB=E9=99=A4=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=8F=AF=E9=80=89=E4=B8=BB=E9=A1=B5=E5=AD=97=E6=AE=B5=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E5=B1=95=E7=A4=BA=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E4=BB=A5=E7=AE=80=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E3=80=82=E6=9B=B4=E6=96=B0=E4=BA=86=E6=8F=92=E4=BB=B6=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=8D=A1=E7=89=87=E7=9A=84=E6=A0=B7=E5=BC=8F=EF=BC=8C?= =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E6=9B=B4=E5=A5=BD=E7=9A=84=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E3=80=82"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1d22f19fa6d9929fa31b59d43f0228fe5060d1f5. --- .../napcat-onebot/network/plugin/types.ts | 1 + .../napcat-webui-backend/src/api/Plugin.ts | 2 + .../src/components/display_card/container.tsx | 2 +- .../display_card/plugin_store_card.tsx | 88 +++++++++---------- .../src/controllers/plugin_manager.ts | 2 + .../src/pages/dashboard/plugin.tsx | 35 +++++++- .../pages/dashboard/plugin_config_modal.tsx | 21 ++++- 7 files changed, 98 insertions(+), 53 deletions(-) diff --git a/packages/napcat-onebot/network/plugin/types.ts b/packages/napcat-onebot/network/plugin/types.ts index 448b276b..5469816d 100644 --- a/packages/napcat-onebot/network/plugin/types.ts +++ b/packages/napcat-onebot/network/plugin/types.ts @@ -13,6 +13,7 @@ export interface PluginPackageJson { main?: string; description?: string; author?: string; + homepage?: string; } // ==================== 插件配置 Schema ==================== diff --git a/packages/napcat-webui-backend/src/api/Plugin.ts b/packages/napcat-webui-backend/src/api/Plugin.ts index 0367ba2b..d1ca0b98 100644 --- a/packages/napcat-webui-backend/src/api/Plugin.ts +++ b/packages/napcat-webui-backend/src/api/Plugin.ts @@ -72,6 +72,7 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: string; description: string; author: string; + homepage?: string; status: string; hasConfig: boolean; hasPages: boolean; @@ -109,6 +110,7 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: p.version || '0.0.0', description: p.packageJson?.description || '', author: p.packageJson?.author || '', + homepage: p.packageJson?.homepage, status, hasConfig: !!(p.runtime.module?.plugin_config_schema || p.runtime.module?.plugin_config_ui), hasPages diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index 99d66630..54743776 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -4,7 +4,7 @@ import clsx from 'clsx'; import key from '@/const/key'; export interface ContainerProps { - title: React.ReactNode; + title: string; tag?: React.ReactNode; action: React.ReactNode; enableSwitch: React.ReactNode; diff --git a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx index 53fd3ba4..0ea60016 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx @@ -2,8 +2,8 @@ import { Button } from '@heroui/button'; import { Chip } from '@heroui/chip'; import { Tooltip } from '@heroui/tooltip'; import { useState } from 'react'; -import clsx from 'clsx'; import { IoMdDownload, IoMdRefresh, IoMdCheckmarkCircle } from 'react-icons/io'; +import { FaGithub } from 'react-icons/fa'; import DisplayCardContainer from './container'; import { PluginStoreItem } from '@/types/plugin-store'; @@ -59,32 +59,28 @@ const PluginStoreCard: React.FC = ({ return ( - {homepage ? ( - - window.open(homepage, '_blank')} - > - {name} - - - ) : ( - {name} - )} - v{version} -
- } + title={name} tag={
+ {homepage && ( + + + + )} {installStatus === 'installed' && ( } + startContent={} > 已安装 @@ -94,11 +90,17 @@ const PluginStoreCard: React.FC = ({ color="warning" size="sm" variant="flat" - className="h-6 text-[10px] bg-warning-50 dark:bg-warning-500/10 text-warning-600" > 可更新 )} + + v{version} +
} enableSwitch={undefined} @@ -117,46 +119,38 @@ const PluginStoreCard: React.FC = ({ } > -
+
{/* 作者和包名 */} -
- - 作者: {author || '未知'} - - / +
+ 作者: {author || '未知'} + · - {id} + {id}
{/* 描述 */} - -
-
- {description || '暂无描述'} -
+
+
+ {description || '暂无描述'}
- +
- {/* 标签栏 - 优化后的极简风格 */} -
+ {/* 标签 */} +
{tags && tags.length > 0 ? ( - tags.map((tag, index) => ( -
( + - {tag} -
+ )) ) : ( - no tags + 暂无标签 )}
diff --git a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts index 1e36a917..d805ad3d 100644 --- a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts +++ b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts @@ -16,6 +16,8 @@ export interface PluginItem { description: string; /** 作者 */ author: string; + /** 主页链接 */ + homepage?: string; /** 状态: active-运行中, disabled-已禁用, stopped-已停止 */ status: PluginStatus; /** 是否有配置项 */ diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx index 667ab4b6..3857fecb 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx @@ -1,5 +1,5 @@ import { Button } from '@heroui/button'; -import { useEffect, useState, useRef } from 'react'; +import { useEffect, useState, useRef, useMemo } from 'react'; import toast from 'react-hot-toast'; import { IoMdRefresh } from 'react-icons/io'; import { FiUpload } from 'react-icons/fi'; @@ -10,9 +10,11 @@ import PluginDisplayCard from '@/components/display_card/plugin_card'; import PluginManager, { PluginItem } from '@/controllers/plugin_manager'; import useDialog from '@/hooks/use-dialog'; import PluginConfigModal from '@/pages/dashboard/plugin_config_modal'; +import { PluginStoreItem } from '@/types/plugin-store'; export default function PluginPage () { const [plugins, setPlugins] = useState([]); + const [storePlugins, setStorePlugins] = useState([]); const [loading, setLoading] = useState(false); const [pluginManagerNotFound, setPluginManagerNotFound] = useState(false); const dialog = useDialog(); @@ -25,7 +27,11 @@ export default function PluginPage () { setLoading(true); setPluginManagerNotFound(false); try { - const listResult = await PluginManager.getPluginList(); + // 并行加载本地插件列表和商店插件列表 + const [listResult, storeResult] = await Promise.all([ + PluginManager.getPluginList(), + PluginManager.getPluginStoreList().catch(() => ({ plugins: [] })) + ]); if (listResult.pluginManagerNotFound) { setPluginManagerNotFound(true); @@ -33,6 +39,7 @@ export default function PluginPage () { } else { setPlugins(listResult.plugins); } + setStorePlugins(storeResult.plugins || []); } catch (e: any) { toast.error(e.message); } finally { @@ -40,6 +47,25 @@ export default function PluginPage () { } }; + // 创建一个 Map 用于快速查找商店插件的 homepage + const storeHomepageMap = useMemo(() => { + const map = new Map(); + for (const plugin of storePlugins) { + if (plugin.homepage) { + map.set(plugin.id, plugin.homepage); + } + } + return map; + }, [storePlugins]); + + // 合并本地插件和商店数据中的 homepage + const pluginsWithHomepage = useMemo(() => { + return plugins.map(plugin => ({ + ...plugin, + homepage: plugin.homepage || storeHomepageMap.get(plugin.id) + })); + }, [plugins, storeHomepageMap]); + useEffect(() => { loadPlugins(); }, []); @@ -172,6 +198,7 @@ export default function PluginPage () { isOpen={isOpen} onOpenChange={onOpenChange} pluginId={currentPluginId} + homepage={storeHomepageMap.get(currentPluginId)} />
@@ -211,11 +238,11 @@ export default function PluginPage () { 插件管理器未加载,请检查 plugins 目录是否存在

- ) : plugins.length === 0 ? ( + ) : pluginsWithHomepage.length === 0 ? (
暂时没有安装插件
) : (
- {plugins.map(plugin => ( + {pluginsWithHomepage.map(plugin => ( void; /** 插件包名 (id) */ pluginId: string; + /** 插件主页 URL */ + homepage?: string; } /** Schema 更新事件类型 */ @@ -25,7 +29,7 @@ interface SchemaUpdateEvent { afterKey?: string; } -export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId }: Props) { +export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId, homepage }: Props) { const [loading, setLoading] = useState(false); const [schema, setSchema] = useState([]); const [config, setConfig] = useState>({}); @@ -373,6 +377,21 @@ export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId }: P )} + {homepage && ( + + + + )} From 5fabf0ab24f2cc979154856c339df12f91860cc7 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sun, 1 Feb 2026 10:44:38 +0800 Subject: [PATCH 7/9] =?UTF-8?q?Revert=20"=E4=BC=98=E5=8C=96=E6=A0=87?= =?UTF-8?q?=E9=A2=98=E5=8C=BA=E5=9F=9F=E6=A0=B7=E5=BC=8F=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E9=95=BF=E6=A0=87=E9=A2=98=E6=AD=A3=E7=A1=AE=E6=88=AA?= =?UTF-8?q?=E6=96=AD=E6=98=BE=E7=A4=BA=E7=9C=81=E7=95=A5=E5=8F=B7"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8a0912b5b92045b1e5c76d36a16c5ef1c39ad263. --- .../src/components/display_card/container.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index 54743776..806bbe07 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -40,9 +40,8 @@ const DisplayCardContainer: React.FC = ({ >
- {/* 限制标题区域最大宽度并隐藏溢出,确保 long title 能正确截断显示省略号 */} -
- +
+ {_title}
From 88a37f974d52e9ca8fd6f830f7f9811e315c1635 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sun, 1 Feb 2026 10:44:38 +0800 Subject: [PATCH 8/9] =?UTF-8?q?Revert=20"=E4=B8=BA=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0=E4=B8=BB=E9=A1=B5=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=B9=B6=E4=BC=98=E5=8C=96=E5=B1=95=E7=A4=BA=E7=BB=84?= =?UTF-8?q?=E4=BB=B6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4e5dddde905ebd7ee8a78b9ea2219b8d1d4250d4. --- .../napcat-onebot/network/plugin/types.ts | 1 - .../napcat-webui-backend/src/api/Plugin.ts | 2 - .../src/components/display_card/container.tsx | 4 +- .../components/display_card/plugin_card.tsx | 16 ++-- .../display_card/plugin_store_card.tsx | 88 +++++++++---------- .../src/controllers/plugin_manager.ts | 2 - .../src/pages/dashboard/plugin.tsx | 35 +------- .../pages/dashboard/plugin_config_modal.tsx | 21 +---- 8 files changed, 56 insertions(+), 113 deletions(-) diff --git a/packages/napcat-onebot/network/plugin/types.ts b/packages/napcat-onebot/network/plugin/types.ts index 5469816d..448b276b 100644 --- a/packages/napcat-onebot/network/plugin/types.ts +++ b/packages/napcat-onebot/network/plugin/types.ts @@ -13,7 +13,6 @@ export interface PluginPackageJson { main?: string; description?: string; author?: string; - homepage?: string; } // ==================== 插件配置 Schema ==================== diff --git a/packages/napcat-webui-backend/src/api/Plugin.ts b/packages/napcat-webui-backend/src/api/Plugin.ts index d1ca0b98..0367ba2b 100644 --- a/packages/napcat-webui-backend/src/api/Plugin.ts +++ b/packages/napcat-webui-backend/src/api/Plugin.ts @@ -72,7 +72,6 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: string; description: string; author: string; - homepage?: string; status: string; hasConfig: boolean; hasPages: boolean; @@ -110,7 +109,6 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: p.version || '0.0.0', description: p.packageJson?.description || '', author: p.packageJson?.author || '', - homepage: p.packageJson?.homepage, status, hasConfig: !!(p.runtime.module?.plugin_config_schema || p.runtime.module?.plugin_config_ui), hasPages diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index 806bbe07..790d09b9 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -22,7 +22,6 @@ export interface DisplayCardProps { const DisplayCardContainer: React.FC = ({ title: _title, - tag, action, enableSwitch, children, @@ -46,8 +45,7 @@ const DisplayCardContainer: React.FC = ({
- {tag &&
{tag}
} - {enableSwitch &&
{enableSwitch}
} +
{enableSwitch}
{children} {action} diff --git a/packages/napcat-webui-frontend/src/components/display_card/plugin_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/plugin_card.tsx index d884dc92..e79ab221 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/plugin_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/plugin_card.tsx @@ -1,7 +1,6 @@ import { Button } from '@heroui/button'; import { Switch } from '@heroui/switch'; import { Chip } from '@heroui/chip'; -import { Tooltip } from '@heroui/tooltip'; import { useState } from 'react'; import { MdDeleteForever, MdSettings } from 'react-icons/md'; @@ -42,27 +41,28 @@ const PluginDisplayCard: React.FC = ({ - +
+
- +
{hasConfig && ( - - )} {installStatus === 'installed' && ( = ({ } > -
- {/* 作者和包名 */} -
- 作者: {author || '未知'} - · - - {id} - +
+
+ + 作者 + +
+ {author || '未知'} +
- - {/* 描述 */} -
-
+
+ + 版本 + +
+ v{version} +
+
+
+ + 描述 + +
{description || '暂无描述'}
- - {/* 标签 */} -
- {tags && tags.length > 0 ? ( - tags.slice(0, 3).map((tag, index) => ( - - {tag} - - )) - ) : ( - 暂无标签 - )} -
+ {id && ( +
+ + 包名 + +
+ {id || '包名'} +
+
+ )} + {tags && tags.length > 0 && ( +
+ + 标签 + +
+ {tags.slice(0, 2).join(' · ')} +
+
+ )}
); diff --git a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts index d805ad3d..1e36a917 100644 --- a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts +++ b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts @@ -16,8 +16,6 @@ export interface PluginItem { description: string; /** 作者 */ author: string; - /** 主页链接 */ - homepage?: string; /** 状态: active-运行中, disabled-已禁用, stopped-已停止 */ status: PluginStatus; /** 是否有配置项 */ diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx index 3857fecb..667ab4b6 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx @@ -1,5 +1,5 @@ import { Button } from '@heroui/button'; -import { useEffect, useState, useRef, useMemo } from 'react'; +import { useEffect, useState, useRef } from 'react'; import toast from 'react-hot-toast'; import { IoMdRefresh } from 'react-icons/io'; import { FiUpload } from 'react-icons/fi'; @@ -10,11 +10,9 @@ import PluginDisplayCard from '@/components/display_card/plugin_card'; import PluginManager, { PluginItem } from '@/controllers/plugin_manager'; import useDialog from '@/hooks/use-dialog'; import PluginConfigModal from '@/pages/dashboard/plugin_config_modal'; -import { PluginStoreItem } from '@/types/plugin-store'; export default function PluginPage () { const [plugins, setPlugins] = useState([]); - const [storePlugins, setStorePlugins] = useState([]); const [loading, setLoading] = useState(false); const [pluginManagerNotFound, setPluginManagerNotFound] = useState(false); const dialog = useDialog(); @@ -27,11 +25,7 @@ export default function PluginPage () { setLoading(true); setPluginManagerNotFound(false); try { - // 并行加载本地插件列表和商店插件列表 - const [listResult, storeResult] = await Promise.all([ - PluginManager.getPluginList(), - PluginManager.getPluginStoreList().catch(() => ({ plugins: [] })) - ]); + const listResult = await PluginManager.getPluginList(); if (listResult.pluginManagerNotFound) { setPluginManagerNotFound(true); @@ -39,7 +33,6 @@ export default function PluginPage () { } else { setPlugins(listResult.plugins); } - setStorePlugins(storeResult.plugins || []); } catch (e: any) { toast.error(e.message); } finally { @@ -47,25 +40,6 @@ export default function PluginPage () { } }; - // 创建一个 Map 用于快速查找商店插件的 homepage - const storeHomepageMap = useMemo(() => { - const map = new Map(); - for (const plugin of storePlugins) { - if (plugin.homepage) { - map.set(plugin.id, plugin.homepage); - } - } - return map; - }, [storePlugins]); - - // 合并本地插件和商店数据中的 homepage - const pluginsWithHomepage = useMemo(() => { - return plugins.map(plugin => ({ - ...plugin, - homepage: plugin.homepage || storeHomepageMap.get(plugin.id) - })); - }, [plugins, storeHomepageMap]); - useEffect(() => { loadPlugins(); }, []); @@ -198,7 +172,6 @@ export default function PluginPage () { isOpen={isOpen} onOpenChange={onOpenChange} pluginId={currentPluginId} - homepage={storeHomepageMap.get(currentPluginId)} />
@@ -238,11 +211,11 @@ export default function PluginPage () { 插件管理器未加载,请检查 plugins 目录是否存在

- ) : pluginsWithHomepage.length === 0 ? ( + ) : plugins.length === 0 ? (
暂时没有安装插件
) : (
- {pluginsWithHomepage.map(plugin => ( + {plugins.map(plugin => ( void; /** 插件包名 (id) */ pluginId: string; - /** 插件主页 URL */ - homepage?: string; } /** Schema 更新事件类型 */ @@ -29,7 +25,7 @@ interface SchemaUpdateEvent { afterKey?: string; } -export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId, homepage }: Props) { +export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId }: Props) { const [loading, setLoading] = useState(false); const [schema, setSchema] = useState([]); const [config, setConfig] = useState>({}); @@ -377,21 +373,6 @@ export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId, hom )} - {homepage && ( - - - - )} From d743a05ccb66d57c8a2e250539678a53ad2f6485 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sun, 1 Feb 2026 10:57:41 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=E5=86=8D=E8=AF=B4=E4=B8=91=E6=88=91?= =?UTF-8?q?=E6=89=93=E6=AD=BB=E4=BD=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/display_card/container.tsx | 2 +- .../display_card/plugin_store_card.tsx | 25 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index 790d09b9..330f3f80 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -4,7 +4,7 @@ import clsx from 'clsx'; import key from '@/const/key'; export interface ContainerProps { - title: string; + title: React.ReactNode; tag?: React.ReactNode; action: React.ReactNode; enableSwitch: React.ReactNode; diff --git a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx index b758a9e5..bfd10580 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx @@ -1,5 +1,6 @@ import { Button } from '@heroui/button'; import { Chip } from '@heroui/chip'; +import { Tooltip } from '@heroui/tooltip'; import { useState } from 'react'; import { IoMdDownload, IoMdRefresh, IoMdCheckmarkCircle } from 'react-icons/io'; @@ -20,7 +21,7 @@ const PluginStoreCard: React.FC = ({ onInstall, installStatus = 'not-installed', }) => { - const { name, version, author, description, tags, id } = data; + const { name, version, author, description, tags, id, homepage } = data; const [processing, setProcessing] = useState(false); const handleInstall = () => { @@ -53,11 +54,31 @@ const PluginStoreCard: React.FC = ({ }; const buttonConfig = getButtonConfig(); + const titleContent = homepage ? ( + + + {name} + + + ) : ( + name + ); return ( {installStatus === 'installed' && (