chore: add english comment

main
jialin 1 year ago
parent 9a0aeb491a
commit 9bd6dedd90

@ -33,6 +33,7 @@
"jotai": "^2.8.4",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"marked": "^14.1.0",
"numeral": "^2.0.6",
"query-string": "^9.0.0",
"react": "^18.2.0",
@ -42,6 +43,7 @@
"umi-presets-pro": "^2.0.3"
},
"devDependencies": {
"@types/marked": "^6.0.0",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@umijs/case-sensitive-paths-webpack-plugin": "^1.0.1",

@ -71,6 +71,9 @@ dependencies:
lodash:
specifier: ^4.17.21
version: 4.17.21
marked:
specifier: ^14.1.0
version: 14.1.0
numeral:
specifier: ^2.0.6
version: 2.0.6
@ -94,6 +97,9 @@ dependencies:
version: 2.0.3(@babel/core@7.24.9)(@types/react-dom@18.3.0)(@types/react@18.3.1)(antd@5.18.3)(dva@2.5.0-beta.2)(rc-field-form@1.44.0)(react-dom@18.2.0)(react@18.2.0)(umi@4.3.6)
devDependencies:
'@types/marked':
specifier: ^6.0.0
version: 6.0.0
'@types/react':
specifier: ^18.3.1
version: 18.3.1
@ -4459,7 +4465,7 @@ packages:
dependencies:
'@babel/core': 7.24.5
postcss: 7.0.39
postcss-syntax: 0.36.2(postcss@8.4.38)
postcss-syntax: 0.36.2(postcss@7.0.39)
transitivePeerDependencies:
- supports-color
dev: false
@ -4500,7 +4506,7 @@ packages:
postcss-syntax: '>=0.36.2'
dependencies:
postcss: 7.0.39
postcss-syntax: 0.36.2(postcss@8.4.38)
postcss-syntax: 0.36.2(postcss@7.0.39)
remark: 13.0.0
unist-util-find-all-after: 3.0.2
transitivePeerDependencies:
@ -4833,6 +4839,13 @@ packages:
resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==}
dev: false
/@types/marked@6.0.0:
resolution: {integrity: sha512-jmjpa4BwUsmhxcfsgUit/7A9KbrC48Q0q8KvnY107ogcjGgTFDlIL3RpihNpx2Mu1hM4mdFQjoVc4O6JoGKHsA==, tarball: https://registry.npmjs.org/@types/marked/-/marked-6.0.0.tgz}
deprecated: This is a stub types definition. marked provides its own type definitions, so you do not need this installed.
dependencies:
marked: 14.1.0
dev: true
/@types/mdast@3.0.15:
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==, tarball: https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz}
dependencies:
@ -11806,6 +11819,11 @@ packages:
engines: {node: '>=8'}
dev: false
/marked@14.1.0:
resolution: {integrity: sha512-P93GikH/Pde0hM5TAXEd8I4JAYi8IB03n8qzW8Bh1BIEFpEyBoYxi/XWZA53LSpTeLBiMQOoSMj0u5E/tiVYTA==, tarball: https://registry.npmjs.org/marked/-/marked-14.1.0.tgz}
engines: {node: '>= 18'}
hasBin: true
/mathml-tag-names@2.1.3:
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
dev: false
@ -13125,7 +13143,7 @@ packages:
dependencies:
htmlparser2: 3.10.1
postcss: 7.0.39
postcss-syntax: 0.36.2(postcss@8.4.38)
postcss-syntax: 0.36.2(postcss@7.0.39)
dev: false
/postcss-image-set-function@4.0.7(postcss@8.4.38):
@ -13599,6 +13617,30 @@ packages:
lodash: 4.17.21
postcss: 8.4.38
/postcss-syntax@0.36.2(postcss@7.0.39):
resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==}
peerDependencies:
postcss: '>=5.0.0'
postcss-html: '*'
postcss-jsx: '*'
postcss-less: '*'
postcss-markdown: '*'
postcss-scss: '*'
peerDependenciesMeta:
postcss-html:
optional: true
postcss-jsx:
optional: true
postcss-less:
optional: true
postcss-markdown:
optional: true
postcss-scss:
optional: true
dependencies:
postcss: 7.0.39
dev: false
/postcss-syntax@0.36.2(postcss@8.4.38):
resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==}
peerDependencies:
@ -16773,7 +16815,7 @@ packages:
postcss-sass: 0.4.4
postcss-scss: 2.1.1
postcss-selector-parser: 6.0.16
postcss-syntax: 0.36.2(postcss@8.4.38)
postcss-syntax: 0.36.2(postcss@7.0.39)
postcss-value-parser: 4.2.0
resolve-from: 5.0.0
slash: 3.0.0

@ -9,7 +9,7 @@ import { RequestConfig, history } from '@umijs/max';
const loginPath = '/login';
// 运行时配置
// runtime configuration
export async function getInitialState(): Promise<{
fetchUserInfo: () => Promise<Global.UserInfo>;
currentUser?: Global.UserInfo;

@ -0,0 +1,28 @@
import { marked, Tokens } from 'marked';
import React from 'react';
interface MarkdownViewerProps {
content: string;
height?: string;
}
const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
content,
height = 'auto'
}) => {
const renderer = new marked.Renderer();
renderer.link = ({ href, title, text }: Tokens.Link) => {
return `<a href="${href}" title="${title || ''}" target="_blank" rel="noopener noreferrer">${text}</a>`;
};
return (
<div style={{ height, overflow: 'auto' }}>
<div
dangerouslySetInnerHTML={{ __html: marked(content, { renderer }) }}
/>
</div>
);
};
export default React.memo(MarkdownViewer);

@ -1,75 +1,27 @@
import { platformCall } from '@/utils';
import { SearchOutlined } from '@ant-design/icons';
import { Input, Table, Tag } from 'antd';
import _ from 'lodash';
import React from 'react';
import IconFont from '../icon-font';
import './index.less';
const dataSource = [
{
scope: 'playground',
command: 'New Message',
keybindingWin: 'Ctrl + N',
keybindingMac: 'N'
},
{
scope: 'models',
span: {
rowSpan: 3,
colSpan: 1
},
command: 'deploy model from Hugging Face',
keybindingWin: 'Ctrl + 1',
keybindingMac: '1'
},
{
scope: 'models',
command: 'deploy model from Ollama Library',
keybindingWin: 'Ctrl + 2',
keybindingMac: '2',
span: {
rowSpan: 0,
colSpan: 0
}
},
{
scope: 'models',
command: '从 Hugging Face 搜索模型',
keybindingWin: 'Ctrl + K',
keybindingMac: 'K',
span: {
rowSpan: 0,
colSpan: 0
}
}
];
import KeyMapConfig from './keymap';
const ShortCuts: React.FC<{ intl: any }> = ({ intl }) => {
const platform = platformCall();
const [dataList, setDataList] = React.useState<any[]>(dataSource);
const [dataList, setDataList] = React.useState<any[]>(KeyMapConfig);
const columns = [
{
title: 'Scope',
dataIndex: 'scope',
key: 'scope',
width: 120,
onCell: (row: any, index: number) => {
if (row.span) {
return row.span;
}
return {
rowSpan: 1,
colSpan: 1
};
}
width: 160
},
{
title: 'Command',
title: 'Action',
dataIndex: 'command',
key: 'command'
key: 'command',
render: (text: string, row: any) => {
return <span>{intl.formatMessage({ id: text })}</span>;
}
},
{
title: 'Keybinding',
@ -77,21 +29,14 @@ const ShortCuts: React.FC<{ intl: any }> = ({ intl }) => {
key: 'keybinding',
width: 180,
render: (text: string, row: any) => {
if (platform.isMac) {
return (
<Tag>
<IconFont type="icon-command"></IconFont> + {row.keybindingMac}
</Tag>
);
}
return <Tag>{row.keybindingWin}</Tag>;
return <Tag>{row.keybinding}</Tag>;
}
}
];
const handleInputChange = (e: any) => {
const value = e.target.value;
const list = _.filter(dataSource, (item: any) => {
const list = _.filter(KeyMapConfig, (item: any) => {
return (
item.command.toLowerCase().includes(value.toLowerCase()) ||
item.scope.toLowerCase().includes(value.toLowerCase())
@ -103,10 +48,12 @@ const ShortCuts: React.FC<{ intl: any }> = ({ intl }) => {
const debounceHandleInputChange = _.debounce(handleInputChange, 300);
return (
<div className="short-cuts">
<h3 style={{ marginBottom: 20 }}>GPUStack </h3>
<h3 style={{ marginBottom: 20, fontWeight: 'var(--font-weight-bold)' }}>
{intl.formatMessage({ id: 'shortcuts.title' })}
</h3>
<Input
allowClear
placeholder="Search keybindings"
placeholder={intl.formatMessage({ id: 'shortcuts.search.placeholder' })}
style={{ marginBottom: 16 }}
onChange={debounceHandleInputChange}
prefix={
@ -125,6 +72,7 @@ const ShortCuts: React.FC<{ intl: any }> = ({ intl }) => {
columns={columns}
dataSource={dataList}
pagination={false}
scroll={{ y: 450 }}
></Table>
</div>
);
@ -138,7 +86,7 @@ export const modalConfig = {
style: {
top: '10%'
},
width: 660
width: 700
};
export default React.memo(ShortCuts);

@ -0,0 +1,60 @@
import { KeyMap } from '@/config/hotkeys';
export default [
{
scope: 'playground',
command: 'shortcuts.playground.newmessage',
keybinding: KeyMap.CREATE.iconKeybinding
},
{
scope: 'playground',
command: 'shortcuts.playground.clearmessage',
keybinding: KeyMap.CLEAR.iconKeybinding
},
{
scope: 'playground',
command: 'shortcuts.playground.toggleparams',
keybinding: KeyMap.RIGHT.iconKeybinding
},
{
scope: 'models',
// span: {
// rowSpan: 3,
// colSpan: 1
// },
command: 'shortcuts.models.newmodelHF',
keybinding: KeyMap.NEW1.iconKeybinding
},
{
scope: 'models',
command: 'shortcuts.models.newmodelLM',
keybinding: KeyMap.NEW2.iconKeybinding
// span: {
// rowSpan: 0,
// colSpan: 0
// }
},
{
scope: 'models',
command: 'shortcuts.models.search',
keybinding: KeyMap.SEARCH.iconKeybinding
// span: {
// rowSpan: 0,
// colSpan: 0
// }
},
{
scope: 'resources',
command: 'shortcuts.resources.addworker',
keybinding: KeyMap.CREATE.iconKeybinding
},
{
scope: 'API keys',
command: 'shortcuts.apikeys.new',
keybinding: KeyMap.CREATE.iconKeybinding
},
{
scope: 'users',
command: 'shortcuts.users.new',
keybinding: KeyMap.CREATE.iconKeybinding
}
];

@ -1,17 +1,60 @@
export default {
CREATE: ['ctrl+alt+n', 'option+meta+n'],
SAVE: ['ctrl+s', 'meta+s'],
import { platformCall } from '@/utils';
const platform = platformCall();
const KeybindingsMap = {
CREATE: ['alt+ctrl+N', 'alt+meta+N'],
CLEAR: ['alt+ctrl+W', 'alt+meta+W'],
RIGHT: ['ctrl+RIGHT', 'meta+RIGHT'],
SAVE: ['ctrl+S', 'meta+S'],
SUBMIT: ['ctrl+enter', 'meta+enter'],
SAVEAS: ['ctrl+shift+s', 'meta+shift+s'],
OPEN: ['ctrl+o', 'meta+o'],
CANCEL: ['ctrl+w', 'meta+w'],
SAVEAS: ['alt+ctrl+S', 'alt+meta+S'],
OPEN: ['alt+ctrl+O', 'alt+meta+O'],
CANCEL: ['ctrl+W', 'meta+W'],
DELETE: ['delete'],
COPY: ['ctrl+c', 'meta+c'],
REFRESH: ['ctrl+r', 'meta+r'],
EDIT: ['ctrl+e', 'meta+e'],
SEARCH: ['ctrl+f', 'meta+f'],
RESET: ['ctrl+shift+r', 'meta+shift+r'],
INPUT: ['ctrl+k', 'meta+k'],
COPY: ['ctrl+C', 'meta+C'],
REFRESH: ['ctrl+R', 'meta+R'],
EDIT: ['ctrl+E', 'meta+E'],
SEARCH: ['ctrl+K', 'meta+K'],
RESET: ['alt+ctrl+R', 'alt+meta+R'],
INPUT: ['ctrl+K', 'meta+K'],
NEW1: ['ctrl+1', 'meta+1'],
NEW2: ['ctrl+2', 'meta+2']
};
type KeyBindingType = keyof typeof KeybindingsMap;
type KeybindingValue = {
keybinding: string;
command: KeyBindingType;
textKeybinding: string;
iconKeybinding: string;
};
const KeybiningList: KeybindingValue[] = Object.entries(KeybindingsMap).map(
([key, value]) => {
const keybinding = platform.isMac ? value[1] || value[0] : value[0];
return {
keybinding: keybinding,
command: key,
textKeybinding: platform.isMac
? keybinding.replace('meta', 'Command').replace('alt', 'Option')
: keybinding.replace('ctrl', 'Ctrl'),
iconKeybinding: platform.isMac
? keybinding.replace('meta', '⌘').replace('alt', '⌥')
: keybinding.replace('ctrl', 'Ctrl')
} as KeybindingValue;
}
);
const KeyMap: Record<KeyBindingType, KeybindingValue> = KeybiningList.reduce(
(acc: any, item) => {
acc[item.command] = item;
return acc;
},
{}
);
console.log('KeyMap=========', KeyMap);
export { KeyMap, KeybiningList };
export default KeybindingsMap;

@ -1,4 +1,4 @@
// 应用前置、全局运行的逻辑时 会在这里执行
// Logic that runs globally and before the application will be executed here
import dayjs from 'dayjs';
import localizedFormat from 'dayjs/plugin/localizedFormat';

@ -1,23 +1,27 @@
@media screen and (max-width: 480px) {
/* 在小屏幕的时候可以有更好的体验 */
.umi-plugin-layout-container {
width: 100% !important;
}
.umi-plugin-layout-container > * {
border-radius: 0 !important;
}
}
.umi-plugin-layout-menu .anticon {
margin-right: 8px;
}
.umi-plugin-layout-menu .ant-dropdown-menu-item {
min-width: 160px;
}
.umi-plugin-layout-right {
display: flex !important;
height: 100%;
overflow: hidden;
}
.umi-plugin-layout-right .umi-plugin-layout-action {
display: flex;
align-items: center;
@ -26,25 +30,32 @@
cursor: pointer;
transition: all 0.3s;
}
.umi-plugin-layout-right .umi-plugin-layout-action > i {
color: rgba(255, 255, 255, 0.85);
color: rgba(255, 255, 255, 85%);
vertical-align: middle;
}
.umi-plugin-layout-right .umi-plugin-layout-action:hover {
background: rgba(0, 0, 0, 0.025);
background: rgba(0, 0, 0, 2.5%);
}
.umi-plugin-layout-right .umi-plugin-layout-action.opened {
background: rgba(0, 0, 0, 0.025);
background: rgba(0, 0, 0, 2.5%);
}
.umi-plugin-layout-right .umi-plugin-layout-search {
padding: 0 12px;
}
.umi-plugin-layout-right .umi-plugin-layout-search:hover {
background: transparent;
}
.umi-plugin-layout-name {
margin-left: 8px;
}
.umi-plugin-layout-name.umi-plugin-layout-hide-avatar-img {
margin-left: 0;
}

@ -1,6 +1,4 @@
// @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import GpustackLogo from '@/assets/images/gpustack-logo.png';
import SmallLogo from '@/assets/images/small-logo-200x200.png';
import React from 'react';

@ -35,7 +35,7 @@ type InitialStateType = {
currentUser?: Global.UserInfo;
};
// 过滤出需要显示的路由, 这里的filterFn 指 不希望显示的层级
// Filter out the routes that need to be displayed, where filterFn indicates the levels that should not be shown
const filterRoutes = (
routes: IRoute[],
filterFn: (route: IRoute) => boolean
@ -99,8 +99,6 @@ export default (props: any) => {
setInitialState: null
};
// initialState: InitialStateType
const { initialState, loading, setInitialState } = initialInfo;
const userConfig = {
@ -189,6 +187,8 @@ export default (props: any) => {
const { location } = history;
console.log('onPageChange', userInfo, initialState);
// if user is not change password, redirect to change password page
if (
location.pathname !== loginPath &&
userInfo?.require_password_change
@ -198,8 +198,7 @@ export default (props: any) => {
return;
}
// 如果没有登录,重定向到 login
// if user is not logged in, redirect to login page
if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath);
} else if (location.pathname === '/') {

@ -1,6 +1,4 @@
// @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import React from 'react';
import icons from './icons';
@ -13,7 +11,6 @@ function formatIcon(name: string) {
}
export function patchRoutes({ routes, initialState }) {
console.log('patchRoutes99999', routes);
Object.keys(routes).forEach((key) => {
const { icon } = routes[key];
if (icon && typeof icon === 'string') {

@ -1,5 +1,3 @@
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import type { RunTimeLayoutConfig } from './types';
export interface IRuntimeConfig {
layout?: RunTimeLayoutConfig;

@ -1,8 +1,5 @@
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
/// <reference types="@ant-design/pro-components" />
import type InitialStateType from '@@/plugin-initialState/@@initialState';
import '@ant-design/pro-components';
import type { HeaderProps, ProLayoutProps } from '@ant-design/pro-components';
type InitDataType = ReturnType<typeof InitialStateType>;
@ -26,12 +23,12 @@ export type RunTimeLayoutConfig = (initData: InitDataType) => Omit<
loading: InitDataType['loading'];
initialState: InitDataType['initialState'];
setInitialState: InitDataType['setInitialState'];
},
}
) => JSX.Element)
| false;
rightRender?: (
initialState: InitDataType['initialState'],
setInitialState: InitDataType['setInitialState'],
runtimeConfig: RunTimeLayoutConfig,
runtimeConfig: RunTimeLayoutConfig
) => JSX.Element;
};

@ -5,6 +5,7 @@ import menu from './en-US/menu';
import models from './en-US/models';
import playground from './en-US/playground';
import resources from './en-US/resources';
import shortcuts from './en-US/shortcuts';
import usage from './en-US/usage';
import users from './en-US/users';
@ -17,5 +18,6 @@ export default {
...apikeys,
...users,
...dashboard,
...usage
...usage,
...shortcuts
};

@ -0,0 +1,13 @@
export default {
'shortcuts.search.placeholder': 'Search keybindings',
'shortcuts.title': 'Keyboard shortcuts',
'shortcuts.playground.newmessage': 'New message',
'shortcuts.playground.clearmessage': 'Clear messages',
'shortcuts.playground.toggleparams': 'Collapse/Expand parameters',
'shortcuts.models.newmodelHF': 'Deploy Hugging Face model',
'shortcuts.models.newmodelLM': 'Deploy Ollama model',
'shortcuts.models.search': 'Search models from Hugging Face',
'shortcuts.resources.addworker': 'Add worker',
'shortcuts.apikeys.new': 'New API key',
'shortcuts.users.new': 'New user'
};

@ -5,6 +5,7 @@ import menu from './zh-CN/menu';
import models from './zh-CN/models';
import playground from './zh-CN/playground';
import resources from './zh-CN/resources';
import shortcuts from './zh-CN/shortcuts';
import usage from './zh-CN/usage';
import users from './zh-CN/users';
@ -17,5 +18,6 @@ export default {
...apikeys,
...users,
...dashboard,
...usage
...usage,
...shortcuts
};

@ -0,0 +1,14 @@
// keyborad shortcuts
export default {
'shortcuts.title': '快捷键',
'shortcuts.search.placeholder': '搜索快捷键',
'shortcuts.playground.newmessage': '新建消息',
'shortcuts.playground.clearmessage': '清空消息',
'shortcuts.playground.toggleparams': '收起/展开参数',
'shortcuts.models.newmodelHF': '部署 Hugging Face 模型',
'shortcuts.models.newmodelLM': '部署 Ollama 模型',
'shortcuts.models.search': '从 Hugging Face 搜索模型',
'shortcuts.resources.addworker': '添加节点',
'shortcuts.apikeys.new': '新建 API 密钥',
'shortcuts.users.new': '新建用户'
};

@ -8,11 +8,11 @@ const AccessPage: React.FC = () => {
<PageContainer
ghost
header={{
title: '权限示例'
title: 'permission example'
}}
>
<Access accessible={access.canSeeAdmin}>
<Button> Admin </Button>
<Button>Only Admin can see this button</Button>
</Access>
</PageContainer>
);

@ -1,6 +1,7 @@
import DeleteModal from '@/components/delete-modal';
import PageTools from '@/components/page-tools';
import { PageAction } from '@/config';
import HotKeys from '@/config/hotkeys';
import type { PageActionType } from '@/config/types';
import useTableRowSelection from '@/hooks/use-table-row-selection';
import useTableSort from '@/hooks/use-table-sort';
@ -12,6 +13,7 @@ import { Button, Input, Space, Table, Tooltip } from 'antd';
import dayjs from 'dayjs';
import _ from 'lodash';
import { useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { deleteApisKey, queryApisKeysList } from './apis';
import AddAPIKeyModal from './components/add-apikey';
import { ListItem } from './config/types';
@ -140,6 +142,16 @@ const APIKeys: React.FC = () => {
fetchData();
}, [queryParams]);
useHotkeys(
HotKeys.CREATE,
() => {
handleAddUser();
},
{
enabled: !openAddModal
}
);
return (
<>
<PageContainer

@ -1,5 +1,4 @@
import hotkeys from '@/config/hotkeys';
import { platformCall } from '@/utils';
import { SearchOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Input } from 'antd';
@ -13,7 +12,6 @@ const SearchInput: React.FC<{
const intl = useIntl();
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
const platform = platformCall();
useHotkeys(hotkeys.INPUT.join(','), () => {
inputRef.current?.focus?.();

@ -10,7 +10,6 @@ const Separator: React.FC = () => {
style={{ height: 'calc(100vh - 89px)', marginInline: '0px' }}
></Divider>
<span className="shape"></span>
{/* <span className="shape-s"></span> */}
</div>
);
};

@ -3,6 +3,7 @@
.shape {
position: absolute;
border-radius: 0 2px 0 0;
top: 10px;
left: -10px;
width: 22px;
@ -14,15 +15,4 @@
background-color: var(--color-white-1);
z-index: 100;
}
.shape-s {
position: absolute;
top: 50%;
left: 4px;
width: 30px;
height: 30px;
border: 16px solid transparent;
border-left: 16px solid var(--color-fill-sider);
z-index: 100;
}
}

@ -239,6 +239,30 @@ const MessageList: React.FC<MessageProps> = forwardRef((props, ref) => {
}
);
useHotkeys(
HotKeys.CREATE.join(','),
() => {
handleNewMessage();
},
{
enabled: !loading,
enableOnFormTags: !loading,
preventDefault: true
}
);
useHotkeys(
HotKeys.CLEAR.join(','),
() => {
handleClear();
},
{
enabled: !loading,
enableOnFormTags: !loading,
preventDefault: true
}
);
return (
<div className="ground-left">
<div

@ -1,9 +1,11 @@
import IconFont from '@/components/icon-font';
import HotKeys from '@/config/hotkeys';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Divider, Space } from 'antd';
import classNames from 'classnames';
import { useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import GroundLeft from './components/ground-left';
import ParamsSettings from './components/params-settings';
import './style/play-ground.less';
@ -20,6 +22,18 @@ const Playground: React.FC = () => {
groundLeftRef.current?.viewCode?.();
};
useHotkeys(
HotKeys.RIGHT.join(','),
() => {
setCollapse((pre) => {
return !pre;
});
},
{
preventDefault: true
}
);
return (
<PageContainer
ghost

@ -2,6 +2,7 @@ import DeleteModal from '@/components/delete-modal';
import PageTools from '@/components/page-tools';
import ProgressBar from '@/components/progress-bar';
import StatusTag from '@/components/status-tag';
import Hotkeys from '@/config/hotkeys';
import useTableRowSelection from '@/hooks/use-table-row-selection';
import useTableSort from '@/hooks/use-table-sort';
import { convertFileSize, handleBatchRequest } from '@/utils';
@ -15,6 +16,7 @@ import { useIntl } from '@umijs/max';
import { Button, Input, Space, Table, Tooltip } from 'antd';
import _ from 'lodash';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { deleteWorker, queryWorkersList } from '../apis';
import { WorkerStatusMapValue, status } from '../config';
import { Filesystem, GPUDeviceItem, ListItem } from '../config/types';
@ -166,6 +168,10 @@ const Resources: React.FC = () => {
fetchData();
}, [queryParams]);
useHotkeys(Hotkeys.CREATE.join(','), handleAddWorker, {
enabled: !open
});
return (
<>
<PageTools

@ -2,6 +2,7 @@ import DeleteModal from '@/components/delete-modal';
import DropdownButtons from '@/components/drop-down-buttons';
import PageTools from '@/components/page-tools';
import { PageAction } from '@/config';
import HotKeys from '@/config/hotkeys';
import type { PageActionType } from '@/config/types';
import useTableRowSelection from '@/hooks/use-table-row-selection';
import useTableSort from '@/hooks/use-table-sort';
@ -20,6 +21,7 @@ import { Button, Input, Space, Table, message } from 'antd';
import dayjs from 'dayjs';
import _ from 'lodash';
import { useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { createUser, deleteUser, queryUsersList, updateUser } from './apis';
import AddModal from './components/add-modal';
import { FormData, ListItem } from './config/types';
@ -197,6 +199,16 @@ const Users: React.FC = () => {
fetchData();
}, [queryParams]);
useHotkeys(
HotKeys.CREATE,
() => {
handleAddUser();
},
{
enabled: !openAddModal
}
);
return (
<>
<PageContainer

@ -48,9 +48,8 @@ export const generateRandomArray = (config?: {
let prevValue = Math.floor(Math.random() * (max - min + 1)) + min;
for (let i = 0; i < length; i++) {
// 确保波动不太大
let newValue = prevValue + Math.floor(Math.random() * 21) - offset; // 波动范围 [-10, 10]
newValue = Math.max(min, Math.min(max, newValue)); // 保证在 [10, 100] 范围内
let newValue = prevValue + Math.floor(Math.random() * 21) - offset; // Fluctuation range [-10, 10]
newValue = Math.max(min, Math.min(max, newValue)); // Ensure within [10, 100]
data.push(newValue);
prevValue = newValue;
}
@ -68,15 +67,15 @@ export const generateFluctuatingData2 = ({
const y = [];
for (let i = 0; i < total; i++) {
// 生成一个基本的线性趋势,使用正弦函数
// Generate a basic linear trend using a sine function
const phaseShift = Math.random() * 2 * Math.PI;
const trend =
max * Math.sin((3 * Math.PI * i + phaseShift) / total) + max / 2;
// 生成噪声
// Generate noise
const noise = (Math.random() * 2 - 1) * noiseLevel;
// 叠加趋势和噪声
// Add trend and noise
const value = trend + noise;
x.push(i);
y.push(Math.max(min, value));
@ -94,19 +93,19 @@ export const generateFluctuatingData = ({
min = 0
}) => {
/**
* 线,
* Generate a set of data for a line chart with a natural and aesthetically pleasing trend.
*
* :
* total (number):
* trendType (string): , 'linear', 'sine', 'exponential'
* max (number):
* f (number):
* phase (number):
* min (number):
* Parameters:
* total (number): Number of data points to generate
* trendType (string): Type of data trend, options are 'linear', 'sine', 'exponential'
* max (number): Fluctuation amplitude
* f (number): Fluctuation frequency
* phase (number): Fluctuation phase
* min (number): Minimum value of the data
*
* :
* x (number[]): x
* y (number[]): y
* Returns:
* x (number[]): x-axis data
* y (number[]): y-axis data
*/
const x = Array.from({ length: total }, (_, i) => (i * 10) / (total - 1));
@ -131,11 +130,11 @@ export const generateFluctuatingData = ({
break;
default:
throw new Error(
'无效的trendType参数,请选择"linear", "sine"或"exponential".'
'Invalid trendType parameter. Please choose "linear", "sine", or "exponential".'
);
}
// 将数据调整到最小值
// Adjust the data to the minimum value
const minY = Math.min(...y);
y = y.map((val) => _.round(val - minY + min, 2));

Loading…
Cancel
Save