chore: add english comment
This commit is contained in:
@@ -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",
|
||||
|
||||
Generated
+46
-4
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
];
|
||||
+55
-12
@@ -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
-1
@@ -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';
|
||||
|
||||
+15
-4
@@ -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') {
|
||||
|
||||
Vendored
-2
@@ -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;
|
||||
|
||||
Vendored
+3
-6
@@ -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
|
||||
|
||||
+18
-19
@@ -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));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user