chore: add english comment

This commit is contained in:
jialin
2024-08-28 14:51:20 +08:00
parent 9a0aeb491a
commit 9bd6dedd90
28 changed files with 353 additions and 145 deletions
+2
View File
@@ -33,6 +33,7 @@
"jotai": "^2.8.4", "jotai": "^2.8.4",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"marked": "^14.1.0",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"query-string": "^9.0.0", "query-string": "^9.0.0",
"react": "^18.2.0", "react": "^18.2.0",
@@ -42,6 +43,7 @@
"umi-presets-pro": "^2.0.3" "umi-presets-pro": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/marked": "^6.0.0",
"@types/react": "^18.3.1", "@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@umijs/case-sensitive-paths-webpack-plugin": "^1.0.1", "@umijs/case-sensitive-paths-webpack-plugin": "^1.0.1",
+46 -4
View File
@@ -71,6 +71,9 @@ dependencies:
lodash: lodash:
specifier: ^4.17.21 specifier: ^4.17.21
version: 4.17.21 version: 4.17.21
marked:
specifier: ^14.1.0
version: 14.1.0
numeral: numeral:
specifier: ^2.0.6 specifier: ^2.0.6
version: 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) 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: devDependencies:
'@types/marked':
specifier: ^6.0.0
version: 6.0.0
'@types/react': '@types/react':
specifier: ^18.3.1 specifier: ^18.3.1
version: 18.3.1 version: 18.3.1
@@ -4459,7 +4465,7 @@ packages:
dependencies: dependencies:
'@babel/core': 7.24.5 '@babel/core': 7.24.5
postcss: 7.0.39 postcss: 7.0.39
postcss-syntax: 0.36.2(postcss@8.4.38) postcss-syntax: 0.36.2(postcss@7.0.39)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: false dev: false
@@ -4500,7 +4506,7 @@ packages:
postcss-syntax: '>=0.36.2' postcss-syntax: '>=0.36.2'
dependencies: dependencies:
postcss: 7.0.39 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 remark: 13.0.0
unist-util-find-all-after: 3.0.2 unist-util-find-all-after: 3.0.2
transitivePeerDependencies: transitivePeerDependencies:
@@ -4833,6 +4839,13 @@ packages:
resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==} resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==}
dev: false 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: /@types/mdast@3.0.15:
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==, tarball: https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz} resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==, tarball: https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz}
dependencies: dependencies:
@@ -11806,6 +11819,11 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: false 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: /mathml-tag-names@2.1.3:
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
dev: false dev: false
@@ -13125,7 +13143,7 @@ packages:
dependencies: dependencies:
htmlparser2: 3.10.1 htmlparser2: 3.10.1
postcss: 7.0.39 postcss: 7.0.39
postcss-syntax: 0.36.2(postcss@8.4.38) postcss-syntax: 0.36.2(postcss@7.0.39)
dev: false dev: false
/postcss-image-set-function@4.0.7(postcss@8.4.38): /postcss-image-set-function@4.0.7(postcss@8.4.38):
@@ -13599,6 +13617,30 @@ packages:
lodash: 4.17.21 lodash: 4.17.21
postcss: 8.4.38 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): /postcss-syntax@0.36.2(postcss@8.4.38):
resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==} resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==}
peerDependencies: peerDependencies:
@@ -16773,7 +16815,7 @@ packages:
postcss-sass: 0.4.4 postcss-sass: 0.4.4
postcss-scss: 2.1.1 postcss-scss: 2.1.1
postcss-selector-parser: 6.0.16 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 postcss-value-parser: 4.2.0
resolve-from: 5.0.0 resolve-from: 5.0.0
slash: 3.0.0 slash: 3.0.0
+1 -1
View File
@@ -9,7 +9,7 @@ import { RequestConfig, history } from '@umijs/max';
const loginPath = '/login'; const loginPath = '/login';
// 运行时配置 // runtime configuration
export async function getInitialState(): Promise<{ export async function getInitialState(): Promise<{
fetchUserInfo: () => Promise<Global.UserInfo>; fetchUserInfo: () => Promise<Global.UserInfo>;
currentUser?: Global.UserInfo; currentUser?: Global.UserInfo;
+28
View File
@@ -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);
+16 -68
View File
@@ -1,75 +1,27 @@
import { platformCall } from '@/utils';
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import { Input, Table, Tag } from 'antd'; import { Input, Table, Tag } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React from 'react'; import React from 'react';
import IconFont from '../icon-font';
import './index.less'; import './index.less';
import KeyMapConfig from './keymap';
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
}
}
];
const ShortCuts: React.FC<{ intl: any }> = ({ intl }) => { const ShortCuts: React.FC<{ intl: any }> = ({ intl }) => {
const platform = platformCall(); const [dataList, setDataList] = React.useState<any[]>(KeyMapConfig);
const [dataList, setDataList] = React.useState<any[]>(dataSource);
const columns = [ const columns = [
{ {
title: 'Scope', title: 'Scope',
dataIndex: 'scope', dataIndex: 'scope',
key: 'scope', key: 'scope',
width: 120, width: 160
onCell: (row: any, index: number) => {
if (row.span) {
return row.span;
}
return {
rowSpan: 1,
colSpan: 1
};
}
}, },
{ {
title: 'Command', title: 'Action',
dataIndex: 'command', dataIndex: 'command',
key: 'command' key: 'command',
render: (text: string, row: any) => {
return <span>{intl.formatMessage({ id: text })}</span>;
}
}, },
{ {
title: 'Keybinding', title: 'Keybinding',
@@ -77,21 +29,14 @@ const ShortCuts: React.FC<{ intl: any }> = ({ intl }) => {
key: 'keybinding', key: 'keybinding',
width: 180, width: 180,
render: (text: string, row: any) => { render: (text: string, row: any) => {
if (platform.isMac) { return <Tag>{row.keybinding}</Tag>;
return (
<Tag>
<IconFont type="icon-command"></IconFont> + {row.keybindingMac}
</Tag>
);
}
return <Tag>{row.keybindingWin}</Tag>;
} }
} }
]; ];
const handleInputChange = (e: any) => { const handleInputChange = (e: any) => {
const value = e.target.value; const value = e.target.value;
const list = _.filter(dataSource, (item: any) => { const list = _.filter(KeyMapConfig, (item: any) => {
return ( return (
item.command.toLowerCase().includes(value.toLowerCase()) || item.command.toLowerCase().includes(value.toLowerCase()) ||
item.scope.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); const debounceHandleInputChange = _.debounce(handleInputChange, 300);
return ( return (
<div className="short-cuts"> <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 <Input
allowClear allowClear
placeholder="Search keybindings" placeholder={intl.formatMessage({ id: 'shortcuts.search.placeholder' })}
style={{ marginBottom: 16 }} style={{ marginBottom: 16 }}
onChange={debounceHandleInputChange} onChange={debounceHandleInputChange}
prefix={ prefix={
@@ -125,6 +72,7 @@ const ShortCuts: React.FC<{ intl: any }> = ({ intl }) => {
columns={columns} columns={columns}
dataSource={dataList} dataSource={dataList}
pagination={false} pagination={false}
scroll={{ y: 450 }}
></Table> ></Table>
</div> </div>
); );
@@ -138,7 +86,7 @@ export const modalConfig = {
style: { style: {
top: '10%' top: '10%'
}, },
width: 660 width: 700
}; };
export default React.memo(ShortCuts); export default React.memo(ShortCuts);
+60
View File
@@ -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
View File
@@ -1,17 +1,60 @@
export default { import { platformCall } from '@/utils';
CREATE: ['ctrl+alt+n', 'option+meta+n'], const platform = platformCall();
SAVE: ['ctrl+s', 'meta+s'], 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'], SUBMIT: ['ctrl+enter', 'meta+enter'],
SAVEAS: ['ctrl+shift+s', 'meta+shift+s'], SAVEAS: ['alt+ctrl+S', 'alt+meta+S'],
OPEN: ['ctrl+o', 'meta+o'], OPEN: ['alt+ctrl+O', 'alt+meta+O'],
CANCEL: ['ctrl+w', 'meta+w'], CANCEL: ['ctrl+W', 'meta+W'],
DELETE: ['delete'], DELETE: ['delete'],
COPY: ['ctrl+c', 'meta+c'], COPY: ['ctrl+C', 'meta+C'],
REFRESH: ['ctrl+r', 'meta+r'], REFRESH: ['ctrl+R', 'meta+R'],
EDIT: ['ctrl+e', 'meta+e'], EDIT: ['ctrl+E', 'meta+E'],
SEARCH: ['ctrl+f', 'meta+f'], SEARCH: ['ctrl+K', 'meta+K'],
RESET: ['ctrl+shift+r', 'meta+shift+r'], RESET: ['alt+ctrl+R', 'alt+meta+R'],
INPUT: ['ctrl+k', 'meta+k'], INPUT: ['ctrl+K', 'meta+K'],
NEW1: ['ctrl+1', 'meta+1'], NEW1: ['ctrl+1', 'meta+1'],
NEW2: ['ctrl+2', 'meta+2'] 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
View File
@@ -1,4 +1,4 @@
// 应用前置、全局运行的逻辑时 会在这里执行 // Logic that runs globally and before the application will be executed here
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import localizedFormat from 'dayjs/plugin/localizedFormat'; import localizedFormat from 'dayjs/plugin/localizedFormat';
+15 -4
View File
@@ -1,23 +1,27 @@
@media screen and (max-width: 480px) { @media screen and (max-width: 480px) {
/* 在小屏幕的时候可以有更好的体验 */
.umi-plugin-layout-container { .umi-plugin-layout-container {
width: 100% !important; width: 100% !important;
} }
.umi-plugin-layout-container > * { .umi-plugin-layout-container > * {
border-radius: 0 !important; border-radius: 0 !important;
} }
} }
.umi-plugin-layout-menu .anticon { .umi-plugin-layout-menu .anticon {
margin-right: 8px; margin-right: 8px;
} }
.umi-plugin-layout-menu .ant-dropdown-menu-item { .umi-plugin-layout-menu .ant-dropdown-menu-item {
min-width: 160px; min-width: 160px;
} }
.umi-plugin-layout-right { .umi-plugin-layout-right {
display: flex !important; display: flex !important;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }
.umi-plugin-layout-right .umi-plugin-layout-action { .umi-plugin-layout-right .umi-plugin-layout-action {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -26,25 +30,32 @@
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
} }
.umi-plugin-layout-right .umi-plugin-layout-action > i { .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; vertical-align: middle;
} }
.umi-plugin-layout-right .umi-plugin-layout-action:hover { .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 { .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 { .umi-plugin-layout-right .umi-plugin-layout-search {
padding: 0 12px; padding: 0 12px;
} }
.umi-plugin-layout-right .umi-plugin-layout-search:hover { .umi-plugin-layout-right .umi-plugin-layout-search:hover {
background: transparent; background: transparent;
} }
.umi-plugin-layout-name { .umi-plugin-layout-name {
margin-left: 8px; margin-left: 8px;
} }
.umi-plugin-layout-name.umi-plugin-layout-hide-avatar-img { .umi-plugin-layout-name.umi-plugin-layout-hide-avatar-img {
margin-left: 0; margin-left: 0;
} }
-2
View File
@@ -1,6 +1,4 @@
// @ts-nocheck // @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import GpustackLogo from '@/assets/images/gpustack-logo.png'; import GpustackLogo from '@/assets/images/gpustack-logo.png';
import SmallLogo from '@/assets/images/small-logo-200x200.png'; import SmallLogo from '@/assets/images/small-logo-200x200.png';
import React from 'react'; import React from 'react';
+4 -5
View File
@@ -35,7 +35,7 @@ type InitialStateType = {
currentUser?: Global.UserInfo; 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 = ( const filterRoutes = (
routes: IRoute[], routes: IRoute[],
filterFn: (route: IRoute) => boolean filterFn: (route: IRoute) => boolean
@@ -99,8 +99,6 @@ export default (props: any) => {
setInitialState: null setInitialState: null
}; };
// initialState: InitialStateType
const { initialState, loading, setInitialState } = initialInfo; const { initialState, loading, setInitialState } = initialInfo;
const userConfig = { const userConfig = {
@@ -189,6 +187,8 @@ export default (props: any) => {
const { location } = history; const { location } = history;
console.log('onPageChange', userInfo, initialState); console.log('onPageChange', userInfo, initialState);
// if user is not change password, redirect to change password page
if ( if (
location.pathname !== loginPath && location.pathname !== loginPath &&
userInfo?.require_password_change userInfo?.require_password_change
@@ -198,8 +198,7 @@ export default (props: any) => {
return; return;
} }
// 如果没有登录,重定向到 login // if user is not logged in, redirect to login page
if (!initialState?.currentUser && location.pathname !== loginPath) { if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath); history.push(loginPath);
} else if (location.pathname === '/') { } else if (location.pathname === '/') {
-3
View File
@@ -1,6 +1,4 @@
// @ts-nocheck // @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import React from 'react'; import React from 'react';
import icons from './icons'; import icons from './icons';
@@ -13,7 +11,6 @@ function formatIcon(name: string) {
} }
export function patchRoutes({ routes, initialState }) { export function patchRoutes({ routes, initialState }) {
console.log('patchRoutes99999', routes);
Object.keys(routes).forEach((key) => { Object.keys(routes).forEach((key) => {
const { icon } = routes[key]; const { icon } = routes[key];
if (icon && typeof icon === 'string') { if (icon && typeof icon === 'string') {
-2
View File
@@ -1,5 +1,3 @@
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import type { RunTimeLayoutConfig } from './types'; import type { RunTimeLayoutConfig } from './types';
export interface IRuntimeConfig { export interface IRuntimeConfig {
layout?: RunTimeLayoutConfig; layout?: RunTimeLayoutConfig;
+3 -6
View File
@@ -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 type InitialStateType from '@@/plugin-initialState/@@initialState';
import '@ant-design/pro-components';
import type { HeaderProps, ProLayoutProps } from '@ant-design/pro-components'; import type { HeaderProps, ProLayoutProps } from '@ant-design/pro-components';
type InitDataType = ReturnType<typeof InitialStateType>; type InitDataType = ReturnType<typeof InitialStateType>;
@@ -26,12 +23,12 @@ export type RunTimeLayoutConfig = (initData: InitDataType) => Omit<
loading: InitDataType['loading']; loading: InitDataType['loading'];
initialState: InitDataType['initialState']; initialState: InitDataType['initialState'];
setInitialState: InitDataType['setInitialState']; setInitialState: InitDataType['setInitialState'];
}, }
) => JSX.Element) ) => JSX.Element)
| false; | false;
rightRender?: ( rightRender?: (
initialState: InitDataType['initialState'], initialState: InitDataType['initialState'],
setInitialState: InitDataType['setInitialState'], setInitialState: InitDataType['setInitialState'],
runtimeConfig: RunTimeLayoutConfig, runtimeConfig: RunTimeLayoutConfig
) => JSX.Element; ) => JSX.Element;
}; };
+3 -1
View File
@@ -5,6 +5,7 @@ import menu from './en-US/menu';
import models from './en-US/models'; import models from './en-US/models';
import playground from './en-US/playground'; import playground from './en-US/playground';
import resources from './en-US/resources'; import resources from './en-US/resources';
import shortcuts from './en-US/shortcuts';
import usage from './en-US/usage'; import usage from './en-US/usage';
import users from './en-US/users'; import users from './en-US/users';
@@ -17,5 +18,6 @@ export default {
...apikeys, ...apikeys,
...users, ...users,
...dashboard, ...dashboard,
...usage ...usage,
...shortcuts
}; };
+13
View File
@@ -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'
};
+3 -1
View File
@@ -5,6 +5,7 @@ import menu from './zh-CN/menu';
import models from './zh-CN/models'; import models from './zh-CN/models';
import playground from './zh-CN/playground'; import playground from './zh-CN/playground';
import resources from './zh-CN/resources'; import resources from './zh-CN/resources';
import shortcuts from './zh-CN/shortcuts';
import usage from './zh-CN/usage'; import usage from './zh-CN/usage';
import users from './zh-CN/users'; import users from './zh-CN/users';
@@ -17,5 +18,6 @@ export default {
...apikeys, ...apikeys,
...users, ...users,
...dashboard, ...dashboard,
...usage ...usage,
...shortcuts
}; };
+14
View File
@@ -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': '新建用户'
};
+2 -2
View File
@@ -8,11 +8,11 @@ const AccessPage: React.FC = () => {
<PageContainer <PageContainer
ghost ghost
header={{ header={{
title: '权限示例' title: 'permission example'
}} }}
> >
<Access accessible={access.canSeeAdmin}> <Access accessible={access.canSeeAdmin}>
<Button> Admin </Button> <Button>Only Admin can see this button</Button>
</Access> </Access>
</PageContainer> </PageContainer>
); );
+12
View File
@@ -1,6 +1,7 @@
import DeleteModal from '@/components/delete-modal'; import DeleteModal from '@/components/delete-modal';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import HotKeys from '@/config/hotkeys';
import type { PageActionType } from '@/config/types'; import type { PageActionType } from '@/config/types';
import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableRowSelection from '@/hooks/use-table-row-selection';
import useTableSort from '@/hooks/use-table-sort'; import useTableSort from '@/hooks/use-table-sort';
@@ -12,6 +13,7 @@ import { Button, Input, Space, Table, Tooltip } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { deleteApisKey, queryApisKeysList } from './apis'; import { deleteApisKey, queryApisKeysList } from './apis';
import AddAPIKeyModal from './components/add-apikey'; import AddAPIKeyModal from './components/add-apikey';
import { ListItem } from './config/types'; import { ListItem } from './config/types';
@@ -140,6 +142,16 @@ const APIKeys: React.FC = () => {
fetchData(); fetchData();
}, [queryParams]); }, [queryParams]);
useHotkeys(
HotKeys.CREATE,
() => {
handleAddUser();
},
{
enabled: !openAddModal
}
);
return ( return (
<> <>
<PageContainer <PageContainer
@@ -1,5 +1,4 @@
import hotkeys from '@/config/hotkeys'; import hotkeys from '@/config/hotkeys';
import { platformCall } from '@/utils';
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Input } from 'antd'; import { Input } from 'antd';
@@ -13,7 +12,6 @@ const SearchInput: React.FC<{
const intl = useIntl(); const intl = useIntl();
const [isFocus, setIsFocus] = useState(false); const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null); const inputRef = useRef<any>(null);
const platform = platformCall();
useHotkeys(hotkeys.INPUT.join(','), () => { useHotkeys(hotkeys.INPUT.join(','), () => {
inputRef.current?.focus?.(); inputRef.current?.focus?.();
@@ -10,7 +10,6 @@ const Separator: React.FC = () => {
style={{ height: 'calc(100vh - 89px)', marginInline: '0px' }} style={{ height: 'calc(100vh - 89px)', marginInline: '0px' }}
></Divider> ></Divider>
<span className="shape"></span> <span className="shape"></span>
{/* <span className="shape-s"></span> */}
</div> </div>
); );
}; };
+1 -11
View File
@@ -3,6 +3,7 @@
.shape { .shape {
position: absolute; position: absolute;
border-radius: 0 2px 0 0;
top: 10px; top: 10px;
left: -10px; left: -10px;
width: 22px; width: 22px;
@@ -14,15 +15,4 @@
background-color: var(--color-white-1); background-color: var(--color-white-1);
z-index: 100; 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 ( return (
<div className="ground-left"> <div className="ground-left">
<div <div
+14
View File
@@ -1,9 +1,11 @@
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import HotKeys from '@/config/hotkeys';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useSearchParams } from '@umijs/max'; import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Divider, Space } from 'antd'; import { Button, Divider, Space } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import GroundLeft from './components/ground-left'; import GroundLeft from './components/ground-left';
import ParamsSettings from './components/params-settings'; import ParamsSettings from './components/params-settings';
import './style/play-ground.less'; import './style/play-ground.less';
@@ -20,6 +22,18 @@ const Playground: React.FC = () => {
groundLeftRef.current?.viewCode?.(); groundLeftRef.current?.viewCode?.();
}; };
useHotkeys(
HotKeys.RIGHT.join(','),
() => {
setCollapse((pre) => {
return !pre;
});
},
{
preventDefault: true
}
);
return ( return (
<PageContainer <PageContainer
ghost ghost
@@ -2,6 +2,7 @@ import DeleteModal from '@/components/delete-modal';
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 StatusTag from '@/components/status-tag'; import StatusTag from '@/components/status-tag';
import Hotkeys from '@/config/hotkeys';
import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableRowSelection from '@/hooks/use-table-row-selection';
import useTableSort from '@/hooks/use-table-sort'; import useTableSort from '@/hooks/use-table-sort';
import { convertFileSize, handleBatchRequest } from '@/utils'; import { convertFileSize, handleBatchRequest } from '@/utils';
@@ -15,6 +16,7 @@ import { useIntl } from '@umijs/max';
import { Button, Input, Space, Table, Tooltip } from 'antd'; import { Button, Input, Space, Table, Tooltip } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { deleteWorker, queryWorkersList } from '../apis'; import { deleteWorker, queryWorkersList } from '../apis';
import { WorkerStatusMapValue, status } from '../config'; import { WorkerStatusMapValue, status } from '../config';
import { Filesystem, GPUDeviceItem, ListItem } from '../config/types'; import { Filesystem, GPUDeviceItem, ListItem } from '../config/types';
@@ -166,6 +168,10 @@ const Resources: React.FC = () => {
fetchData(); fetchData();
}, [queryParams]); }, [queryParams]);
useHotkeys(Hotkeys.CREATE.join(','), handleAddWorker, {
enabled: !open
});
return ( return (
<> <>
<PageTools <PageTools
+12
View File
@@ -2,6 +2,7 @@ import DeleteModal from '@/components/delete-modal';
import DropdownButtons from '@/components/drop-down-buttons'; import DropdownButtons from '@/components/drop-down-buttons';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import HotKeys from '@/config/hotkeys';
import type { PageActionType } from '@/config/types'; import type { PageActionType } from '@/config/types';
import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableRowSelection from '@/hooks/use-table-row-selection';
import useTableSort from '@/hooks/use-table-sort'; import useTableSort from '@/hooks/use-table-sort';
@@ -20,6 +21,7 @@ import { Button, Input, Space, Table, message } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { createUser, deleteUser, queryUsersList, updateUser } from './apis'; import { createUser, deleteUser, queryUsersList, updateUser } from './apis';
import AddModal from './components/add-modal'; import AddModal from './components/add-modal';
import { FormData, ListItem } from './config/types'; import { FormData, ListItem } from './config/types';
@@ -197,6 +199,16 @@ const Users: React.FC = () => {
fetchData(); fetchData();
}, [queryParams]); }, [queryParams]);
useHotkeys(
HotKeys.CREATE,
() => {
handleAddUser();
},
{
enabled: !openAddModal
}
);
return ( return (
<> <>
<PageContainer <PageContainer
+18 -19
View File
@@ -48,9 +48,8 @@ export const generateRandomArray = (config?: {
let prevValue = Math.floor(Math.random() * (max - min + 1)) + min; let prevValue = Math.floor(Math.random() * (max - min + 1)) + min;
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
// 确保波动不太大 let newValue = prevValue + Math.floor(Math.random() * 21) - offset; // Fluctuation range [-10, 10]
let newValue = prevValue + Math.floor(Math.random() * 21) - offset; // 波动范围 [-10, 10] newValue = Math.max(min, Math.min(max, newValue)); // Ensure within [10, 100]
newValue = Math.max(min, Math.min(max, newValue)); // 保证在 [10, 100] 范围内
data.push(newValue); data.push(newValue);
prevValue = newValue; prevValue = newValue;
} }
@@ -68,15 +67,15 @@ export const generateFluctuatingData2 = ({
const y = []; const y = [];
for (let i = 0; i < total; i++) { for (let i = 0; i < total; i++) {
// 生成一个基本的线性趋势,使用正弦函数 // Generate a basic linear trend using a sine function
const phaseShift = Math.random() * 2 * Math.PI; const phaseShift = Math.random() * 2 * Math.PI;
const trend = const trend =
max * Math.sin((3 * Math.PI * i + phaseShift) / total) + max / 2; max * Math.sin((3 * Math.PI * i + phaseShift) / total) + max / 2;
// 生成噪声 // Generate noise
const noise = (Math.random() * 2 - 1) * noiseLevel; const noise = (Math.random() * 2 - 1) * noiseLevel;
// 叠加趋势和噪声 // Add trend and noise
const value = trend + noise; const value = trend + noise;
x.push(i); x.push(i);
y.push(Math.max(min, value)); y.push(Math.max(min, value));
@@ -94,19 +93,19 @@ export const generateFluctuatingData = ({
min = 0 min = 0
}) => { }) => {
/** /**
* 生成一组用于折线图的数据,具有自然且美观的趋势。 * Generate a set of data for a line chart with a natural and aesthetically pleasing trend.
* *
* 参数: * Parameters:
* total (number): 生成数据点的数量 * total (number): Number of data points to generate
* trendType (string): 数据趋势类型, 可选'linear', 'sine', 'exponential' * trendType (string): Type of data trend, options are 'linear', 'sine', 'exponential'
* max (number): 波动幅度 * max (number): Fluctuation amplitude
* f (number): 波动频率 * f (number): Fluctuation frequency
* phase (number): 波动相位 * phase (number): Fluctuation phase
* min (number): 数据的最小值 * min (number): Minimum value of the data
* *
* 返回: * Returns:
* x (number[]): x轴数据 * x (number[]): x-axis data
* y (number[]): y轴数据 * y (number[]): y-axis data
*/ */
const x = Array.from({ length: total }, (_, i) => (i * 10) / (total - 1)); const x = Array.from({ length: total }, (_, i) => (i * 10) / (total - 1));
@@ -131,11 +130,11 @@ export const generateFluctuatingData = ({
break; break;
default: default:
throw new Error( 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); const minY = Math.min(...y);
y = y.map((val) => _.round(val - minY + min, 2)); y = y.map((val) => _.round(val - minY + min, 2));