fix(style): model readme img render

This commit is contained in:
jialin
2024-09-30 16:33:38 +08:00
parent 5f77a0082c
commit ea25291d75
10 changed files with 190 additions and 109 deletions
+23 -2
View File
@@ -40,20 +40,24 @@
line-height: 2; line-height: 2;
} }
a * {
color: inherit;
}
table { table {
width: 100%; width: 100%;
th { th {
text-align: left; text-align: left;
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
line-height: 2; line-height: 1.5;
padding-inline: 6px; padding-inline: 6px;
border-bottom: 1px solid var(--ant-color-split); border-bottom: 1px solid var(--ant-color-split);
word-break: break-word; word-break: break-word;
} }
td { td {
line-height: 2; line-height: 1.5;
padding-inline: 6px; padding-inline: 6px;
border-bottom: 1px solid var(--ant-color-split); border-bottom: 1px solid var(--ant-color-split);
word-break: break-word; 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 { .item-token {
color: inherit; color: inherit;
font-size: inherit; font-size: inherit;
+124 -93
View File
@@ -2,7 +2,7 @@ import { EyeOutlined } from '@ant-design/icons';
import { Image, Typography } from 'antd'; import { Image, Typography } from 'antd';
import { unescape } from 'lodash'; import { unescape } from 'lodash';
import { TokensList, marked } from 'marked'; import { TokensList, marked } from 'marked';
import React, { Fragment, useCallback } from 'react'; import React, { Fragment, useCallback, useEffect } from 'react';
import HighlightCode from '../highlight-code'; import HighlightCode from '../highlight-code';
import './index.less'; import './index.less';
@@ -12,10 +12,12 @@ interface MarkdownViewerProps {
content: string; content: string;
height?: string; height?: string;
theme?: 'light' | 'dark'; theme?: 'light' | 'dark';
generateImgLink?: (src: string) => string;
} }
const MarkdownViewer: React.FC<MarkdownViewerProps> = ({ const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
content, content,
generateImgLink,
height = 'auto', height = 'auto',
theme = 'light' theme = 'light'
}) => { }) => {
@@ -39,109 +41,138 @@ const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
'escape' 'escape'
]; ];
const renderItem = useCallback((token: any, render: any) => { renderer.link = ({ href, title, text }) => {
if (!reDefineTypes.includes(token.type)) { return `<a href="${href}" title="${title || ''}" target="_blank" rel="noopener noreferrer">${text}</a>`;
console.log('token======66==', token.type, token); };
return (
<span
dangerouslySetInnerHTML={{
__html: marked.parser([token], { renderer })
}}
></span>
);
}
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 === 'escape') { const isValidURL = useCallback((url: string) => {
htmlstr = text; const pattern = /^(https?:\/\/|\/\/)([^\s/$.?#].[^\s]*)$/;
}
if (token.type === 'html') { return pattern.test(url);
htmlstr = <div dangerouslySetInnerHTML={{ __html: token.text }} />;
}
if (token.type === 'list') {
htmlstr = token.order ? (
<ol>{render?.(token.items, render)}</ol>
) : (
<ul>{render?.(token.items, render)}</ul>
);
}
if (token.type === 'list_item') {
htmlstr = <li>{text}</li>;
}
if (token.type === 'br') {
htmlstr = <br />;
}
if (token.type === 'em') {
htmlstr = <em>{text}</em>;
}
if (token.type === 'image') {
htmlstr = (
<Image
src={token.href}
alt={token.text}
preview={{
mask: <EyeOutlined />
}}
/>
);
}
if (token.type === 'text') {
htmlstr = text;
}
if (token.type === 'codespan') {
htmlstr = <Text code>{text}</Text>;
}
if (token.type === 'strong') {
htmlstr = <Text strong>{text}</Text>;
}
if (token.type === 'heading') {
htmlstr = <Typography.Title level={4}>{text}</Typography.Title>;
}
if (token.type === 'paragraph') {
htmlstr = <Paragraph> {text}</Paragraph>;
}
if (token.type === 'code') {
htmlstr = (
<HighlightCode theme={theme} code={token.text} lang={token.lang} />
);
}
if (token.type === 'link') {
htmlstr = (
<Link
href={token.href}
title={token.title || ''}
target="_blank"
rel="noopener noreferrer"
>
{text}
</Link>
);
}
if (token.type === 'hr') {
htmlstr = <hr className="hr" />;
}
return htmlstr;
}, []); }, []);
const renderItem = useCallback(
(token: any, render: any) => {
if (!reDefineTypes.includes(token.type)) {
return (
<span
dangerouslySetInnerHTML={{
__html: marked.parser([token], { renderer })
}}
></span>
);
}
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 === 'escape') {
htmlstr = text;
}
if (token.type === 'html') {
htmlstr = <div dangerouslySetInnerHTML={{ __html: token.text }} />;
}
if (token.type === 'list') {
htmlstr = token.order ? (
<ol>{render?.(token.items, render)}</ol>
) : (
<ul>{render?.(token.items, render)}</ul>
);
}
if (token.type === 'list_item') {
htmlstr = <li>{text}</li>;
}
if (token.type === 'br') {
htmlstr = <br />;
}
if (token.type === 'em') {
htmlstr = <em>{text}</em>;
}
if (token.type === 'image') {
let href = token.href;
if (!isValidURL(token.href)) {
href = generateImgLink ? generateImgLink(token.href) : token.href;
}
htmlstr = (
<Image
src={href}
alt={token.text}
preview={{
mask: <EyeOutlined />
}}
/>
);
}
if (token.type === 'text') {
htmlstr = text;
}
if (token.type === 'codespan') {
htmlstr = <Text code>{text}</Text>;
}
if (token.type === 'strong') {
htmlstr = <Text strong>{text}</Text>;
}
if (token.type === 'heading') {
htmlstr = <Typography.Title level={4}>{text}</Typography.Title>;
}
if (token.type === 'paragraph') {
htmlstr = <Paragraph> {text}</Paragraph>;
}
if (token.type === 'code') {
htmlstr = (
<HighlightCode theme={theme} code={token.text} lang={token.lang} />
);
}
if (token.type === 'link') {
htmlstr = (
<Link
href={token.href}
title={token.title || ''}
target="_blank"
rel="noopener noreferrer"
>
{text}
</Link>
);
}
if (token.type === 'hr') {
htmlstr = <hr className="hr" />;
}
return htmlstr;
},
[generateImgLink]
);
const renderTokens = (tokens: TokensList): any => { const renderTokens = (tokens: TokensList): any => {
return tokens?.map((token: any, index: number) => { return tokens?.map((token: any, index: number) => {
return <Fragment key={index}>{renderItem(token, renderTokens)}</Fragment>; return <Fragment key={index}>{renderItem(token, renderTokens)}</Fragment>;
}); });
}; };
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 ( return (
<div <div
style={{ height, overflow: 'auto' }} style={{ height }}
className="markdown-viewer custom-scrollbar-horizontal" className="markdown-viewer custom-scrollbar-horizontal"
> >
{renderTokens(tokens)} {renderTokens(tokens)}
+4
View File
@@ -548,6 +548,10 @@ body {
color: var(--ant-color-error) !important; color: var(--ant-color-error) !important;
} }
.ant-image .ant-image-mask {
border-radius: var(--border-radius-base);
}
.ant-message-notice-wrapper { .ant-message-notice-wrapper {
.ant-message-notice-error { .ant-message-notice-error {
.ant-message-notice-content { .ant-message-notice-content {
+2 -2
View File
@@ -197,12 +197,12 @@ export async function queryModelScopeModelDetail(
} }
export async function queryModelScopeModelFiles( export async function queryModelScopeModelFiles(
params: { name: string }, params: { name: string; revision: string },
options?: any options?: any
) { ) {
const res = await fetch( const res = await fetch(
`${MODE_SCOPE_MODEL_FIELS_API}${params.name}/repo/files?${qs.stringify({ `${MODE_SCOPE_MODEL_FIELS_API}${params.name}/repo/files?${qs.stringify({
Revision: 'master', Revision: params.revision,
Recursive: true, Recursive: true,
Root: '' Root: ''
})}`, })}`,
+4 -3
View File
@@ -1,3 +1,4 @@
import AutoTooltip from '@/components/auto-tooltip';
import { convertFileSize } from '@/utils'; import { convertFileSize } from '@/utils';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import _ from 'lodash'; import _ from 'lodash';
@@ -12,12 +13,12 @@ const GPUCard: React.FC<{
const intl = useIntl(); const intl = useIntl();
return ( return (
<div className="gpu-card"> <div className="gpu-card">
<div className="header"> <div className="header" style={{ width: '100%' }}>
{header ?? ( {header ?? (
<> <AutoTooltip ghost>
{data.label}({data.worker_name})[ {data.label}({data.worker_name})[
{intl.formatMessage({ id: 'resources.table.index' })}:{data.index}] {intl.formatMessage({ id: 'resources.table.index' })}:{data.index}]
</> </AutoTooltip>
)} )}
</div> </div>
<div className="info"> <div className="info">
@@ -153,7 +153,8 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
try { try {
const data = await queryModelScopeModelFiles( const data = await queryModelScopeModelFiles(
{ {
name: props.selectedModel.name || '' name: props.selectedModel.name || '',
revision: props.selectedModel.revision || 'master'
}, },
{ {
signal: axiosTokenRef.current.signal signal: axiosTokenRef.current.signal
+28 -2
View File
@@ -150,7 +150,10 @@ const ModelCard: React.FC<{
target="_blank" target="_blank"
href={`https://huggingface.co/${modelData?.id}`} href={`https://huggingface.co/${modelData?.id}`}
> >
<IconFont type="icon-external-link"></IconFont> <IconFont
type="icon-external-link"
className="font-size-14"
></IconFont>
</Button> </Button>
</Tooltip> </Tooltip>
); );
@@ -165,7 +168,10 @@ const ModelCard: React.FC<{
target="_blank" target="_blank"
href={`https://modelscope.cn/models/${modelData?.name}`} href={`https://modelscope.cn/models/${modelData?.name}`}
> >
<IconFont type="icon-external-link"></IconFont> <IconFont
type="icon-external-link"
className="font-size-14"
></IconFont>
</Button> </Button>
</Tooltip> </Tooltip>
); );
@@ -173,6 +179,16 @@ const ModelCard: React.FC<{
return null; 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(() => { useEffect(() => {
getModelCardData(); getModelCardData();
}, [props.selectedModel.name]); }, [props.selectedModel.name]);
@@ -240,6 +256,11 @@ const ModelCard: React.FC<{
}} }}
> >
<MarkdownViewer <MarkdownViewer
generateImgLink={
modelSource === modelSourceMap.modelscope_value
? generateModeScopeImgLink
: undefined
}
content={readmeText} content={readmeText}
theme="light" theme="light"
></MarkdownViewer> ></MarkdownViewer>
@@ -262,6 +283,11 @@ const ModelCard: React.FC<{
<div className="card-wrapper"> <div className="card-wrapper">
<Spin spinning={loading}> <Spin spinning={loading}>
<MarkdownViewer <MarkdownViewer
generateImgLink={
modelSource === modelSourceMap.modelscope_value
? generateModeScopeImgLink
: undefined
}
content={readmeText} content={readmeText}
theme="light" theme="light"
></MarkdownViewer> ></MarkdownViewer>
@@ -118,6 +118,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
likes: item.Stars, likes: item.Stars,
value: item.Name, value: item.Name,
label: item.Name, label: item.Name,
revision: item.Revision,
task: item.Tasks?.map((sItem: any) => sItem.Name).join(',') task: item.Tasks?.map((sItem: any) => sItem.Name).join(',')
}; };
}); });
@@ -321,7 +321,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
}); });
} }
return list; return list;
}, [modelList]); }, [modelList, intl]);
useEffect(() => { useEffect(() => {
console.log('globalParams:', globalParams.model, globalParams); console.log('globalParams:', globalParams.model, globalParams);
@@ -177,11 +177,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
} }
]} ]}
> >
<SealSelect <SealSelect showSearch options={modelList}></SealSelect>
showSearch
options={modelList}
label={intl.formatMessage({ id: 'playground.model' })}
></SealSelect>
</Form.Item> </Form.Item>
</> </>
)} )}