style: search models style
This commit is contained in:
+2
-1
@@ -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'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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={{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-1
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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': '暂不支持该模型,部署后可能无法使用'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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 }) => {
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user