diff --git a/src/components/markdown-viewer/index.less b/src/components/markdown-viewer/index.less index 6fb99c44..5a020d7e 100644 --- a/src/components/markdown-viewer/index.less +++ b/src/components/markdown-viewer/index.less @@ -40,20 +40,24 @@ line-height: 2; } + a * { + color: inherit; + } + table { width: 100%; th { text-align: left; font-weight: var(--font-weight-bold); - line-height: 2; + line-height: 1.5; padding-inline: 6px; border-bottom: 1px solid var(--ant-color-split); word-break: break-word; } td { - line-height: 2; + line-height: 1.5; padding-inline: 6px; border-bottom: 1px solid var(--ant-color-split); word-break: break-word; @@ -70,6 +74,23 @@ } } + img { + max-width: 100%; + height: auto; + } + + video { + max-width: 100%; + height: auto; + border-radius: var(--border-radius-base); + } + + audio { + max-width: 100%; + height: auto; + border-radius: var(--border-radius-base); + } + .item-token { color: inherit; font-size: inherit; diff --git a/src/components/markdown-viewer/index.tsx b/src/components/markdown-viewer/index.tsx index cdf3bfab..3eb1b3fa 100644 --- a/src/components/markdown-viewer/index.tsx +++ b/src/components/markdown-viewer/index.tsx @@ -2,7 +2,7 @@ import { EyeOutlined } from '@ant-design/icons'; import { Image, Typography } from 'antd'; import { unescape } from 'lodash'; import { TokensList, marked } from 'marked'; -import React, { Fragment, useCallback } from 'react'; +import React, { Fragment, useCallback, useEffect } from 'react'; import HighlightCode from '../highlight-code'; import './index.less'; @@ -12,10 +12,12 @@ interface MarkdownViewerProps { content: string; height?: string; theme?: 'light' | 'dark'; + generateImgLink?: (src: string) => string; } const MarkdownViewer: React.FC = ({ content, + generateImgLink, height = 'auto', theme = 'light' }) => { @@ -39,109 +41,138 @@ const MarkdownViewer: React.FC = ({ 'escape' ]; - const renderItem = useCallback((token: any, render: any) => { - if (!reDefineTypes.includes(token.type)) { - console.log('token======66==', token.type, token); - return ( - - ); - } - let htmlstr: any = null; - let child: any = null; - if (token.tokens?.length) { - child = render?.(token.tokens as TokensList, render); - } - const text = child ? child : unescape(token.text); + renderer.link = ({ href, title, text }) => { + return `${text}`; + }; - if (token.type === 'escape') { - htmlstr = text; - } + const isValidURL = useCallback((url: string) => { + const pattern = /^(https?:\/\/|\/\/)([^\s/$.?#].[^\s]*)$/; - if (token.type === 'html') { - htmlstr =
; - } - if (token.type === 'list') { - htmlstr = token.order ? ( -
    {render?.(token.items, render)}
- ) : ( -
    {render?.(token.items, render)}
- ); - } + return pattern.test(url); + }, []); - if (token.type === 'list_item') { - htmlstr =
  • {text}
  • ; - } + const renderItem = useCallback( + (token: any, render: any) => { + if (!reDefineTypes.includes(token.type)) { + return ( + + ); + } + let htmlstr: any = null; + let child: any = null; + if (token.tokens?.length) { + child = render?.(token.tokens as TokensList, render); + } + const text = child ? child : unescape(token.text); - if (token.type === 'br') { - htmlstr =
    ; - } + if (token.type === 'escape') { + htmlstr = text; + } - if (token.type === 'em') { - htmlstr = {text}; - } + if (token.type === 'html') { + htmlstr =
    ; + } + if (token.type === 'list') { + htmlstr = token.order ? ( +
      {render?.(token.items, render)}
    + ) : ( +
      {render?.(token.items, render)}
    + ); + } - if (token.type === 'image') { - htmlstr = ( - {token.text} - }} - /> - ); - } - if (token.type === 'text') { - htmlstr = text; - } - if (token.type === 'codespan') { - htmlstr = {text}; - } - if (token.type === 'strong') { - htmlstr = {text}; - } - if (token.type === 'heading') { - htmlstr = {text}; - } - if (token.type === 'paragraph') { - htmlstr = {text}; - } - if (token.type === 'code') { - htmlstr = ( - - ); - } - if (token.type === 'link') { - htmlstr = ( - - {text} - - ); - } - if (token.type === 'hr') { - htmlstr =
    ; - } + if (token.type === 'list_item') { + htmlstr =
  • {text}
  • ; + } - return htmlstr; - }, []); + if (token.type === 'br') { + htmlstr =
    ; + } + + if (token.type === 'em') { + htmlstr = {text}; + } + + if (token.type === 'image') { + let href = token.href; + if (!isValidURL(token.href)) { + href = generateImgLink ? generateImgLink(token.href) : token.href; + } + htmlstr = ( + {token.text} + }} + /> + ); + } + if (token.type === 'text') { + htmlstr = text; + } + if (token.type === 'codespan') { + htmlstr = {text}; + } + if (token.type === 'strong') { + htmlstr = {text}; + } + if (token.type === 'heading') { + htmlstr = {text}; + } + if (token.type === 'paragraph') { + htmlstr = {text}; + } + if (token.type === 'code') { + htmlstr = ( + + ); + } + if (token.type === 'link') { + htmlstr = ( + + {text} + + ); + } + if (token.type === 'hr') { + htmlstr =
    ; + } + + return htmlstr; + }, + [generateImgLink] + ); const renderTokens = (tokens: TokensList): any => { return tokens?.map((token: any, index: number) => { return {renderItem(token, renderTokens)}; }); }; + useEffect(() => { + if (!content) { + return; + } + const imgs = document.querySelectorAll('.markdown-viewer img'); + imgs.forEach((img) => { + const src = img.getAttribute('src'); + if (src && !isValidURL(src)) { + img.setAttribute('src', generateImgLink ? generateImgLink(src) : src); + } + }); + }, [content, generateImgLink]); + return (
    {renderTokens(tokens)} diff --git a/src/global.less b/src/global.less index daa66a67..74d07b5f 100644 --- a/src/global.less +++ b/src/global.less @@ -548,6 +548,10 @@ body { color: var(--ant-color-error) !important; } +.ant-image .ant-image-mask { + border-radius: var(--border-radius-base); +} + .ant-message-notice-wrapper { .ant-message-notice-error { .ant-message-notice-content { diff --git a/src/pages/llmodels/apis/index.ts b/src/pages/llmodels/apis/index.ts index e8ba5cd5..bee050c4 100644 --- a/src/pages/llmodels/apis/index.ts +++ b/src/pages/llmodels/apis/index.ts @@ -197,12 +197,12 @@ export async function queryModelScopeModelDetail( } export async function queryModelScopeModelFiles( - params: { name: string }, + params: { name: string; revision: string }, options?: any ) { const res = await fetch( `${MODE_SCOPE_MODEL_FIELS_API}${params.name}/repo/files?${qs.stringify({ - Revision: 'master', + Revision: params.revision, Recursive: true, Root: '' })}`, diff --git a/src/pages/llmodels/components/gpu-card.tsx b/src/pages/llmodels/components/gpu-card.tsx index 762fb0cd..8f74e712 100644 --- a/src/pages/llmodels/components/gpu-card.tsx +++ b/src/pages/llmodels/components/gpu-card.tsx @@ -1,3 +1,4 @@ +import AutoTooltip from '@/components/auto-tooltip'; import { convertFileSize } from '@/utils'; import { useIntl } from '@umijs/max'; import _ from 'lodash'; @@ -12,12 +13,12 @@ const GPUCard: React.FC<{ const intl = useIntl(); return (
    -
    +
    {header ?? ( - <> + {data.label}({data.worker_name})[ {intl.formatMessage({ id: 'resources.table.index' })}:{data.index}] - + )}
    diff --git a/src/pages/llmodels/components/hf-model-file.tsx b/src/pages/llmodels/components/hf-model-file.tsx index 0fc70bdb..79a00474 100644 --- a/src/pages/llmodels/components/hf-model-file.tsx +++ b/src/pages/llmodels/components/hf-model-file.tsx @@ -153,7 +153,8 @@ const HFModelFile: React.FC = forwardRef((props, ref) => { try { const data = await queryModelScopeModelFiles( { - name: props.selectedModel.name || '' + name: props.selectedModel.name || '', + revision: props.selectedModel.revision || 'master' }, { signal: axiosTokenRef.current.signal diff --git a/src/pages/llmodels/components/model-card.tsx b/src/pages/llmodels/components/model-card.tsx index 811625c3..699f0c84 100644 --- a/src/pages/llmodels/components/model-card.tsx +++ b/src/pages/llmodels/components/model-card.tsx @@ -150,7 +150,10 @@ const ModelCard: React.FC<{ target="_blank" href={`https://huggingface.co/${modelData?.id}`} > - + ); @@ -165,7 +168,10 @@ const ModelCard: React.FC<{ target="_blank" href={`https://modelscope.cn/models/${modelData?.name}`} > - + ); @@ -173,6 +179,16 @@ const ModelCard: React.FC<{ return null; }; + const generateModeScopeImgLink = useCallback( + (imgSrc: string) => { + if (!imgSrc) { + return ''; + } + return `https://modelscope.cn/api/v1/models/${modelData?.name}/repo?Revision=${modelData?.Revision}&View=true&FilePath=${imgSrc}`; + }, + [modelData] + ); + useEffect(() => { getModelCardData(); }, [props.selectedModel.name]); @@ -240,6 +256,11 @@ const ModelCard: React.FC<{ }} > @@ -262,6 +283,11 @@ const ModelCard: React.FC<{
    diff --git a/src/pages/llmodels/components/search-model.tsx b/src/pages/llmodels/components/search-model.tsx index a2823434..3003d468 100644 --- a/src/pages/llmodels/components/search-model.tsx +++ b/src/pages/llmodels/components/search-model.tsx @@ -118,6 +118,7 @@ const SearchModel: React.FC = (props) => { likes: item.Stars, value: item.Name, label: item.Name, + revision: item.Revision, task: item.Tasks?.map((sItem: any) => sItem.Name).join(',') }; }); diff --git a/src/pages/playground/components/multiple-chat/model-item.tsx b/src/pages/playground/components/multiple-chat/model-item.tsx index a1aa9d32..20699241 100644 --- a/src/pages/playground/components/multiple-chat/model-item.tsx +++ b/src/pages/playground/components/multiple-chat/model-item.tsx @@ -321,7 +321,7 @@ const ModelItem: React.FC = forwardRef( }); } return list; - }, [modelList]); + }, [modelList, intl]); useEffect(() => { console.log('globalParams:', globalParams.model, globalParams); diff --git a/src/pages/playground/components/params-settings.tsx b/src/pages/playground/components/params-settings.tsx index a830d547..dc8e90c5 100644 --- a/src/pages/playground/components/params-settings.tsx +++ b/src/pages/playground/components/params-settings.tsx @@ -177,11 +177,7 @@ const ParamsSettings: React.FC = ({ } ]} > - + )}