fix: logs scrollbar ux
This commit is contained in:
@@ -13,6 +13,7 @@ import React, {
|
|||||||
interface AutoTooltipProps extends Omit<TagProps, 'title'> {
|
interface AutoTooltipProps extends Omit<TagProps, 'title'> {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
maxWidth?: number | string;
|
maxWidth?: number | string;
|
||||||
|
minWidth?: number | string;
|
||||||
color?: string;
|
color?: string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
ghost?: boolean;
|
ghost?: boolean;
|
||||||
@@ -24,6 +25,7 @@ interface AutoTooltipProps extends Omit<TagProps, 'title'> {
|
|||||||
const AutoTooltip: React.FC<AutoTooltipProps> = ({
|
const AutoTooltip: React.FC<AutoTooltipProps> = ({
|
||||||
children,
|
children,
|
||||||
maxWidth = '100%',
|
maxWidth = '100%',
|
||||||
|
minWidth,
|
||||||
ghost = false,
|
ghost = false,
|
||||||
title,
|
title,
|
||||||
showTitle = false,
|
showTitle = false,
|
||||||
@@ -61,6 +63,7 @@ const AutoTooltip: React.FC<AutoTooltipProps> = ({
|
|||||||
const tagStyle = useMemo(
|
const tagStyle = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
maxWidth,
|
maxWidth,
|
||||||
|
minWidth,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
whiteSpace: 'nowrap' as const,
|
whiteSpace: 'nowrap' as const,
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ interface LogsViewerProps {
|
|||||||
}
|
}
|
||||||
const LogsViewer: React.FC<LogsViewerProps> = (props) => {
|
const LogsViewer: React.FC<LogsViewerProps> = (props) => {
|
||||||
const { height, url } = props;
|
const { height, url } = props;
|
||||||
const { initialize, updateScrollerPosition } = useOverlayScroller({
|
const { initialize, updateScrollerPosition, scrollEventElement } =
|
||||||
theme: 'os-theme-light'
|
useOverlayScroller({
|
||||||
});
|
theme: 'os-theme-light'
|
||||||
|
});
|
||||||
const { isClean, parseAnsi } = useParseAnsi();
|
const { isClean, parseAnsi } = useParseAnsi();
|
||||||
const { setChunkFetch } = useSetChunkFetch();
|
const { setChunkFetch } = useSetChunkFetch();
|
||||||
const chunkRequedtRef = useRef<any>(null);
|
const chunkRequedtRef = useRef<any>(null);
|
||||||
@@ -25,7 +26,6 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
|
|||||||
const cacheDataRef = useRef<any>('');
|
const cacheDataRef = useRef<any>('');
|
||||||
const uidRef = useRef<any>(0);
|
const uidRef = useRef<any>(0);
|
||||||
const [logs, setLogs] = useState<any[]>([]);
|
const [logs, setLogs] = useState<any[]>([]);
|
||||||
const autoScroll = useRef(true);
|
|
||||||
const stopScroll = useRef(false);
|
const stopScroll = useRef(false);
|
||||||
|
|
||||||
const setId = () => {
|
const setId = () => {
|
||||||
@@ -33,10 +33,6 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
|
|||||||
return uidRef.current;
|
return uidRef.current;
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearScreen = () => {
|
|
||||||
cacheDataRef.current = '';
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateContent = useCallback(
|
const updateContent = useCallback(
|
||||||
(data: string) => {
|
(data: string) => {
|
||||||
if (isClean(data)) {
|
if (isClean(data)) {
|
||||||
@@ -44,7 +40,7 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
cacheDataRef.current += data;
|
cacheDataRef.current += data;
|
||||||
}
|
}
|
||||||
const res = parseAnsi(cacheDataRef.current, setId, clearScreen);
|
const res = parseAnsi(cacheDataRef.current, setId);
|
||||||
setLogs(res);
|
setLogs(res);
|
||||||
},
|
},
|
||||||
[setLogs, setId]
|
[setLogs, setId]
|
||||||
@@ -65,10 +61,17 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
|
|||||||
|
|
||||||
const debounceResetStopScroll = _.debounce(() => {
|
const debounceResetStopScroll = _.debounce(() => {
|
||||||
stopScroll.current = false;
|
stopScroll.current = false;
|
||||||
}, 3000);
|
}, 30000);
|
||||||
const handleOnWheel = useCallback(
|
const handleOnWheel = useCallback(
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
stopScroll.current = true;
|
const scrollTop = scrollEventElement.scrollTop;
|
||||||
|
const scrollHeight = scrollEventElement.scrollHeight;
|
||||||
|
const clientHeight = scrollEventElement.clientHeight;
|
||||||
|
if (scrollTop + clientHeight >= scrollHeight) {
|
||||||
|
stopScroll.current = false;
|
||||||
|
} else {
|
||||||
|
stopScroll.current = true;
|
||||||
|
}
|
||||||
debounceResetStopScroll();
|
debounceResetStopScroll();
|
||||||
},
|
},
|
||||||
[debounceResetStopScroll]
|
[debounceResetStopScroll]
|
||||||
@@ -91,7 +94,7 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
|
|||||||
if (logs.length && !stopScroll.current) {
|
if (logs.length && !stopScroll.current) {
|
||||||
updateScrollerPosition(0);
|
updateScrollerPosition(0);
|
||||||
}
|
}
|
||||||
}, [logs, autoScroll.current, stopScroll.current]);
|
}, [logs, stopScroll.current]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs-viewer-wrap-w2">
|
<div className="logs-viewer-wrap-w2">
|
||||||
|
|||||||
@@ -17,138 +17,135 @@ const useParseAnsi = () => {
|
|||||||
return command === 'J' && n === 2;
|
return command === 'J' && n === 2;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const parseAnsi = useCallback(
|
const parseAnsi = useCallback((inputStr: string, setId: () => number) => {
|
||||||
(inputStr: string, setId: () => number, clearScreen: () => void) => {
|
let cursorRow = 0; // current row
|
||||||
let cursorRow = 0; // current row
|
let cursorCol = 0; // current column
|
||||||
let cursorCol = 0; // current column
|
// screen content array
|
||||||
// screen content array
|
let screen = [['']];
|
||||||
let screen = [['']];
|
// replace carriage return and newline characters in the text
|
||||||
// replace carriage return and newline characters in the text
|
let input = inputStr.replace(/\r\n/g, '\n');
|
||||||
let input = inputStr.replace(/\r\n/g, '\n');
|
|
||||||
|
|
||||||
lastIndex.current = 0;
|
lastIndex.current = 0;
|
||||||
|
|
||||||
// handle the \r and \n characters in the text
|
// handle the \r and \n characters in the text
|
||||||
const handleText = (text: string) => {
|
const handleText = (text: string) => {
|
||||||
let processed = '';
|
let processed = '';
|
||||||
for (let char of text) {
|
for (let char of text) {
|
||||||
if (char === '\r') {
|
if (char === '\r') {
|
||||||
cursorCol = 0; // move to the beginning of the line
|
cursorCol = 0; // move to the beginning of the line
|
||||||
} else if (char === '\n') {
|
} else if (char === '\n') {
|
||||||
cursorRow++; // move to the next line
|
cursorRow++; // move to the next line
|
||||||
cursorCol = 0; // move to the beginning of the line
|
cursorCol = 0; // move to the beginning of the line
|
||||||
screen[cursorRow] = screen[cursorRow] || ['']; // create a new line if it does not exist
|
screen[cursorRow] = screen[cursorRow] || ['']; // create a new line if it does not exist
|
||||||
} else {
|
} else {
|
||||||
// add the character to the screen content array
|
// add the character to the screen content array
|
||||||
screen[cursorRow][cursorCol] = char;
|
screen[cursorRow][cursorCol] = char;
|
||||||
cursorCol++;
|
cursorCol++;
|
||||||
}
|
|
||||||
}
|
|
||||||
return processed;
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = ''; // output text
|
|
||||||
|
|
||||||
let match;
|
|
||||||
|
|
||||||
// ANSI color map
|
|
||||||
const colorMap: Record<string, string> = {
|
|
||||||
'30': 'black',
|
|
||||||
'31': 'red',
|
|
||||||
'32': 'green',
|
|
||||||
'33': 'yellow',
|
|
||||||
'34': 'blue',
|
|
||||||
'35': 'magenta',
|
|
||||||
'36': 'cyan',
|
|
||||||
'37': 'white'
|
|
||||||
};
|
|
||||||
|
|
||||||
let currentStyle = ''; // current text style
|
|
||||||
|
|
||||||
// match ANSI control characters
|
|
||||||
while ((match = controlSeqRegex.exec(input)) !== null) {
|
|
||||||
// handle text before the control character
|
|
||||||
let textBeforeControl = input.slice(lastIndex.current, match.index);
|
|
||||||
output += handleText(textBeforeControl); // add the processed text to the output
|
|
||||||
lastIndex.current = controlSeqRegex.lastIndex; // update the last index
|
|
||||||
|
|
||||||
const n = parseInt(match[1], 10) || 1;
|
|
||||||
const m = parseInt(match[2], 10) || 1;
|
|
||||||
const command = match[3];
|
|
||||||
console.log('command', {
|
|
||||||
command,
|
|
||||||
cursorRow,
|
|
||||||
n
|
|
||||||
});
|
|
||||||
|
|
||||||
// handle ANSI control characters
|
|
||||||
switch (command) {
|
|
||||||
case 'A': // up
|
|
||||||
cursorRow = Math.max(0, cursorRow - n);
|
|
||||||
if (cursorRow === 0) {
|
|
||||||
// screen = [['']];
|
|
||||||
// cursorCol = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'B': // down
|
|
||||||
cursorRow += n;
|
|
||||||
break;
|
|
||||||
case 'C': // right
|
|
||||||
cursorCol += n;
|
|
||||||
break;
|
|
||||||
case 'D': // left
|
|
||||||
cursorCol = Math.max(0, cursorCol - n);
|
|
||||||
break;
|
|
||||||
case 'H': // move the cursor to the specified position (n, m)
|
|
||||||
cursorRow = Math.max(0, n - 1);
|
|
||||||
cursorCol = Math.max(0, m - 1);
|
|
||||||
break;
|
|
||||||
case 'J': // clear the screen
|
|
||||||
if (n === 2) {
|
|
||||||
console.log('clear====');
|
|
||||||
screen = [['']];
|
|
||||||
cursorRow = 0;
|
|
||||||
cursorCol = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'm': // color
|
|
||||||
if (match[1] === '0') {
|
|
||||||
currentStyle = '';
|
|
||||||
} else if (colorMap[match[1]]) {
|
|
||||||
currentStyle = `color: ${colorMap[match[1]]};`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the row and column are within the screen content array
|
|
||||||
while (screen.length <= cursorRow) {
|
|
||||||
screen.push(['']);
|
|
||||||
}
|
|
||||||
while (screen[cursorRow].length <= cursorCol) {
|
|
||||||
screen[cursorRow].push('');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return processed;
|
||||||
|
};
|
||||||
|
|
||||||
// handle the remaining text
|
let output = ''; // output text
|
||||||
output += handleText(input.slice(lastIndex.current));
|
|
||||||
|
|
||||||
let result = [];
|
let match;
|
||||||
for (let row = 0; row < screen.length; row++) {
|
|
||||||
let rowContent = screen[row].join('');
|
// ANSI color map
|
||||||
result.push({
|
const colorMap: Record<string, string> = {
|
||||||
content: removeBrackets(rowContent),
|
'30': 'black',
|
||||||
uid: setId()
|
'31': 'red',
|
||||||
});
|
'32': 'green',
|
||||||
}
|
'33': 'yellow',
|
||||||
result.push({
|
'34': 'blue',
|
||||||
content: output,
|
'35': 'magenta',
|
||||||
uid: setId()
|
'36': 'cyan',
|
||||||
|
'37': 'white'
|
||||||
|
};
|
||||||
|
|
||||||
|
let currentStyle = ''; // current text style
|
||||||
|
|
||||||
|
// match ANSI control characters
|
||||||
|
while ((match = controlSeqRegex.exec(input)) !== null) {
|
||||||
|
// handle text before the control character
|
||||||
|
let textBeforeControl = input.slice(lastIndex.current, match.index);
|
||||||
|
output += handleText(textBeforeControl); // add the processed text to the output
|
||||||
|
lastIndex.current = controlSeqRegex.lastIndex; // update the last index
|
||||||
|
|
||||||
|
const n = parseInt(match[1], 10) || 1;
|
||||||
|
const m = parseInt(match[2], 10) || 1;
|
||||||
|
const command = match[3];
|
||||||
|
console.log('command', {
|
||||||
|
command,
|
||||||
|
cursorRow,
|
||||||
|
n
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
// handle ANSI control characters
|
||||||
},
|
switch (command) {
|
||||||
[]
|
case 'A': // up
|
||||||
);
|
cursorRow = Math.max(0, cursorRow - n);
|
||||||
|
if (cursorRow === 0) {
|
||||||
|
// screen = [['']];
|
||||||
|
// cursorCol = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'B': // down
|
||||||
|
cursorRow += n;
|
||||||
|
break;
|
||||||
|
case 'C': // right
|
||||||
|
cursorCol += n;
|
||||||
|
break;
|
||||||
|
case 'D': // left
|
||||||
|
cursorCol = Math.max(0, cursorCol - n);
|
||||||
|
break;
|
||||||
|
case 'H': // move the cursor to the specified position (n, m)
|
||||||
|
cursorRow = Math.max(0, n - 1);
|
||||||
|
cursorCol = Math.max(0, m - 1);
|
||||||
|
break;
|
||||||
|
case 'J': // clear the screen
|
||||||
|
if (n === 2) {
|
||||||
|
console.log('clear====');
|
||||||
|
screen = [['']];
|
||||||
|
cursorRow = 0;
|
||||||
|
cursorCol = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'm': // color
|
||||||
|
if (match[1] === '0') {
|
||||||
|
currentStyle = '';
|
||||||
|
} else if (colorMap[match[1]]) {
|
||||||
|
currentStyle = `color: ${colorMap[match[1]]};`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the row and column are within the screen content array
|
||||||
|
while (screen.length <= cursorRow) {
|
||||||
|
screen.push(['']);
|
||||||
|
}
|
||||||
|
while (screen[cursorRow].length <= cursorCol) {
|
||||||
|
screen[cursorRow].push('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the remaining text
|
||||||
|
output += handleText(input.slice(lastIndex.current));
|
||||||
|
|
||||||
|
let result = [];
|
||||||
|
for (let row = 0; row < screen.length; row++) {
|
||||||
|
let rowContent = screen[row].join('');
|
||||||
|
result.push({
|
||||||
|
content: removeBrackets(rowContent),
|
||||||
|
uid: setId()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
result.push({
|
||||||
|
content: output,
|
||||||
|
uid: setId()
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, []);
|
||||||
|
|
||||||
return { parseAnsi, isClean };
|
return { parseAnsi, isClean };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,7 +61,12 @@ const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
|||||||
{ version: currentVersion }
|
{ version: currentVersion }
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<Button type="link" href={externalLinks.release} target="_blank">
|
<Button
|
||||||
|
type="link"
|
||||||
|
href={externalLinks.release}
|
||||||
|
target="_blank"
|
||||||
|
style={{ paddingInline: 0 }}
|
||||||
|
>
|
||||||
{intl.formatMessage({ id: 'common.text.changelog' })}
|
{intl.formatMessage({ id: 'common.text.changelog' })}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default function useOverlayScroller(options?: any) {
|
|||||||
});
|
});
|
||||||
instanceRef.current?.update?.();
|
instanceRef.current?.update?.();
|
||||||
}, 300),
|
}, 300),
|
||||||
[scrollEventElement, instanceRef]
|
[scrollEventElement.current, instanceRef.current]
|
||||||
);
|
);
|
||||||
|
|
||||||
const scrollauto = React.useCallback(() => {
|
const scrollauto = React.useCallback(() => {
|
||||||
@@ -65,7 +65,7 @@ export default function useOverlayScroller(options?: any) {
|
|||||||
behavior: 'auto'
|
behavior: 'auto'
|
||||||
});
|
});
|
||||||
instanceRef.current?.update?.();
|
instanceRef.current?.update?.();
|
||||||
}, [scrollEventElement, instanceRef]);
|
}, [scrollEventElement.current, instanceRef.current]);
|
||||||
|
|
||||||
const throttledUpdateScrollerPosition = React.useCallback(
|
const throttledUpdateScrollerPosition = React.useCallback(
|
||||||
(delay?: number) => {
|
(delay?: number) => {
|
||||||
@@ -78,25 +78,25 @@ export default function useOverlayScroller(options?: any) {
|
|||||||
[throttledScroll, scrollauto]
|
[throttledScroll, scrollauto]
|
||||||
);
|
);
|
||||||
|
|
||||||
// const createInstance = React.useCallback((el: any) => {
|
const createInstance = React.useCallback(
|
||||||
// if (el) {
|
(el: any) => {
|
||||||
// instanceRef.current?.destroy?.();
|
if (instanceRef.current) {
|
||||||
// initialize(el);
|
return;
|
||||||
// instanceRef.current = instance?.();
|
}
|
||||||
// scrollEventElement.current =
|
if (el) {
|
||||||
// instanceRef.current?.elements()?.scrollEventElement;
|
initialize(el);
|
||||||
// }
|
instanceRef.current = instance?.();
|
||||||
// }, []);
|
scrollEventElement.current =
|
||||||
|
instanceRef.current?.elements()?.scrollEventElement;
|
||||||
React.useEffect(() => {
|
}
|
||||||
return () => {
|
},
|
||||||
instanceRef.current?.destroy?.();
|
[initialize, instance]
|
||||||
};
|
);
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initialize,
|
initialize: createInstance,
|
||||||
instance: instanceRef.current,
|
instance: instanceRef.current,
|
||||||
|
scrollEventElement: scrollEventElement.current,
|
||||||
updateScrollerPosition: throttledUpdateScrollerPosition
|
updateScrollerPosition: throttledUpdateScrollerPosition
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export const getRightRenderContent = (opts: {
|
|||||||
mode: 'vertical',
|
mode: 'vertical',
|
||||||
expandIcon: false,
|
expandIcon: false,
|
||||||
// inlineCollapsed: collapsed,
|
// inlineCollapsed: collapsed,
|
||||||
triggerSubMenuAction: 'click',
|
triggerSubMenuAction: 'hover',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: 'help',
|
key: 'help',
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export default {
|
|||||||
'resources.table.vramutilization': 'VRAM Utilization',
|
'resources.table.vramutilization': 'VRAM Utilization',
|
||||||
'resources.table.total': 'Total',
|
'resources.table.total': 'Total',
|
||||||
'resources.table.used': 'Used',
|
'resources.table.used': 'Used',
|
||||||
|
'resources.table.allocated': 'Allocated',
|
||||||
'resources.table.wokers': 'workers',
|
'resources.table.wokers': 'workers',
|
||||||
'resources.worker.linuxormaxos': 'Linux or MacOS',
|
'resources.worker.linuxormaxos': 'Linux or MacOS',
|
||||||
'resources.table.unified': 'Unified Memory',
|
'resources.table.unified': 'Unified Memory',
|
||||||
|
|||||||
@@ -27,5 +27,5 @@ export default {
|
|||||||
'users.password.confirm.error': 'The two passwords entered do not match.',
|
'users.password.confirm.error': 'The two passwords entered do not match.',
|
||||||
'users.login.title': 'Log in to {name}',
|
'users.login.title': 'Log in to {name}',
|
||||||
'users.version.islatest': '{version} is the latest version',
|
'users.version.islatest': '{version} is the latest version',
|
||||||
'users.version.update': 'Version {version} is available'
|
'users.version.update': 'GPUStack {version} is available'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export default {
|
|||||||
'resources.table.utilization': '利用率',
|
'resources.table.utilization': '利用率',
|
||||||
'resources.table.total': '总量',
|
'resources.table.total': '总量',
|
||||||
'resources.table.used': '已用',
|
'resources.table.used': '已用',
|
||||||
|
'resources.table.allocated': '已分配',
|
||||||
'resources.table.wokers': 'workers',
|
'resources.table.wokers': 'workers',
|
||||||
'resources.table.unified': '统一内存',
|
'resources.table.unified': '统一内存',
|
||||||
'resources.worker.linuxormaxos': 'Linux 或 MacOS',
|
'resources.worker.linuxormaxos': 'Linux 或 MacOS',
|
||||||
|
|||||||
@@ -26,5 +26,5 @@ export default {
|
|||||||
'users.password.confirm.error': '两次输入的密码不一致',
|
'users.password.confirm.error': '两次输入的密码不一致',
|
||||||
'users.login.title': '登录 {name}',
|
'users.login.title': '登录 {name}',
|
||||||
'users.version.islatest': '{version} 已是最新版本',
|
'users.version.islatest': '{version} 已是最新版本',
|
||||||
'users.version.update': '{version} 版本可供更新'
|
'users.version.update': 'GPUStack {version} 版本可供更新'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,11 +39,7 @@ import {
|
|||||||
queryModelInstancesList,
|
queryModelInstancesList,
|
||||||
updateModel
|
updateModel
|
||||||
} from '../apis';
|
} from '../apis';
|
||||||
import {
|
import { getSourceRepoConfigValue, modelSourceMap } from '../config';
|
||||||
InstanceStatusMap,
|
|
||||||
getSourceRepoConfigValue,
|
|
||||||
modelSourceMap
|
|
||||||
} from '../config';
|
|
||||||
import { FormData, ListItem, ModelInstanceListItem } from '../config/types';
|
import { FormData, ListItem, ModelInstanceListItem } from '../config/types';
|
||||||
import DeployModal from './deploy-modal';
|
import DeployModal from './deploy-modal';
|
||||||
import InstanceItem from './instance-item';
|
import InstanceItem from './instance-item';
|
||||||
@@ -657,7 +653,6 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
></DeployModal>
|
></DeployModal>
|
||||||
<ViewLogsModal
|
<ViewLogsModal
|
||||||
url={currentInstance.url}
|
url={currentInstance.url}
|
||||||
autoScroll={currentInstance.status !== InstanceStatusMap.Downloading}
|
|
||||||
open={openLogModal}
|
open={openLogModal}
|
||||||
onCancel={handleLogModalCancel}
|
onCancel={handleLogModalCancel}
|
||||||
></ViewLogsModal>
|
></ViewLogsModal>
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
|||||||
<LogsViewer
|
<LogsViewer
|
||||||
height={modalSize.height}
|
height={modalSize.height}
|
||||||
url={url}
|
url={url}
|
||||||
autoScroll={props.autoScroll}
|
|
||||||
params={{
|
params={{
|
||||||
follow: true
|
follow: true
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -173,7 +173,10 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
]
|
]
|
||||||
: [...formatMessages],
|
: [...formatMessages],
|
||||||
...parameters,
|
...parameters,
|
||||||
stream: true
|
stream: true,
|
||||||
|
stream_otpions: {
|
||||||
|
include_usage: true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const result: any = await fetchChunkedData({
|
const result: any = await fetchChunkedData({
|
||||||
data: chatParams,
|
data: chatParams,
|
||||||
|
|||||||
@@ -173,7 +173,10 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
]
|
]
|
||||||
: [...formatMessages],
|
: [...formatMessages],
|
||||||
...params,
|
...params,
|
||||||
stream: true
|
stream: true,
|
||||||
|
stream_otpions: {
|
||||||
|
include_usage: true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// ============== payload end ================
|
// ============== payload end ================
|
||||||
const result: any = await fetchChunkedData({
|
const result: any = await fetchChunkedData({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { BulbOutlined } from '@ant-design/icons';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Modal } from 'antd';
|
import { Button, Modal } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
type ViewModalProps = {
|
type ViewModalProps = {
|
||||||
systemMessage?: string;
|
systemMessage?: string;
|
||||||
@@ -40,8 +40,6 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
|||||||
} = props || {};
|
} = props || {};
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const editorRef = useRef(null);
|
|
||||||
const [loaded, setLoaded] = useState(false);
|
|
||||||
const [codeValue, setCodeValue] = useState('');
|
const [codeValue, setCodeValue] = useState('');
|
||||||
const [lang, setLang] = useState(langMap.shell);
|
const [lang, setLang] = useState(langMap.shell);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import AutoTooltip from '@/components/auto-tooltip';
|
||||||
import PageTools from '@/components/page-tools';
|
import PageTools from '@/components/page-tools';
|
||||||
import ProgressBar from '@/components/progress-bar';
|
import ProgressBar from '@/components/progress-bar';
|
||||||
import useTableSort from '@/hooks/use-table-sort';
|
import useTableSort from '@/hooks/use-table-sort';
|
||||||
@@ -140,6 +141,16 @@ const GPUList: React.FC = () => {
|
|||||||
title={intl.formatMessage({ id: 'resources.table.workername' })}
|
title={intl.formatMessage({ id: 'resources.table.workername' })}
|
||||||
dataIndex="worker_name"
|
dataIndex="worker_name"
|
||||||
key="worker_name"
|
key="worker_name"
|
||||||
|
width={200}
|
||||||
|
render={(text, record: GPUDeviceItem) => {
|
||||||
|
return (
|
||||||
|
<span style={{ display: 'flex', width: '100%' }}>
|
||||||
|
<AutoTooltip ghost maxWidth={340}>
|
||||||
|
{text}
|
||||||
|
</AutoTooltip>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
title={intl.formatMessage({ id: 'resources.table.vender' })}
|
title={intl.formatMessage({ id: 'resources.table.vender' })}
|
||||||
@@ -160,7 +171,7 @@ const GPUList: React.FC = () => {
|
|||||||
dataIndex="core"
|
dataIndex="core"
|
||||||
key="Core"
|
key="Core"
|
||||||
render={(text, record: GPUDeviceItem) => {
|
render={(text, record: GPUDeviceItem) => {
|
||||||
return <span>{record.core?.total}</span>;
|
return <>{record.core ? <span>{record.core?.total}</span> : '-'}</>;
|
||||||
}}
|
}}
|
||||||
/> */}
|
/> */}
|
||||||
<Column
|
<Column
|
||||||
@@ -169,9 +180,15 @@ const GPUList: React.FC = () => {
|
|||||||
key="gpuUtil"
|
key="gpuUtil"
|
||||||
render={(text, record: GPUDeviceItem) => {
|
render={(text, record: GPUDeviceItem) => {
|
||||||
return (
|
return (
|
||||||
<ProgressBar
|
<>
|
||||||
percent={_.round(record.core?.utilization_rate, 2)}
|
{record.core ? (
|
||||||
></ProgressBar>
|
<ProgressBar
|
||||||
|
percent={_.round(record.core?.utilization_rate, 2)}
|
||||||
|
></ProgressBar>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -192,7 +209,10 @@ const GPUList: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{intl.formatMessage({ id: 'resources.table.used' })}:{' '}
|
{intl.formatMessage({ id: 'resources.table.used' })}:{' '}
|
||||||
{convertFileSize(record.memory?.used, 0)}
|
{convertFileSize(
|
||||||
|
record.memory?.used || record.memory?.allocated,
|
||||||
|
0
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -307,6 +307,14 @@ const Resources: React.FC = () => {
|
|||||||
title={intl.formatMessage({ id: 'common.table.name' })}
|
title={intl.formatMessage({ id: 'common.table.name' })}
|
||||||
dataIndex="name"
|
dataIndex="name"
|
||||||
key="name"
|
key="name"
|
||||||
|
width={100}
|
||||||
|
render={(text, record: ListItem) => {
|
||||||
|
return (
|
||||||
|
<AutoTooltip ghost maxWidth={240}>
|
||||||
|
<span>{record.name}</span>
|
||||||
|
</AutoTooltip>
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
title={intl.formatMessage({ id: 'resources.table.labels' })}
|
title={intl.formatMessage({ id: 'resources.table.labels' })}
|
||||||
@@ -408,10 +416,14 @@ const Resources: React.FC = () => {
|
|||||||
>
|
>
|
||||||
[{item.index}]
|
[{item.index}]
|
||||||
</span>
|
</span>
|
||||||
<ProgressBar
|
{item.core ? (
|
||||||
key={index}
|
<ProgressBar
|
||||||
percent={_.round(item.core?.utilization_rate, 0)}
|
key={index}
|
||||||
></ProgressBar>
|
percent={_.round(item.core?.utilization_rate, 0)}
|
||||||
|
></ProgressBar>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -455,7 +467,11 @@ const Resources: React.FC = () => {
|
|||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
id: 'resources.table.used'
|
id: 'resources.table.used'
|
||||||
})}
|
})}
|
||||||
: {convertFileSize(item.memory?.used, 0)}
|
:{' '}
|
||||||
|
{convertFileSize(
|
||||||
|
item.memory?.used || item.memory?.allocated,
|
||||||
|
0
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user