From 5f3eb34bafd3f7c87fb810711ae7fb37e4468402 Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Wed, 30 Sep 2026 18:52:00 +0800 Subject: [PATCH] feat(market): show certification badges in Core marketplace (#2615) --- .../plugin-market/PluginMarketComponent.tsx | 1 + .../plugin-market/RecommendationLists.tsx | 1 + .../PluginMarketCardComponent.tsx | 4 ++ .../plugin-market-card/PluginMarketCardVO.ts | 3 + web/src/app/infra/entities/plugin/index.ts | 1 + .../market/CertifiedPluginBadge.tsx | 58 +++++++++++++++++++ web/src/i18n/locales/en-US.ts | 5 ++ web/src/i18n/locales/es-ES.ts | 5 ++ web/src/i18n/locales/ja-JP.ts | 5 ++ web/src/i18n/locales/ru-RU.ts | 5 ++ web/src/i18n/locales/th-TH.ts | 5 ++ web/src/i18n/locales/vi-VN.ts | 5 ++ web/src/i18n/locales/zh-Hans.ts | 5 ++ web/src/i18n/locales/zh-Hant.ts | 5 ++ 14 files changed, 108 insertions(+) create mode 100644 web/src/components/market/CertifiedPluginBadge.tsx diff --git a/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx b/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx index 1dd640e0e..92520d8b2 100644 --- a/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx +++ b/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx @@ -298,6 +298,7 @@ function MarketPageContent({ iconURL, githubURL: plugin.repository, version: plugin.latest_version, + certificationStatus: plugin.certification_status, components: plugin.components || {}, tags: plugin.tags || [], type: plugin.type, diff --git a/web/src/app/home/plugins/components/plugin-market/RecommendationLists.tsx b/web/src/app/home/plugins/components/plugin-market/RecommendationLists.tsx index 6f8dbfe11..a1861312d 100644 --- a/web/src/app/home/plugins/components/plugin-market/RecommendationLists.tsx +++ b/web/src/app/home/plugins/components/plugin-market/RecommendationLists.tsx @@ -57,6 +57,7 @@ function pluginToVO( iconURL, githubURL: plugin.repository, version: plugin.latest_version, + certificationStatus: plugin.certification_status, components: plugin.components, tags: plugin.tags || [], type: plugin.type, diff --git a/web/src/app/home/plugins/components/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx b/web/src/app/home/plugins/components/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx index c8393ac1b..cddd29c64 100644 --- a/web/src/app/home/plugins/components/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx +++ b/web/src/app/home/plugins/components/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx @@ -1,3 +1,4 @@ +import { CertifiedPluginBadge } from '@/components/market/CertifiedPluginBadge'; import { PluginMarketCardVO } from './PluginMarketCardVO'; import { useRef, useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -276,6 +277,9 @@ export default function PluginMarketCardComponent({ {typeLabel} )} + {cardVO.certificationStatus === 'issued' && ( + + )} diff --git a/web/src/app/home/plugins/components/plugin-market/plugin-market-card/PluginMarketCardVO.ts b/web/src/app/home/plugins/components/plugin-market/plugin-market-card/PluginMarketCardVO.ts index 4816d5d3a..c71e2d482 100644 --- a/web/src/app/home/plugins/components/plugin-market/plugin-market-card/PluginMarketCardVO.ts +++ b/web/src/app/home/plugins/components/plugin-market/plugin-market-card/PluginMarketCardVO.ts @@ -9,6 +9,7 @@ export interface IPluginMarketCardVO { iconURL: string; githubURL: string; version: string; + certificationStatus?: string; components?: Record; tags?: string[]; type?: 'plugin' | 'mcp' | 'skill'; @@ -29,6 +30,7 @@ export class PluginMarketCardVO implements IPluginMarketCardVO { installCount: number; likeCount: number; version: string; + certificationStatus?: string; components?: Record; tags?: string[]; type?: 'plugin' | 'mcp' | 'skill'; @@ -46,6 +48,7 @@ export class PluginMarketCardVO implements IPluginMarketCardVO { this.likeCount = prop.likeCount ?? 0; this.pluginId = prop.pluginId; this.version = prop.version; + this.certificationStatus = prop.certificationStatus; this.components = prop.components; this.tags = prop.tags; this.type = prop.type; diff --git a/web/src/app/infra/entities/plugin/index.ts b/web/src/app/infra/entities/plugin/index.ts index 64f19396e..8ade9b80f 100644 --- a/web/src/app/infra/entities/plugin/index.ts +++ b/web/src/app/infra/entities/plugin/index.ts @@ -36,6 +36,7 @@ export enum PluginV4Status { } export interface PluginV4 { + certification_status?: string; id: number; plugin_id: string; mcp_id?: string; diff --git a/web/src/components/market/CertifiedPluginBadge.tsx b/web/src/components/market/CertifiedPluginBadge.tsx new file mode 100644 index 000000000..eb9041fe2 --- /dev/null +++ b/web/src/components/market/CertifiedPluginBadge.tsx @@ -0,0 +1,58 @@ +'use client'; + +import { BadgeCheck } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@/components/ui/tooltip'; + +export function CertifiedPluginBadge({ + compact = false, +}: { + compact?: boolean; +}) { + const { t, i18n } = useTranslation(); + const language = i18n.language.startsWith('zh') + ? 'zh' + : i18n.language.startsWith('ja') + ? 'ja' + : 'en'; + const docsURL = `https://langbot.app/docs/${language}/plugin/certified-plugins`; + return ( + + + + event.stopPropagation()} + className={`inline-flex shrink-0 items-center gap-1 rounded border border-emerald-600/25 bg-emerald-50 text-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500 ${compact ? 'h-4 px-0.5' : 'px-2 py-0.5 text-xs font-medium'}`} + > + + {!compact && t('market.certifiedPlugin')} + + + + {t('market.certificationTooltip')} + event.stopPropagation()} + > + {t('market.certificationLearnMore')} + + + + + ); +} diff --git a/web/src/i18n/locales/en-US.ts b/web/src/i18n/locales/en-US.ts index 5d29d623c..4232c399e 100644 --- a/web/src/i18n/locales/en-US.ts +++ b/web/src/i18n/locales/en-US.ts @@ -1624,6 +1624,11 @@ const enUS = { }, }, market: { + certifiedPlugin: 'Certified plugin', + certificationTooltip: + 'This version has passed a review of data isolation and compatibility with shared runtimes, and its package is signed.', + certificationLearnMore: 'Learn about certification', + runnerUsage: 'Runner usage', runnerUsageAll: 'All', runnerUsageAgent: 'Agent / Pipeline', diff --git a/web/src/i18n/locales/es-ES.ts b/web/src/i18n/locales/es-ES.ts index 31ef6e9e1..ec77461cb 100644 --- a/web/src/i18n/locales/es-ES.ts +++ b/web/src/i18n/locales/es-ES.ts @@ -1655,6 +1655,11 @@ const esES = { }, }, market: { + certifiedPlugin: 'Plugin certificado', + certificationTooltip: + 'Esta versión ha superado la revisión de aislamiento de datos y compatibilidad con entornos compartidos. El paquete está firmado.', + certificationLearnMore: 'Acerca de la certificación', + runnerUsage: 'Uso del ejecutor', runnerUsageAll: 'Todos', runnerUsageAgent: 'Agent / Flujo', diff --git a/web/src/i18n/locales/ja-JP.ts b/web/src/i18n/locales/ja-JP.ts index 0d8746171..f24e83328 100644 --- a/web/src/i18n/locales/ja-JP.ts +++ b/web/src/i18n/locales/ja-JP.ts @@ -1639,6 +1639,11 @@ const jaJP = { }, }, market: { + certifiedPlugin: '認証済みプラグイン', + certificationTooltip: + 'このバージョンはデータ分離と共有実行環境との互換性の審査に合格し、パッケージに署名されています。', + certificationLearnMore: 'プラグイン認証について', + runnerUsage: 'ランナーの用途', runnerUsageAll: 'すべて', runnerUsageAgent: 'Agent / パイプライン', diff --git a/web/src/i18n/locales/ru-RU.ts b/web/src/i18n/locales/ru-RU.ts index d7e5c9937..9bcbe1158 100644 --- a/web/src/i18n/locales/ru-RU.ts +++ b/web/src/i18n/locales/ru-RU.ts @@ -1651,6 +1651,11 @@ const ruRU = { }, }, market: { + certifiedPlugin: 'Сертифицированный плагин', + certificationTooltip: + 'Эта версия прошла проверку изоляции данных и совместимости с общей средой выполнения. Пакет подписан.', + certificationLearnMore: 'О сертификации', + runnerUsage: 'Назначение исполнителя', runnerUsageAll: 'Все', runnerUsageAgent: 'Agent / Конвейер', diff --git a/web/src/i18n/locales/th-TH.ts b/web/src/i18n/locales/th-TH.ts index de83ce95b..d1b7be940 100644 --- a/web/src/i18n/locales/th-TH.ts +++ b/web/src/i18n/locales/th-TH.ts @@ -1593,6 +1593,11 @@ const thTH = { }, }, market: { + certifiedPlugin: 'ปลั๊กอินที่ผ่านการรับรอง', + certificationTooltip: + 'เวอร์ชันนี้ผ่านการตรวจสอบการแยกข้อมูลและความเข้ากันได้กับสภาพแวดล้อมรันร่วมกัน และแพ็กเกจได้รับการลงนามแล้ว', + certificationLearnMore: 'เกี่ยวกับการรับรองปลั๊กอิน', + runnerUsage: 'การใช้งานรันเนอร์', runnerUsageAll: 'ทั้งหมด', runnerUsageAgent: 'Agent / ไปป์ไลน์', diff --git a/web/src/i18n/locales/vi-VN.ts b/web/src/i18n/locales/vi-VN.ts index dd42a0af1..f3e72f46d 100644 --- a/web/src/i18n/locales/vi-VN.ts +++ b/web/src/i18n/locales/vi-VN.ts @@ -1619,6 +1619,11 @@ const viVN = { }, }, market: { + certifiedPlugin: 'Plugin được chứng nhận', + certificationTooltip: + 'Phiên bản này đã vượt qua đánh giá về cách ly dữ liệu và khả năng tương thích với môi trường chạy dùng chung. Gói đã được ký.', + certificationLearnMore: 'Tìm hiểu về chứng nhận', + runnerUsage: 'Mục đích trình chạy', runnerUsageAll: 'Tất cả', runnerUsageAgent: 'Agent / Pipeline', diff --git a/web/src/i18n/locales/zh-Hans.ts b/web/src/i18n/locales/zh-Hans.ts index aa8fd2f9e..0c0e65000 100644 --- a/web/src/i18n/locales/zh-Hans.ts +++ b/web/src/i18n/locales/zh-Hans.ts @@ -1531,6 +1531,11 @@ const zhHans = { }, }, market: { + certifiedPlugin: '认证插件', + certificationTooltip: + '此版本已通过数据隔离与共享运行环境兼容性审核,插件包已签名。', + certificationLearnMore: '了解插件认证', + runnerUsage: '运行器用途', runnerUsageAll: '全部', runnerUsageAgent: 'Agent / 流水线', diff --git a/web/src/i18n/locales/zh-Hant.ts b/web/src/i18n/locales/zh-Hant.ts index 600ce965f..007629468 100644 --- a/web/src/i18n/locales/zh-Hant.ts +++ b/web/src/i18n/locales/zh-Hant.ts @@ -1529,6 +1529,11 @@ const zhHant = { }, }, market: { + certifiedPlugin: '認證外掛', + certificationTooltip: + '此版本已通過資料隔離與共享執行環境相容性審核,外掛套件已簽章。', + certificationLearnMore: '瞭解外掛認證', + runnerUsage: '運行器用途', runnerUsageAll: '全部', runnerUsageAgent: 'Agent / 流水線',
{t('market.certificationTooltip')}