fix(style): catalog size tags overflow
This commit is contained in:
Generated
+41
-4
@@ -128,6 +128,9 @@ dependencies:
|
|||||||
rc-resize-observer:
|
rc-resize-observer:
|
||||||
specifier: ^1.4.0
|
specifier: ^1.4.0
|
||||||
version: 1.4.0(react-dom@18.2.0)(react@18.2.0)
|
version: 1.4.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
rc-tabs:
|
||||||
|
specifier: ^15.5.0
|
||||||
|
version: 15.5.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
rc-virtual-list:
|
rc-virtual-list:
|
||||||
specifier: ^3.14.8
|
specifier: ^3.14.8
|
||||||
version: 3.14.8(react-dom@18.2.0)(react@18.2.0)
|
version: 3.14.8(react-dom@18.2.0)(react@18.2.0)
|
||||||
@@ -5208,7 +5211,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-hy8b7Y1J8OGe6LbAjj3xniQrj3v6lsivCcrmf4TzSgPzLkhIeKgc5IZnT7ReIqmEuodjfO8EYAuoFvIrHi/+jQ==, tarball: https://registry.npmjs.org/@types/history/-/history-5.0.0.tgz}
|
resolution: {integrity: sha512-hy8b7Y1J8OGe6LbAjj3xniQrj3v6lsivCcrmf4TzSgPzLkhIeKgc5IZnT7ReIqmEuodjfO8EYAuoFvIrHi/+jQ==, tarball: https://registry.npmjs.org/@types/history/-/history-5.0.0.tgz}
|
||||||
deprecated: This is a stub types definition. history provides its own type definitions, so you do not need this installed.
|
deprecated: This is a stub types definition. history provides its own type definitions, so you do not need this installed.
|
||||||
dependencies:
|
dependencies:
|
||||||
history: 4.10.1
|
history: 5.3.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/hoist-non-react-statics@3.3.5:
|
/@types/hoist-non-react-statics@3.3.5:
|
||||||
@@ -5354,7 +5357,7 @@ packages:
|
|||||||
'@types/history': 4.7.11
|
'@types/history': 4.7.11
|
||||||
'@types/react': 18.3.16
|
'@types/react': 18.3.16
|
||||||
'@types/react-router': 5.1.20
|
'@types/react-router': 5.1.20
|
||||||
redux: 3.7.2
|
redux: 4.2.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/react-router@5.1.20:
|
/@types/react-router@5.1.20:
|
||||||
@@ -9076,7 +9079,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==, tarball: https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz}
|
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==, tarball: https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz}
|
||||||
engines: {node: '>=0.11'}
|
engines: {node: '>=0.11'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.7
|
'@babel/runtime': 7.26.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/dayjs@1.11.11:
|
/dayjs@1.11.11:
|
||||||
@@ -15567,6 +15570,22 @@ packages:
|
|||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/rc-menu@9.16.0(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-vAL0yqPkmXWk3+YKRkmIR8TYj3RVdEt3ptG2jCJXWNAvQbT0VJJdRyHZ7kG/l1JsZlB+VJq/VcYOo69VR4oD+w==, tarball: https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.0.tgz}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.9.0'
|
||||||
|
react-dom: '>=16.9.0'
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.26.0
|
||||||
|
'@rc-component/trigger': 2.2.3(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
classnames: 2.5.1
|
||||||
|
rc-motion: 2.9.3(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
rc-overflow: 1.3.2(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
rc-util: 5.44.2(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/rc-menu@9.8.4(react-dom@18.2.0)(react@18.2.0):
|
/rc-menu@9.8.4(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-lmw2j8I2fhdIzHmC9ajfImfckt0WDb2KVJJBBRIsxPEw2kGkEfjLMUoB1NgiNT/Q5cC8PdjGOGQjHJIJMwyNMw==, tarball: https://registry.npmjs.org/rc-menu/-/rc-menu-9.8.4.tgz}
|
resolution: {integrity: sha512-lmw2j8I2fhdIzHmC9ajfImfckt0WDb2KVJJBBRIsxPEw2kGkEfjLMUoB1NgiNT/Q5cC8PdjGOGQjHJIJMwyNMw==, tarball: https://registry.npmjs.org/rc-menu/-/rc-menu-9.8.4.tgz}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -16050,6 +16069,24 @@ packages:
|
|||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/rc-tabs@15.5.0(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-NrDcTaUJLh9UuDdMBkjKTn97U9iXG44s9D03V5NHkhEDWO5/nC6PwC3RhkCWFMKB9hh+ryqgZ+TIr1b9Jd/hnQ==, tarball: https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.5.0.tgz}
|
||||||
|
engines: {node: '>=8.x'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.9.0'
|
||||||
|
react-dom: '>=16.9.0'
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.26.0
|
||||||
|
classnames: 2.5.1
|
||||||
|
rc-dropdown: 4.2.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
rc-menu: 9.16.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
rc-motion: 2.9.3(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
rc-resize-observer: 1.4.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
rc-util: 5.44.2(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/rc-textarea@0.4.7(react-dom@18.2.0)(react@18.2.0):
|
/rc-textarea@0.4.7(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-IQPd1CDI3mnMlkFyzt2O4gQ2lxUsnBAeJEoZGJnkkXgORNqyM9qovdrCj9NzcRfpHgLdzaEbU3AmobNFGUznwQ==, tarball: https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.4.7.tgz}
|
resolution: {integrity: sha512-IQPd1CDI3mnMlkFyzt2O4gQ2lxUsnBAeJEoZGJnkkXgORNqyM9qovdrCj9NzcRfpHgLdzaEbU3AmobNFGUznwQ==, tarball: https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.4.7.tgz}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -16804,7 +16841,7 @@ packages:
|
|||||||
/regenerator-transform@0.15.2:
|
/regenerator-transform@0.15.2:
|
||||||
resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==, tarball: https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz}
|
resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==, tarball: https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.7
|
'@babel/runtime': 7.26.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/regexp-tree@0.1.27:
|
/regexp-tree@0.1.27:
|
||||||
|
|||||||
@@ -11,13 +11,13 @@
|
|||||||
:global {
|
:global {
|
||||||
.copy-button-wrapper {
|
.copy-button-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
background-color: #383838;
|
background-color: rgba(255, 255, 255, 20%);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
right: -28px;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
border-radius: 4px;
|
border-radius: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.simplebar-content {
|
.simplebar-content {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type StatusTagProps = {
|
|||||||
download?: {
|
download?: {
|
||||||
percent: number;
|
percent: number;
|
||||||
};
|
};
|
||||||
|
extra?: React.ReactNode;
|
||||||
actions?: {
|
actions?: {
|
||||||
label: string;
|
label: string;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
@@ -40,6 +41,7 @@ type StatusTagProps = {
|
|||||||
const StatusTag: React.FC<StatusTagProps> = ({
|
const StatusTag: React.FC<StatusTagProps> = ({
|
||||||
statusValue,
|
statusValue,
|
||||||
download,
|
download,
|
||||||
|
extra,
|
||||||
actions = [],
|
actions = [],
|
||||||
type = 'tag'
|
type = 'tag'
|
||||||
}) => {
|
}) => {
|
||||||
@@ -78,7 +80,6 @@ const StatusTag: React.FC<StatusTagProps> = ({
|
|||||||
style={{ color: 'rgba(255,255,255,.8)' }}
|
style={{ color: 'rgba(255,255,255,.8)' }}
|
||||||
text={statusValue.message || ''}
|
text={statusValue.message || ''}
|
||||||
size="small"
|
size="small"
|
||||||
placement="right"
|
|
||||||
></CopyButton>
|
></CopyButton>
|
||||||
{actions?.map((item) => {
|
{actions?.map((item) => {
|
||||||
return (
|
return (
|
||||||
@@ -104,7 +105,7 @@ const StatusTag: React.FC<StatusTagProps> = ({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SimpleBar style={{ maxHeight: 200 }}>
|
<SimpleBar style={{ maxHeight: 200 }} autoHide={false}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: 'max-content',
|
width: 'max-content',
|
||||||
@@ -115,6 +116,7 @@ const StatusTag: React.FC<StatusTagProps> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{statusValue.message}
|
{statusValue.message}
|
||||||
|
{extra}
|
||||||
</div>
|
</div>
|
||||||
</SimpleBar>
|
</SimpleBar>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,4 +13,8 @@
|
|||||||
height: 22px;
|
height: 22px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tags-content {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,99 +1,126 @@
|
|||||||
import { MoreOutlined } from '@ant-design/icons';
|
import { MoreOutlined } from '@ant-design/icons';
|
||||||
import { Dropdown, Tag } from 'antd';
|
import { Dropdown, Tag } from 'antd';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import _ from 'lodash';
|
||||||
|
import ResizeObserver from 'rc-resize-observer';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const TagsWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
interface TagsWrapperProps {
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
gap?: number;
|
||||||
const observer = useRef<IntersectionObserver | null>(null);
|
dataList: any[];
|
||||||
const resizeObserver = useRef<ResizeObserver | null>(null);
|
renderTag: (item: any) => React.ReactNode;
|
||||||
const [hiddenIndex, setHiddenIndex] = useState<number>(4);
|
}
|
||||||
const uid = useRef(0);
|
|
||||||
|
|
||||||
const updateUid = () => {
|
const TagsWrapper: React.FC<TagsWrapperProps> = (props) => {
|
||||||
uid.current += 1;
|
const { gap = 0, dataList, renderTag } = props;
|
||||||
return uid.current;
|
const tagsContentRef = useRef<HTMLDivElement>(null);
|
||||||
};
|
const [hiddenIndices, setHiddenIndices] = useState({
|
||||||
|
start: 0,
|
||||||
const dropItems = useMemo(() => {
|
end: dataList.length
|
||||||
return React.Children.toArray(children)
|
|
||||||
.slice(hiddenIndex)
|
|
||||||
.map((child) => ({
|
|
||||||
label: child,
|
|
||||||
key: updateUid()
|
|
||||||
}));
|
|
||||||
}, [children, hiddenIndex]);
|
|
||||||
|
|
||||||
const updateHiddenIndex = () => {
|
|
||||||
if (!wrapperRef.current || !observer.current) return;
|
|
||||||
|
|
||||||
const childrenList = Array.from(wrapperRef.current.children);
|
|
||||||
let newHiddenIndex = 0;
|
|
||||||
|
|
||||||
childrenList.forEach((child, index) => {
|
|
||||||
const rect = child.getBoundingClientRect();
|
|
||||||
// visible in wrapperRef
|
|
||||||
const issivible =
|
|
||||||
rect.top < wrapperRef.current!.clientHeight &&
|
|
||||||
rect.bottom > 0 &&
|
|
||||||
rect.left < wrapperRef.current!.clientWidth &&
|
|
||||||
rect.right > 0;
|
|
||||||
|
|
||||||
if (!issivible) {
|
|
||||||
newHiddenIndex = Math.max(newHiddenIndex, index + 1);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
|
const moreBtnRef = useRef<HTMLDivElement>(null);
|
||||||
|
const moreButtonWidth = useRef(0);
|
||||||
|
const nodeSizeList = useRef<number[]>([]);
|
||||||
|
|
||||||
setHiddenIndex(newHiddenIndex);
|
const calculateHiddenIndices = () => {
|
||||||
|
const wrapperWidth = wrapperRef.current?.offsetWidth || 0;
|
||||||
|
const childNodes = tagsContentRef.current?.childNodes;
|
||||||
|
let sizeList = nodeSizeList.current;
|
||||||
|
|
||||||
|
if (!sizeList.length && childNodes?.length) {
|
||||||
|
sizeList = _.map(childNodes, (node: HTMLDivElement, index: number) => {
|
||||||
|
if (index === childNodes.length - 1) {
|
||||||
|
return node.offsetWidth;
|
||||||
|
}
|
||||||
|
return node.offsetWidth + gap;
|
||||||
|
});
|
||||||
|
nodeSizeList.current = sizeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapperWidth === 0 || !sizeList.length) return;
|
||||||
|
|
||||||
|
// cache more button width
|
||||||
|
if (moreBtnRef.current?.offsetWidth) {
|
||||||
|
moreButtonWidth.current = moreBtnRef.current?.offsetWidth + gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalWidth = 0;
|
||||||
|
let start = 0;
|
||||||
|
let end = dataList.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < sizeList.length; i++) {
|
||||||
|
const nodeWidth = sizeList[i];
|
||||||
|
|
||||||
|
if (totalWidth + moreButtonWidth.current >= wrapperWidth) {
|
||||||
|
end = i - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
totalWidth += nodeWidth;
|
||||||
|
|
||||||
|
if (totalWidth >= wrapperWidth) {
|
||||||
|
end = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hiddenIndices.start !== start || hiddenIndices.end !== end) {
|
||||||
|
setHiddenIndices({
|
||||||
|
start,
|
||||||
|
end
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!wrapperRef.current) return;
|
if (tagsContentRef.current) {
|
||||||
|
calculateHiddenIndices();
|
||||||
|
}
|
||||||
|
}, [dataList, gap]);
|
||||||
|
|
||||||
// observer.current = new IntersectionObserver(
|
const handleContentResize = _.throttle(() => {
|
||||||
// (entries) => {
|
calculateHiddenIndices();
|
||||||
// const lastEntry = entries[entries.length - 1];
|
}, 200);
|
||||||
// if (lastEntry && lastEntry.intersectionRatio < 1) {
|
|
||||||
// setHiddenIndex((prev) =>
|
|
||||||
// Math.max(prev, React.Children.count(children))
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// { root: wrapperRef.current, threshold: 1 }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const childrenList = wrapperRef.current.children;
|
|
||||||
// if (childrenList.length > 0) {
|
|
||||||
// observer.current.observe(childrenList[childrenList.length - 1]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
resizeObserver.current = new ResizeObserver(() => {
|
|
||||||
// updateHiddenIndex();
|
|
||||||
});
|
|
||||||
resizeObserver.current.observe(wrapperRef.current);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
observer.current?.disconnect();
|
|
||||||
resizeObserver.current?.disconnect();
|
|
||||||
};
|
|
||||||
}, [children]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ResizeObserver onResize={handleContentResize}>
|
||||||
<div className="tags-wrapper" ref={wrapperRef}>
|
<div className="tags-wrapper" ref={wrapperRef}>
|
||||||
{React.Children.toArray(children).slice(0, hiddenIndex)}
|
<ResizeObserver onResize={handleContentResize}>
|
||||||
{React.Children.toArray(children).length > 4 && (
|
<div className="tags-content" ref={tagsContentRef} style={{ gap }}>
|
||||||
|
{_.map(
|
||||||
|
_.slice(dataList, hiddenIndices.start, hiddenIndices.end),
|
||||||
|
(item: any, index: number) => {
|
||||||
|
return <span key={index}>{renderTag?.(item)}</span>;
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ResizeObserver>
|
||||||
|
{hiddenIndices.end < dataList.length && (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
trigger={['hover']}
|
trigger={['hover']}
|
||||||
menu={{
|
menu={{
|
||||||
items: dropItems
|
items: _.map(
|
||||||
|
_.slice(dataList, hiddenIndices.end),
|
||||||
|
(item: any, index: number) => {
|
||||||
|
return {
|
||||||
|
label: renderTag?.(item),
|
||||||
|
key: index
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tag className="more">
|
<Tag
|
||||||
|
className="more"
|
||||||
|
style={{ marginInline: `${gap}px 0` }}
|
||||||
|
ref={moreBtnRef}
|
||||||
|
>
|
||||||
<MoreOutlined rotate={90} />
|
<MoreOutlined rotate={90} />
|
||||||
</Tag>
|
</Tag>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</ResizeObserver>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { MoreOutlined } from '@ant-design/icons';
|
||||||
|
import { Dropdown, Tag } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface MoreDropdownProps {
|
||||||
|
items: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const MoreDropdown: React.FC<MoreDropdownProps> = (props) => {
|
||||||
|
const { items } = props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{items && items.length > 0 ? (
|
||||||
|
<Dropdown
|
||||||
|
trigger={['hover']}
|
||||||
|
menu={{
|
||||||
|
items: items
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tag className="more">
|
||||||
|
<MoreOutlined rotate={90} />
|
||||||
|
</Tag>
|
||||||
|
</Dropdown>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(MoreDropdown);
|
||||||
@@ -89,5 +89,6 @@ export default {
|
|||||||
'models.form.search.gguftips':
|
'models.form.search.gguftips':
|
||||||
'If using macOS or Windows as a worker, check GGUF (uncheck for audio models).',
|
'If using macOS or Windows as a worker, check GGUF (uncheck for audio models).',
|
||||||
'models.form.button.addlabel': 'Add Label',
|
'models.form.button.addlabel': 'Add Label',
|
||||||
'models.filter.category': 'Filter by category'
|
'models.filter.category': 'Filter by category',
|
||||||
|
'models.list.more.logs': 'View More'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -85,5 +85,6 @@ export default {
|
|||||||
'models.form.search.gguftips':
|
'models.form.search.gguftips':
|
||||||
'当 macOS 或 Windows 作 worker 时勾选 GGUF(搜索音频模型时取消勾选)',
|
'当 macOS 或 Windows 作 worker 时勾选 GGUF(搜索音频模型时取消勾选)',
|
||||||
'models.form.button.addlabel': '添加标签',
|
'models.form.button.addlabel': '添加标签',
|
||||||
'models.filter.category': '按类别筛选'
|
'models.filter.category': '按类别筛选',
|
||||||
|
'models.list.more.logs': '查看更多'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,6 +27,28 @@ const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
|||||||
e.target.src = fallbackImg;
|
e.target.src = fallbackImg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderTag = (sItem: any) => {
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
key={sItem}
|
||||||
|
className="tag-item"
|
||||||
|
style={{
|
||||||
|
marginRight: 0,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '2px 6px',
|
||||||
|
borderRadius: 4,
|
||||||
|
fontSize: 12,
|
||||||
|
opacity: 0.7,
|
||||||
|
height: 22
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{sItem}B
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={handleOnClick}
|
onClick={handleOnClick}
|
||||||
@@ -81,7 +103,7 @@ const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
|||||||
<span className="flex-center">
|
<span className="flex-center">
|
||||||
{_.map(data.licenses, (license: string, index: number) => {
|
{_.map(data.licenses, (license: string, index: number) => {
|
||||||
return (
|
return (
|
||||||
<span key={license} className="flex-center m-r-16">
|
<span key={license} className="flex-center m-r-8">
|
||||||
<IconFont type="icon-justice1" className="m-r-5"></IconFont>
|
<IconFont type="icon-justice1" className="m-r-5"></IconFont>
|
||||||
<span>{license}</span>
|
<span>{license}</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -113,15 +135,11 @@ const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
|||||||
<>
|
<>
|
||||||
<span className="dot"></span>
|
<span className="dot"></span>
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<TagWrapper>
|
<TagWrapper
|
||||||
{data.sizes.map((sItem, i) => {
|
gap={8}
|
||||||
return (
|
dataList={data.sizes}
|
||||||
<Tag key={sItem} className="tag-item">
|
renderTag={renderTag}
|
||||||
{sItem}B
|
></TagWrapper>
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TagWrapper>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
ThunderboltFilled
|
ThunderboltFilled
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Col, Divider, Row, Space, Tag, Tooltip } from 'antd';
|
import { Button, Col, Divider, Row, Space, Tag, Tooltip } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
@@ -282,6 +282,21 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
|||||||
? { percent: item.download_progress }
|
? { percent: item.download_progress }
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
extra={
|
||||||
|
item.state === InstanceStatusMap.Error ? (
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() =>
|
||||||
|
handleChildSelect('viewlog', item, list)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: 'models.list.more.logs'
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
statusValue={{
|
statusValue={{
|
||||||
status:
|
status:
|
||||||
item.state === InstanceStatusMap.Downloading &&
|
item.state === InstanceStatusMap.Downloading &&
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ const PlaygroundEmbedding: React.FC = () => {
|
|||||||
const getModelListByEmbedding = async () => {
|
const getModelListByEmbedding = async () => {
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
categories: modelCategoriesMap.embedding
|
categories: modelCategoriesMap.embedding,
|
||||||
|
with_meta: true
|
||||||
};
|
};
|
||||||
const res = await queryModelsList(params);
|
const res = await queryModelsList(params);
|
||||||
const list = _.map(res.data || [], (item: any) => {
|
const list = _.map(res.data || [], (item: any) => {
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ const PlaygroundRerank: React.FC = () => {
|
|||||||
const getModelListByReranker = async () => {
|
const getModelListByReranker = async () => {
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
categories: modelCategoriesMap.reranker
|
categories: modelCategoriesMap.reranker,
|
||||||
|
with_meta: true
|
||||||
};
|
};
|
||||||
const res = await queryModelsList(params);
|
const res = await queryModelsList(params);
|
||||||
const list = _.map(res.data || [], (item: any) => {
|
const list = _.map(res.data || [], (item: any) => {
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
|||||||
{ version: versionInfo.version }
|
{ version: versionInfo.version }
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
<li>{intl.formatMessage({ id: 'resources.worker.select.command' })}</li>
|
|
||||||
<li>
|
<li>
|
||||||
<span>
|
<span>
|
||||||
{intl.formatMessage({ id: 'resources.worker.driver.install' })}
|
{intl.formatMessage({ id: 'resources.worker.driver.install' })}
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ export const addWorkerGuide: Record<string, any> = {
|
|||||||
return `docker run -d --ipc=host --network=host gpustack/gpustack:${params.tag} --server-url ${params.server} --token ${params.token}`;
|
return `docker run -d --ipc=host --network=host gpustack/gpustack:${params.tag} --server-url ${params.server} --token ${params.token}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
rocm: {
|
||||||
|
registerWorker(params: { server: string; tag: string; token: string }) {
|
||||||
|
return `docker run -d --network=host --ipc=host --group-add=video --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --device /dev/kfd --device /dev/dri gpustack/gpustack:${params.tag} --server-url ${params.server} --token ${params.token}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
getToken:
|
getToken:
|
||||||
'docker run -it ${gpustack_container_id} cat /var/lib/gpustack/token'
|
'docker run -it ${gpustack_container_id} cat /var/lib/gpustack/token'
|
||||||
@@ -65,6 +70,7 @@ export const addWorkerGuide: Record<string, any> = {
|
|||||||
|
|
||||||
export const containerInstallOptions = [
|
export const containerInstallOptions = [
|
||||||
{ label: 'CUDA', value: 'cuda' },
|
{ label: 'CUDA', value: 'cuda' },
|
||||||
|
{ label: 'AMD', value: 'rocm' },
|
||||||
{ label: 'CANN', value: 'npu' },
|
{ label: 'CANN', value: 'npu' },
|
||||||
{ label: 'MUSA', value: 'musa' },
|
{ label: 'MUSA', value: 'musa' },
|
||||||
{ label: 'CPU', value: 'cpu' }
|
{ label: 'CPU', value: 'cpu' }
|
||||||
|
|||||||
Reference in New Issue
Block a user