style: search models style

This commit is contained in:
jialin
2024-08-23 17:17:02 +08:00
parent 76444f28cb
commit a60be9c88f
24 changed files with 571 additions and 115 deletions
+2 -1
View File
@@ -3,6 +3,7 @@ module.exports = {
rules: { rules: {
'react/no-unstable-nested-components': 1, 'react/no-unstable-nested-components': 1,
'no-unused-vars': 'off', 'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'off' '@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/class-name-casing': 'off'
} }
}; };
+3
View File
@@ -1,3 +1,6 @@
module.exports = { module.exports = {
extends: require.resolve('@umijs/max/stylelint'), extends: require.resolve('@umijs/max/stylelint'),
rules: {
'selector-class-pattern': null
}
}; };
+20
View File
@@ -2,6 +2,14 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
.m-b-8 {
margin-bottom: 8px;
}
.m-b-5 {
margin-bottom: 5px;
}
.m-l-10 { .m-l-10 {
margin-left: 10px; margin-left: 10px;
} }
@@ -10,6 +18,10 @@
margin-left: 5px; margin-left: 5px;
} }
.m-l-2 {
margin-left: 2px;
}
.m-l-8 { .m-l-8 {
margin-left: 8px; margin-left: 8px;
} }
@@ -75,6 +87,14 @@
gap: 5px; gap: 5px;
} }
.gap-10 {
gap: 10px;
}
.gap-8 {
gap: 8px;
}
.relative { .relative {
position: relative; position: relative;
} }
@@ -0,0 +1,77 @@
import hljs from 'highlight.js';
import { memo, useMemo } from 'react';
import CopyButton from '../copy-button';
import './styles/light.less';
import { escapeHtml } from './utils';
interface CodeViewerProps {
code: string;
lang: string;
autodetect?: boolean;
ignoreIllegals?: boolean;
copyable?: boolean;
}
const CodeViewer: React.FC<CodeViewerProps> = (props) => {
const {
code,
lang,
autodetect = true,
ignoreIllegals = true,
copyable = true
} = props || {};
const highlightedCode = useMemo(() => {
const autodetectLang = autodetect && !lang;
const cannotDetectLanguage = !autodetectLang && !hljs.getLanguage(lang);
let className = '';
if (!cannotDetectLanguage) {
className = `hljs ${lang}`;
}
// No idea what language to use, return raw code
if (cannotDetectLanguage) {
console.warn(`The language "${lang}" you specified could not be found.`);
return {
value: escapeHtml(code),
className: className
};
}
if (autodetectLang) {
const result = hljs.highlightAuto(code);
return {
value: result.value,
className: className
};
}
const result = hljs.highlight(code, {
language: lang,
ignoreIllegals: ignoreIllegals
});
return {
value: result.value,
className: className
};
}, [code, lang, autodetect, ignoreIllegals]);
return (
<pre className="code-pre light">
<code
className={highlightedCode.className}
dangerouslySetInnerHTML={{
__html: highlightedCode.value
}}
></code>
{copyable && (
<CopyButton
text={code}
size="small"
style={{ color: '#abb2bf' }}
></CopyButton>
)}
</pre>
);
};
export default memo(CodeViewer);
@@ -1,7 +1,7 @@
import hljs from 'highlight.js'; import hljs from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import CopyButton from '../copy-button'; import CopyButton from '../copy-button';
import './styles/dark.less';
import { escapeHtml } from './utils'; import { escapeHtml } from './utils';
interface CodeViewerProps { interface CodeViewerProps {
@@ -56,7 +56,7 @@ const CodeViewer: React.FC<CodeViewerProps> = (props) => {
}, [code, lang, autodetect, ignoreIllegals]); }, [code, lang, autodetect, ignoreIllegals]);
return ( return (
<pre className="code-pre"> <pre className="code-pre dark">
<code <code
className={highlightedCode.className} className={highlightedCode.className}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
+9 -3
View File
@@ -1,16 +1,22 @@
import CodeViewer from './code-viewer'; import CodeViewer from './code-viewer';
import './style.less'; import CodeViewerLight from './code-viewer-light';
import './styles/index.less';
const HighlightCode: React.FC<{ const HighlightCode: React.FC<{
code: string; code: string;
lang?: string; lang?: string;
copyable?: boolean; copyable?: boolean;
theme?: 'light' | 'dark';
}> = (props) => { }> = (props) => {
const { code, lang = 'bash', copyable = true } = props; const { code, lang = 'bash', copyable = true, theme = 'dark' } = props;
return ( return (
<div className="high-light-wrapper"> <div className="high-light-wrapper">
<CodeViewer lang={lang} code={code} copyable={copyable} /> {theme === 'dark' ? (
<CodeViewer lang={lang} code={code} copyable={copyable} />
) : (
<CodeViewerLight lang={lang} code={code} copyable={copyable} />
)}
</div> </div>
); );
}; };
@@ -0,0 +1,106 @@
// @ts-ingore
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
}
/*
Atom One Dark by Daniel Gamage
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
base: #282c34
mono-1: #abb2bf
mono-2: #818896
mono-3: #5c6370
hue-1: #56b6c2
hue-2: #61aeee
hue-3: #c678dd
hue-4: #98c379
hue-5: #e06c75
hue-5-2: #be5046
hue-6: #d19a66
hue-6-2: #e6c07b
*/
.code-pre.dark {
.hljs {
color: #abb2bf;
background: #282c34;
}
.hljs-comment,
.hljs-quote {
color: #5c6370;
font-style: italic;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #c678dd;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e06c75;
}
.hljs-literal {
color: #56b6c2;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta .hljs-string {
color: #98c379;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #d19a66;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #61aeee;
}
.hljs-built_in,
.hljs-title.class_,
.hljs-class .hljs-title {
color: #e6c07b;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}
}
@@ -27,7 +27,6 @@
.code-pre { .code-pre {
padding-inline: 12px 32px; padding-inline: 12px 32px;
position: relative; position: relative;
background-color: #282c34;
border-radius: var(--border-radius-mini); border-radius: var(--border-radius-mini);
.copy-button { .copy-button {
@@ -35,5 +34,13 @@
top: 6px; top: 6px;
right: 6px; right: 6px;
} }
&.dark {
background-color: #282c34;
}
&.light {
background-color: rgb(250, 250, 250);
}
} }
} }
@@ -0,0 +1,106 @@
// @ts-ingore
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
}
/*
Atom One Light by Daniel Gamage
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
base: #fafafa
mono-1: #383a42
mono-2: #686b77
mono-3: #a0a1a7
hue-1: #0184bb
hue-2: #4078f2
hue-3: #a626a4
hue-4: #50a14f
hue-5: #e45649
hue-5-2: #c91243
hue-6: #986801
hue-6-2: #c18401
*/
.code-pre.light {
.hljs {
color: #383a42;
background: #fafafa;
}
.hljs-comment,
.hljs-quote {
color: #a0a1a7;
font-style: italic;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #a626a4;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e45649;
}
.hljs-literal {
color: #0184bb;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta .hljs-string {
color: #50a14f;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #986801;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #4078f2;
}
.hljs-built_in,
.hljs-title.class_,
.hljs-class .hljs-title {
color: #c18401;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}
}
+3 -1
View File
@@ -33,5 +33,7 @@ export default {
'models.search.noresult': 'No related models found', 'models.search.noresult': 'No related models found',
'models.search.nofiles': 'No available files', 'models.search.nofiles': 'No available files',
'models.search.networkerror': 'Network connection exception!', 'models.search.networkerror': 'Network connection exception!',
'models.search.hfvisit': 'Please make sure you can visit' 'models.search.hfvisit': 'Please make sure you can visit',
'models.search.unsupport':
'This model is not supported and may be unusable after deployment.'
}; };
+2 -1
View File
@@ -33,5 +33,6 @@ export default {
'models.search.noresult': '未找到相关模型', 'models.search.noresult': '未找到相关模型',
'models.search.nofiles': '无可用文件', 'models.search.nofiles': '无可用文件',
'models.search.networkerror': '网络连接异常!', 'models.search.networkerror': '网络连接异常!',
'models.search.hfvisit': '请确保您可以访问' 'models.search.hfvisit': '请确保您可以访问',
'models.search.unsupport': '暂不支持该模型,部署后可能无法使用'
}; };
+4 -1
View File
@@ -148,7 +148,10 @@ export async function queryHuggingfaceModels(
additionalFields: ['sha'], additionalFields: ['sha'],
fetch(url: string, config: any) { fetch(url: string, config: any) {
try { try {
return fetch(`${url}&sort=${params.search.sort}`, { const newUrl = params.search.sort
? `${url}&sort=${params.search.sort}`
: url;
return fetch(`${newUrl}`, {
...config, ...config,
signal: options.signal signal: options.signal
}); });
@@ -0,0 +1,26 @@
import { convertFileSize } from '@/utils';
import React from 'react';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
const FileParts: React.FC<{
fileList: any[];
}> = ({ fileList }) => {
return (
<SimpleBar style={{ maxHeight: 200 }}>
{fileList.map((file, index) => {
return (
<div key={index} className="flex-between m-b-5">
<span>
{' '}
Part {file.part} of {file.total}
</span>
<span>{convertFileSize(file.size)}</span>
</div>
);
})}
</SimpleBar>
);
};
export default FileParts;
+100 -13
View File
@@ -1,14 +1,16 @@
import { convertFileSize } from '@/utils'; import { convertFileSize } from '@/utils';
import { InfoCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Col, Empty, Row, Select, Space, Spin, Tag } from 'antd'; import { Col, Empty, Row, Select, Spin, Tag, Tooltip } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import _ from 'lodash'; import _ from 'lodash';
import { useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import SimpleBar from 'simplebar-react'; import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css'; import 'simplebar-react/dist/simplebar.min.css';
import { queryHuggingfaceModelFiles } from '../apis'; import { queryHuggingfaceModelFiles } from '../apis';
import FileType from '../config/file-type'; import FileType from '../config/file-type';
import '../style/hf-model-file.less'; import '../style/hf-model-file.less';
import FileParts from './file-parts';
import TitleWrapper from './title-wrapper'; import TitleWrapper from './title-wrapper';
interface HFModelFileProps { interface HFModelFileProps {
@@ -18,6 +20,8 @@ interface HFModelFileProps {
onSelectFile?: (file: any) => void; onSelectFile?: (file: any) => void;
} }
const pattern = /^(.*)-(\d+)-of-(\d+)\.gguf$/;
const HFModelFile: React.FC<HFModelFileProps> = (props) => { const HFModelFile: React.FC<HFModelFileProps> = (props) => {
const { collapsed, loadingModel } = props; const { collapsed, loadingModel } = props;
const intl = useIntl(); const intl = useIntl();
@@ -44,6 +48,50 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
setCurrent(item.path); setCurrent(item.path);
}; };
const parseFilename = (filename: string) => {
const match = filename.match(pattern);
if (match) {
return {
filename: match[1],
part: parseInt(match[2], 10),
total: parseInt(match[3], 10)
};
} else {
return null;
}
};
const generateGroupByFilename = useCallback((list: any[]) => {
const data = _.find(list, (item: any) => {
const parsed = parseFilename(item.path);
return !!parsed;
});
// general file
if (!data) {
return list;
}
const newList = _.map(list, (item: any) => {
const parsed = parseFilename(item.path);
return {
...item,
...parsed
};
});
const group = _.groupBy(newList, 'filename');
return _.map(group, (value: any[], key: string) => {
return {
path: key,
size: _.sumBy(value, 'size'),
parts: value
};
});
}, []);
const handleFetchModelFiles = async () => { const handleFetchModelFiles = async () => {
if (!props.repo) { if (!props.repo) {
setDataSource({ fileList: [], loading: false }); setDataSource({ fileList: [], loading: false });
@@ -68,11 +116,15 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
const list = _.filter(fileList, (file: any) => { const list = _.filter(fileList, (file: any) => {
return _.endsWith(file.path, '.gguf') || _.includes(file.path, '.gguf'); return _.endsWith(file.path, '.gguf') || _.includes(file.path, '.gguf');
}); });
const sortList = _.sortBy(list, (item: any) => {
const newList = generateGroupByFilename(list);
console.log('newList==========', newList);
const sortList = _.sortBy(newList, (item: any) => {
return sortType === 'size' ? item.size : item.path; return sortType === 'size' ? item.size : item.path;
}); });
setDataSource({ fileList: sortList, loading: false }); setDataSource({ fileList: sortList, loading: false });
handleSelectModelFile(list[0]); handleSelectModelFile(sortList[0]);
} catch (error) { } catch (error) {
setDataSource({ fileList: [], loading: false }); setDataSource({ fileList: [], loading: false });
handleSelectModelFile({}); handleSelectModelFile({});
@@ -87,22 +139,33 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
setDataSource({ ...dataSource, fileList: list }); setDataSource({ ...dataSource, fileList: list });
}; };
const getModelQuantizationType = (item: any) => { const getModelQuantizationType = useCallback((item: any) => {
const name = _.split(item.path, '.').slice(0, -1).join('.'); let itemPath = item.path;
let path = _.split(itemPath, '/').pop();
if (!_.endsWith(path, '.gguf') && !_.includes(path, '.gguf')) {
path = `${path}.gguf`;
}
const name = _.split(path, '.').slice(0, -1).join('.');
let quanType = _.toUpper(name.split('-').slice(-1)[0]); let quanType = _.toUpper(name.split('-').slice(-1)[0]);
if (quanType.indexOf('.') > -1) { if (quanType.indexOf('.') > -1) {
quanType = _.split(quanType, '.').pop(); quanType = _.split(quanType, '.').pop();
} }
console.log('quanType', quanType, FileType[quanType]);
if (FileType[quanType] !== undefined) { if (FileType[quanType] !== undefined) {
return ( return (
<Tag className="tag-item" color="cyan"> <Tag
className="tag-item"
color="cyan"
style={{
marginRight: 0
}}
>
{quanType} {quanType}
</Tag> </Tag>
); );
} }
return null; return null;
}; }, []);
const handleOnEnter = (e: any, item: any) => { const handleOnEnter = (e: any, item: any) => {
e.stopPropagation(); e.stopPropagation();
@@ -143,7 +206,7 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
></Select> ></Select>
</TitleWrapper> </TitleWrapper>
<SimpleBar <SimpleBar
style={{ maxHeight: collapsed ? 'max-content' : 'calc(100vh - 330px)' }} style={{ maxHeight: collapsed ? 'max-content' : 'calc(100vh - 300px)' }}
> >
<div style={{ padding: '16px 24px' }}> <div style={{ padding: '16px 24px' }}>
<Spin <Spin
@@ -164,7 +227,7 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
onKeyDown={(e) => handleOnEnter(e, item)} onKeyDown={(e) => handleOnEnter(e, item)}
> >
<div className="title">{item.path}</div> <div className="title">{item.path}</div>
<Space className="tags"> <div className="tags">
<Tag <Tag
className="tag-item" className="tag-item"
color="green" color="green"
@@ -177,8 +240,32 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
</span> </span>
</Tag> </Tag>
{getModelQuantizationType(item)} {getModelQuantizationType(item)}
</Space> {item.parts && item.parts.length > 1 && (
<div className="btn"></div> <Tooltip
color="var(--color-white-1)"
overlayInnerStyle={{
width: 150,
color: 'var(--ant-color-text-secondary)'
}}
title={
<FileParts fileList={item.parts}></FileParts>
}
>
<Tag
className="tag-item"
color="purple"
style={{
marginRight: 0
}}
>
<span style={{ opacity: 1 }}>
<InfoCircleOutlined className="m-r-5" />
{item.parts.length} parts
</span>
</Tag>
</Tooltip>
)}
</div>
</div> </div>
</Col> </Col>
); );
+24 -10
View File
@@ -2,9 +2,11 @@ import { formatNumber } from '@/utils';
import { import {
DownloadOutlined, DownloadOutlined,
FolderOutlined, FolderOutlined,
HeartOutlined HeartOutlined,
WarningOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Space, Tag } from 'antd'; import { useIntl } from '@umijs/max';
import { Tag, Tooltip } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
@@ -21,8 +23,18 @@ interface HFModelItemProps {
source?: string; source?: string;
tags?: string[]; tags?: string[];
} }
const warningTask = ['image', 'audio', 'video'];
const HFModelItem: React.FC<HFModelItemProps> = (props) => { const HFModelItem: React.FC<HFModelItemProps> = (props) => {
const intl = useIntl();
const isExcludeTask = () => {
if (!props.task) {
return false;
}
return _.some(warningTask, (item: string) => {
return props.task?.toLowerCase().includes(item);
});
};
return ( return (
<div <div
tabIndex={0} tabIndex={0}
@@ -36,10 +48,17 @@ const HFModelItem: React.FC<HFModelItemProps> = (props) => {
style={{ color: 'var(--ant-color-text-tertiary)' }} style={{ color: 'var(--ant-color-text-tertiary)' }}
/> />
{props.title} {props.title}
{isExcludeTask() && (
<Tooltip
title={intl.formatMessage({ id: 'models.search.unsupport' })}
>
<WarningOutlined className="m-l-2" style={{ color: 'orange' }} />
</Tooltip>
)}
</div> </div>
<div className="info"> <div className="info">
{props.source === modelSourceMap.huggingface_value ? ( {props.source === modelSourceMap.huggingface_value ? (
<Space size={16}> <div className="info-item">
{/* {props.task && ( {/* {props.task && (
<Tag <Tag
className="tag-item" className="tag-item"
@@ -64,10 +83,10 @@ const HFModelItem: React.FC<HFModelItemProps> = (props) => {
<DownloadOutlined className="m-r-5" /> <DownloadOutlined className="m-r-5" />
{formatNumber(props.downloads)} {formatNumber(props.downloads)}
</span> </span>
</Space> </div>
) : ( ) : (
<div className="flex-between"> <div className="flex-between">
<Space size={10}> <div className="tags">
{_.map(props.tags, (tag: string, index: string) => { {_.map(props.tags, (tag: string, index: string) => {
return ( return (
<Tag <Tag
@@ -83,11 +102,6 @@ const HFModelItem: React.FC<HFModelItemProps> = (props) => {
</Tag> </Tag>
); );
})} })}
</Space>
<div className="btn">
{/* <Button size="middle">
{props.active ? 'Selected' : 'Select'}
</Button> */}
</div> </div>
</div> </div>
)} )}
+15 -16
View File
@@ -98,24 +98,23 @@ const ModelCard: React.FC<{
return ( return (
<> <>
<TitleWrapper> <TitleWrapper>
<span>{intl.formatMessage({ id: 'models.data.card' })}</span> <div className="title">{modelData?.id} </div>
{modelData?.id && (
<Tooltip title={intl.formatMessage({ id: 'models.viewin.hf' })}>
<Button
size="small"
type="link"
target="_blank"
href={`https://huggingface.co/${modelData.id}`}
>
<IconFont type="icon-external-link"></IconFont>
</Button>
</Tooltip>
)}
</TitleWrapper> </TitleWrapper>
<div className="wrapper"> <div className="card-wrapper">
{modelData ? ( {modelData ? (
<div className="model-card-wrap"> <div className="model-card-wrap">
<div className="title">
{modelData.id}{' '}
<Tooltip title={intl.formatMessage({ id: 'models.viewin.hf' })}>
<Button
size="small"
type="link"
target="_blank"
href={`https://huggingface.co/${modelData.id}`}
>
<IconFont type="icon-external-link"></IconFont>
</Button>
</Tooltip>
</div>
<div className="flex-between flex-center"> <div className="flex-between flex-center">
{modelData.config?.model_type && ( {modelData.config?.model_type && (
<Tag className="tag-item" color="gold"> <Tag className="tag-item" color="gold">
@@ -132,7 +131,6 @@ const ModelCard: React.FC<{
<div <div
style={{ style={{
borderRadius: 4, borderRadius: 4,
backgroundColor: '#282c34',
marginTop: 16, marginTop: 16,
overflow: 'hidden' overflow: 'hidden'
}} }}
@@ -154,6 +152,7 @@ const ModelCard: React.FC<{
code={readmeText} code={readmeText}
lang="markdown" lang="markdown"
copyable={false} copyable={false}
theme="light"
></HighlightCode> ></HighlightCode>
</SimpleBar> </SimpleBar>
</div> </div>
+1 -15
View File
@@ -1,9 +1,8 @@
import IconFont from '@/components/icon-font';
import hotkeys from '@/config/hotkeys'; import hotkeys from '@/config/hotkeys';
import { platformCall } from '@/utils'; import { platformCall } from '@/utils';
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Input, Tag } from 'antd'; import { Input } from 'antd';
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
@@ -31,19 +30,6 @@ const SearchInput: React.FC<{
placeholder={intl.formatMessage({ placeholder={intl.formatMessage({
id: 'model.deploy.search.placeholder' id: 'model.deploy.search.placeholder'
})} })}
suffix={
!isFocus && (
<Tag style={{ marginRight: 0 }}>
{platform.isMac ? (
<>
<IconFont type="icon-command"></IconFont> + K
</>
) : (
<>CTRL + K</>
)}
</Tag>
)
}
prefix={ prefix={
<> <>
<SearchOutlined <SearchOutlined
+11 -6
View File
@@ -65,6 +65,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
axiosTokenRef.current?.abort?.(); axiosTokenRef.current?.abort?.();
axiosTokenRef.current = new AbortController(); axiosTokenRef.current = new AbortController();
if (dataSource.loading) return; if (dataSource.loading) return;
const sort = sortType ?? dataSource.sortType;
try { try {
setDataSource((pre) => { setDataSource((pre) => {
pre.loading = true; pre.loading = true;
@@ -72,17 +73,19 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
}); });
setLoadingModel?.(true); setLoadingModel?.(true);
cacheRepoOptions.current = []; cacheRepoOptions.current = [];
const task: any = searchInputRef.current ? '' : 'text-generation';
const params = { const params = {
search: { search: {
query: searchInputRef.current || '', query: searchInputRef.current || '',
sort: sortType || dataSource.sortType, sort: sort,
tags: ['gguf'] tags: ['gguf'],
task
} }
}; };
const models = await queryHuggingfaceModels(params, { const models = await queryHuggingfaceModels(params, {
signal: axiosTokenRef.current.signal signal: axiosTokenRef.current.signal
}); });
const list = _.map(models || [], (item: any) => { let list = _.map(models || [], (item: any) => {
return { return {
...item, ...item,
value: item.name, value: item.name,
@@ -95,7 +98,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
repoOptions: list, repoOptions: list,
loading: false, loading: false,
networkError: false, networkError: false,
sortType: sortType || dataSource.sortType sortType: sort
}); });
setLoadingModel?.(false); setLoadingModel?.(false);
handleOnSelectModel(list[0]); handleOnSelectModel(list[0]);
@@ -103,7 +106,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
setDataSource({ setDataSource({
repoOptions: [], repoOptions: [],
loading: false, loading: false,
sortType: sortType || dataSource.sortType, sortType: sort,
networkError: error?.message === 'Failed to fetch' networkError: error?.message === 'Failed to fetch'
}); });
setLoadingModel?.(false); setLoadingModel?.(false);
@@ -188,8 +191,9 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
}; };
const handleSortChange = (value: string) => { const handleSortChange = (value: string) => {
handleOnSearchRepo(value); handleOnSearchRepo(value || '');
}; };
const renderHFSearch = () => { const renderHFSearch = () => {
return ( return (
<> <>
@@ -204,6 +208,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
</span> </span>
</span> </span>
<Select <Select
allowClear
value={dataSource.sortType} value={dataSource.sortType}
onChange={handleSortChange} onChange={handleSortChange}
labelRender={({ label }) => { labelRender={({ label }) => {
+10 -30
View File
@@ -103,7 +103,10 @@ const Models: React.FC<ModelsProps> = ({
source: modelSourceMap.huggingface_value source: modelSourceMap.huggingface_value
}); });
}, },
{ preventDefault: true } {
preventDefault: true,
enabled: !openAddModal && !openDeployModal.show && !openLogModal
}
); );
useHotkeys( useHotkeys(
@@ -115,25 +118,15 @@ const Models: React.FC<ModelsProps> = ({
source: modelSourceMap.ollama_library_value source: modelSourceMap.ollama_library_value
}); });
}, },
{ preventDefault: true } {
preventDefault: true,
enabled: !openAddModal && !openDeployModal.show && !openLogModal
}
); );
const sourceOptions = [ const sourceOptions = [
{ {
label: ( label: 'Hugging Face',
<span className="flex-center flex-between">
<span>Hugging Face</span>
<Tag style={{ marginRight: 0 }} className="m-l-10">
{platform.isMac ? (
<>
<IconFont type="icon-command"></IconFont> + 1
</>
) : (
<>CTRL + 1</>
)}
</Tag>
</span>
),
value: modelSourceMap.huggingface_value, value: modelSourceMap.huggingface_value,
key: 'huggingface', key: 'huggingface',
icon: <IconFont type="icon-huggingface"></IconFont>, icon: <IconFont type="icon-huggingface"></IconFont>,
@@ -146,20 +139,7 @@ const Models: React.FC<ModelsProps> = ({
} }
}, },
{ {
label: ( label: 'Ollama Library',
<span className="flex-center flex-between">
<span>Ollama Library</span>
<Tag style={{ marginRight: 0 }} className="m-l-10">
{platform.isMac ? (
<>
<IconFont type="icon-command"></IconFont> + 2
</>
) : (
<>CTRL + 2</>
)}
</Tag>
</span>
),
value: modelSourceMap.ollama_library_value, value: modelSourceMap.ollama_library_value,
key: 'ollama_library', key: 'ollama_library',
icon: <IconFont type="icon-ollama"></IconFont>, icon: <IconFont type="icon-ollama"></IconFont>,
+10 -2
View File
@@ -3,7 +3,7 @@
.hf-model-file { .hf-model-file {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 10px; padding: 12px 14px;
border: 1px solid var(--ant-color-border); border: 1px solid var(--ant-color-border);
border-radius: var(--border-radius-base); border-radius: var(--border-radius-base);
cursor: pointer; cursor: pointer;
@@ -17,7 +17,15 @@
} }
.title { .title {
margin-bottom: 10px; margin-bottom: 12px;
}
.tags {
display: flex;
gap: 8px;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
} }
.tag-item { .tag-item {
+13 -1
View File
@@ -5,7 +5,7 @@
justify-content: space-between; justify-content: space-between;
border: 1px solid var(--ant-color-border); border: 1px solid var(--ant-color-border);
border-radius: var(--border-radius-base); border-radius: var(--border-radius-base);
padding: 12px; padding: 12px 14px;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
@@ -24,6 +24,12 @@
.info { .info {
color: var(--ant-color-text-tertiary); color: var(--ant-color-text-tertiary);
.info-item {
display: flex;
align-items: center;
gap: 16px;
}
.tag-item { .tag-item {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -37,4 +43,10 @@
// color: var(--ant-color-text-secondary); // color: var(--ant-color-text-secondary);
} }
} }
.tags {
display: flex;
align-items: center;
flex-wrap: wrap;
}
} }
+8 -9
View File
@@ -1,13 +1,10 @@
.model-card-wrap { .model-card-wrap {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 72px;
padding: 10px;
border: 1px solid var(--ant-color-border);
border-radius: var(--border-radius-base); border-radius: var(--border-radius-base);
.title { .title {
margin-bottom: 10px; margin-bottom: 5px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@@ -30,15 +27,17 @@
.mkd-title { .mkd-title {
cursor: pointer; cursor: pointer;
color: rgba(255, 255, 255, 80%); background-color: var(--color-fill-sider);
border-bottom: 1px solid rgba(255, 255, 255, 10%); border-bottom: 1px solid rgba(255, 255, 255, 10%);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 10px; padding: 10px;
} height: 36px;
.simplebar-scrollbar::before {
background: var(--color-scroll-bg);
} }
} }
.card-wrapper {
padding: 16px 24px;
padding-top: 0;
}
+1 -1
View File
@@ -9,7 +9,7 @@
align-items: center; align-items: center;
font-size: 14px; font-size: 14px;
padding: @padding; padding: @padding;
padding-top: 0; padding-top: 10px;
margin-bottom: 0; margin-bottom: 0;
background-color: var(--color-white-1); background-color: var(--color-white-1);
} }
+10 -2
View File
@@ -31,9 +31,15 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
<div> <div>
<h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3> <h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3>
<h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4> <h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4>
<HighlightCode code={addWorkerGuide.mac.getToken}></HighlightCode> <HighlightCode
code={addWorkerGuide.mac.getToken}
theme="dark"
></HighlightCode>
<h4>Windows </h4> <h4>Windows </h4>
<HighlightCode code={addWorkerGuide.win.getToken}></HighlightCode> <HighlightCode
code={addWorkerGuide.win.getToken}
theme="dark"
></HighlightCode>
<h3> <h3>
2. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '} 2. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '}
<span <span
@@ -49,9 +55,11 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
<h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4> <h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4>
<HighlightCode <HighlightCode
code={addWorkerGuide.mac.registerWorker(origin)} code={addWorkerGuide.mac.registerWorker(origin)}
theme="dark"
></HighlightCode> ></HighlightCode>
<h4>Windows </h4> <h4>Windows </h4>
<HighlightCode <HighlightCode
theme="dark"
code={addWorkerGuide.win.registerWorker(origin)} code={addWorkerGuide.win.registerWorker(origin)}
></HighlightCode> ></HighlightCode>
<h3>3. {intl.formatMessage({ id: 'resources.worker.add.step3' })}</h3> <h3>3. {intl.formatMessage({ id: 'resources.worker.add.step3' })}</h3>