From 2194b2975c4cfc270dd85d5cfc9643a6806dcca8 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Sat, 30 Aug 2025 17:08:03 +0800 Subject: [PATCH] refactor: market plugin detail dialog --- web/package.json | 1 + .../PluginDetailDialog.tsx | 381 +++++++++--------- 2 files changed, 192 insertions(+), 190 deletions(-) diff --git a/web/package.json b/web/package.json index 8cce9538..26cb7ca7 100644 --- a/web/package.json +++ b/web/package.json @@ -58,6 +58,7 @@ "react-i18next": "^15.5.1", "react-markdown": "^10.1.0", "react-photo-view": "^1.2.7", + "remark-gfm": "^4.0.1", "sonner": "^2.0.3", "tailwind-merge": "^3.2.0", "tailwindcss": "^4.1.5", diff --git a/web/src/app/home/plugins/plugin-market/plugin-detail-dialog/PluginDetailDialog.tsx b/web/src/app/home/plugins/plugin-market/plugin-detail-dialog/PluginDetailDialog.tsx index 83a3994b..2bb29f8d 100644 --- a/web/src/app/home/plugins/plugin-market/plugin-detail-dialog/PluginDetailDialog.tsx +++ b/web/src/app/home/plugins/plugin-market/plugin-detail-dialog/PluginDetailDialog.tsx @@ -6,11 +6,12 @@ import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Loader2, Download, Users } from 'lucide-react'; import ReactMarkdown from 'react-markdown'; -import { PluginV4 } from '@/app/infra/entities/plugin'; -import { extractI18nObject } from '@/i18n/I18nProvider'; +import remarkGfm from 'remark-gfm'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; +import { PluginV4 } from '@/app/infra/entities/plugin'; import { getCloudServiceClientSync } from '@/app/infra/http'; +import { extractI18nObject } from '@/i18n/I18nProvider'; interface PluginDetailDialogProps { open: boolean; @@ -50,7 +51,6 @@ export default function PluginDetailDialog({ author, pluginName, ); - console.log('detailResponse', detailResponse); setPlugin(detailResponse.plugin); // 获取README @@ -58,7 +58,6 @@ export default function PluginDetailDialog({ try { const readmeResponse = await getCloudServiceClientSync().getPluginREADME(author, pluginName); - console.log('readmeResponse', readmeResponse); setReadme(readmeResponse.readme); } catch (error) { console.warn('Failed to load README:', error); @@ -77,210 +76,212 @@ export default function PluginDetailDialog({ if (!open) return null; + const PluginHeader = () => ( +
+ {plugin!.name} +
+

+ {extractI18nObject(plugin!.label) || plugin!.name} +

+
+ + + {plugin!.author} / {plugin!.name} + +
+
+ v{plugin!.latest_version} + + + {plugin!.install_count.toLocaleString()} {t('market.downloads')} + + {plugin!.repository && ( + + )} +
+
+
+ ); + + const PluginDescription = () => ( +
+

+ {extractI18nObject(plugin!.description) || t('market.noDescription')} +

+
+ ); + + const PluginOptions = () => ( +
+ +
+ ); + + const ReadmeContent = () => ( +
+ ( +
+ + + ), + thead: ({ ...props }) => , + tbody: ({ ...props }) => ( + + ), + th: ({ ...props }) => ( + + ), + // 删除线支持 + del: ({ ...props }) => ( + + ), + // Todo 列表支持 + input: ({ type, checked, ...props }) => { + if (type === 'checkbox') { + return ( + + ); + } + return ; + }, + ul: ({ ...props }) =>
    , + ol: ({ ...props }) =>
      , + li: ({ ...props }) =>
    1. , + h1: ({ ...props }) => ( +

      + ), + h2: ({ ...props }) => ( +

      + ), + p: ({ ...props }) =>

      , + code: ({ className, children, ...props }) => { + const match = /language-(\w+)/.exec(className || ''); + const isCodeBlock = match ? true : false; + + // 如果是代码块(有语言标识),由 pre 标签处理样式,淡灰色底,黑色字 + if (isCodeBlock) { + return ( + + {children} + + ); + } + + // 内联代码样式 - 淡灰色底 + return ( + + {children} + + ); + }, + pre: ({ ...props }) => ( +

      +          ),
      +        }}
      +      >
      +        {readme}
      +      
      +    
      +  );
      +
         return (
           
      -      
      +      
               {isLoading ? (
      -          
      +
      - {t('market.loading')} + {t('cloud.loading')}
      ) : plugin ? ( -
      - {/* 左侧:插件基本信息 */} -
      - {/* 插件图标和标题 */} -
      - {plugin.name} -
      -

      - {extractI18nObject(plugin.label) || plugin.name} -

      -
      - - - {plugin.author} / {plugin.name} - -
      - -
      - - v{plugin.latest_version} - - - - - - {plugin.install_count.toLocaleString()}{' '} - {t('market.downloads')} - - - - {plugin.repository && ( - { - e.stopPropagation(); - window.open(plugin.repository, '_blank'); - }} - > - - - )} -
      +
      + {/* 插件信息区域 */} +
      +
      +
      + +
      -
      - - {/* 插件描述 */} -
      -

      - {t('market.description')} -

      -

      - {extractI18nObject(plugin.description) || - t('market.noDescription')} -

      -
      - - {/* 标签 */} - {plugin.tags && plugin.tags.length > 0 && ( -
      -

      - {t('market.tags')} -

      -
      - {plugin.tags.map((tag) => ( - - {tag} - - ))} -
      +
      +
      - )} - - {/* 操作按钮 */} -
      - - {/* {plugin.repository && ( - - )} */}
      - {/* 右侧:README内容 */} -
      -
      + {/* README 区域 */} +
      +
      {isLoadingReadme ? (
      - {t('market.loading')} + {t('cloud.loading')}
      ) : ( -
      - ( -

      - {children} -

      - ), - h2: ({ children }) => ( -

      - {children} -

      - ), - h3: ({ children }) => ( -

      - {children} -

      - ), - p: ({ children }) => ( -

      - {children} -

      - ), - ul: ({ children }) => ( -
        - {children} -
      - ), - ol: ({ children }) => ( -
        - {children} -
      - ), - li: ({ children }) => ( -
    2. - {children} -
    3. - ), - code: ({ children, node }) => { - const isInline = - node?.children?.length === 1 && - node?.children[0]?.type === 'text'; - return isInline ? ( - - {children} - - ) : ( - - {children} - - ); - }, - blockquote: ({ children }) => ( -
      - {children} -
      - ), - a: ({ href, children }) => ( - - {children} - - ), - }} - > - {readme} -
      -
      + )}

+ ), + td: ({ ...props }) => ( + + ), + tr: ({ ...props }) => ( +