diff --git a/xinference/web/ui/package-lock.json b/xinference/web/ui/package-lock.json index c22eb66185..71c3559624 100644 --- a/xinference/web/ui/package-lock.json +++ b/xinference/web/ui/package-lock.json @@ -36,6 +36,7 @@ "react-cookie": "^6.1.1", "react-dom": "^18.2.0", "react-i18next": "^15.4.0", + "react-joyride": "^2.9.3", "react-pro-sidebar": "^1.1.0-alpha.1", "react-router-dom": "^6.14.1", "react-scripts": "5.0.1", @@ -2653,6 +2654,12 @@ "@fullcalendar/core": "~6.1.8" } }, + "node_modules/@gilbarbara/deep-equal": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz", + "integrity": "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw==", + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -7792,6 +7799,12 @@ "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" }, + "node_modules/deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==", + "license": "MIT" + }, "node_modules/deep-equal": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", @@ -10751,6 +10764,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-lite": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-1.2.1.tgz", + "integrity": "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw==", + "license": "MIT" + }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -14383,6 +14402,17 @@ "node": ">=4" } }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/postcss": { "version": "8.4.38", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", @@ -16007,6 +16037,45 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, + "node_modules/react-floater": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.9.tgz", + "integrity": "sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.3.1", + "is-lite": "^0.8.2", + "popper.js": "^1.16.0", + "prop-types": "^15.8.1", + "tree-changes": "^0.9.1" + }, + "peerDependencies": { + "react": "15 - 18", + "react-dom": "15 - 18" + } + }, + "node_modules/react-floater/node_modules/@gilbarbara/deep-equal": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz", + "integrity": "sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==", + "license": "MIT" + }, + "node_modules/react-floater/node_modules/is-lite": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.2.tgz", + "integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==", + "license": "MIT" + }, + "node_modules/react-floater/node_modules/tree-changes": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.9.3.tgz", + "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==", + "license": "MIT", + "dependencies": { + "@gilbarbara/deep-equal": "^0.1.1", + "is-lite": "^0.8.2" + } + }, "node_modules/react-i18next": { "version": "15.4.0", "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.4.0.tgz", @@ -16029,11 +16098,62 @@ } } }, + "node_modules/react-innertext": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/react-innertext/-/react-innertext-1.1.5.tgz", + "integrity": "sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": ">=0.0.0 <=99", + "react": ">=0.0.0 <=99" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-joyride": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.9.3.tgz", + "integrity": "sha512-1+Mg34XK5zaqJ63eeBhqdbk7dlGCFp36FXwsEvgpjqrtyywX2C6h9vr3jgxP0bGHCw8Ilsp/nRDzNVq6HJ3rNw==", + "license": "MIT", + "dependencies": { + "@gilbarbara/deep-equal": "^0.3.1", + "deep-diff": "^1.0.2", + "deepmerge": "^4.3.1", + "is-lite": "^1.2.1", + "react-floater": "^0.7.9", + "react-innertext": "^1.1.5", + "react-is": "^16.13.1", + "scroll": "^3.0.1", + "scrollparent": "^2.1.0", + "tree-changes": "^0.11.2", + "type-fest": "^4.27.0" + }, + "peerDependencies": { + "react": "15 - 18", + "react-dom": "15 - 18" + } + }, + "node_modules/react-joyride/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-joyride/node_modules/type-fest": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz", + "integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -17009,6 +17129,18 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/scroll": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz", + "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==", + "license": "MIT" + }, + "node_modules/scrollparent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.1.0.tgz", + "integrity": "sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA==", + "license": "ISC" + }, "node_modules/select": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", @@ -18151,6 +18283,16 @@ "node": ">=8" } }, + "node_modules/tree-changes": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.11.3.tgz", + "integrity": "sha512-r14mvDZ6tqz8PRQmlFKjhUVngu4VZ9d92ON3tp0EGpFBE6PAHOq8Bx8m8ahbNoGE3uI/npjYcJiqVydyOiYXag==", + "license": "MIT", + "dependencies": { + "@gilbarbara/deep-equal": "^0.3.1", + "is-lite": "^1.2.1" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", diff --git a/xinference/web/ui/package.json b/xinference/web/ui/package.json index 27a9633f77..475750dc55 100644 --- a/xinference/web/ui/package.json +++ b/xinference/web/ui/package.json @@ -32,6 +32,7 @@ "react-cookie": "^6.1.1", "react-dom": "^18.2.0", "react-i18next": "^15.4.0", + "react-joyride": "^2.9.3", "react-pro-sidebar": "^1.1.0-alpha.1", "react-router-dom": "^6.14.1", "react-scripts": "5.0.1", diff --git a/xinference/web/ui/src/components/MenuSide.js b/xinference/web/ui/src/components/MenuSide.js index 8365c8c998..59949d4abc 100644 --- a/xinference/web/ui/src/components/MenuSide.js +++ b/xinference/web/ui/src/components/MenuSide.js @@ -21,6 +21,7 @@ import { } from '@mui/material' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import Joyride, { STATUS } from 'react-joyride' import { useLocation, useNavigate } from 'react-router-dom' import icon from '../media/icon.webp' @@ -36,6 +37,10 @@ const MenuSide = () => { const [drawerWidth, setDrawerWidth] = useState( `${Math.min(Math.max(window.innerWidth * 0.2, 287), 320)}px` ) + const [run, setRun] = useState(false) + const [stepIndex, setStepIndex] = useState(0) + const [spotlightClicks, setSpotlightClicks] = useState(false) + const { i18n, t } = useTranslation() const navItems = [ @@ -71,11 +76,51 @@ const MenuSide = () => { }, ] + const clickableSteps = [4, 5] + + const steps = [ + { + target: '.step-0', + content: t('components.step-0'), + disableBeacon: true, + }, + { + target: '.step-1', + content: t('components.step-1'), + disableBeacon: true, + }, + { + target: '.step-2', + content: t('components.step-2'), + disableBeacon: true, + }, + { + target: '.step-3', + content: t('components.step-3'), + disableBeacon: true, + }, + { + target: '.step-4', + content: t('components.step-4'), + disableBeacon: true, + }, + { + target: '.step-5', + content: t('components.step-5'), + disableBeacon: true, + }, + ] + useEffect(() => { setActive(pathname.substring(1)) }, [pathname]) useEffect(() => { + if (!localStorage.getItem('hasSeenGuide')) { + setRun(true) + localStorage.setItem('hasSeenGuide', 'true') + } + const screenWidth = window.innerWidth const maxDrawerWidth = Math.min(Math.max(screenWidth * 0.2, 287), 320) setDrawerWidth(`${maxDrawerWidth}px`) @@ -96,6 +141,44 @@ const MenuSide = () => { } }, []) + const handleJoyrideCallback = (data) => { + const { index, type, status, action } = data + + if (type === 'step:before') { + setStepIndex(index) + setSpotlightClicks(clickableSteps.includes(index)) + } + + if (status === STATUS.FINISHED || status === STATUS.SKIPPED) { + setRun(false) + setStepIndex(0) + } + + if (type === 'step:after') { + if (action !== 'prev') { + setStepIndex(index + 1) + if (index === 3) { + const target = document.querySelector('.step-3') + if (target) { + target.click() + + setTimeout(() => { + window.dispatchEvent(new Event('resize')) + }, 500) + } + } + } else { + setStepIndex(index - 1) + if (index === 4) { + const target = document.querySelector('.step-goBack') + if (target) { + target.click() + } + } + } + } + } + return ( { }} style={{ zIndex: 1 }} > + {/* Title */} { - + {navItems.map(({ text, label, icon }) => { if (!icon) { return ( diff --git a/xinference/web/ui/src/locales/en.json b/xinference/web/ui/src/locales/en.json index 6327f99bd1..955ab4ff8d 100644 --- a/xinference/web/ui/src/locales/en.json +++ b/xinference/web/ui/src/locales/en.json @@ -208,6 +208,17 @@ "components": { "copySuccess": "Copied to clipboard!", - "suggestsCommonParameters": "Suggests common parameters; others allowed." + "suggestsCommonParameters": "Suggests common parameters; others allowed.", + "back": "Back", + "close": "Close", + "next": "Next", + "last": "Finish", + "skip": "Skip Tour", + "step-0": "Menu bar, containing all main function entries.", + "step-1": "Classify models by category for quick access.", + "step-2": "Use the filter function to quickly locate the desired model.", + "step-3": "Click to open the model parameter configuration window.", + "step-4": "Configure relevant model parameters in this window.", + "step-5": "After filling in the required parameters, click the button to start the model." } } diff --git a/xinference/web/ui/src/locales/zh.json b/xinference/web/ui/src/locales/zh.json index 65225f154c..e9003e00fc 100644 --- a/xinference/web/ui/src/locales/zh.json +++ b/xinference/web/ui/src/locales/zh.json @@ -208,6 +208,17 @@ "components": { "copySuccess": "已复制到剪贴板!", - "suggestsCommonParameters": "提示常用参数,也允许输入其他参数" + "suggestsCommonParameters": "提示常用参数,也允许输入其他参数", + "back": "上一步", + "close": "关闭", + "next": "下一步", + "last": "完成", + "skip": "取消引导", + "step-0": "菜单栏, 包含所有主要功能入口。", + "step-1": "根据模型类别对模型进行归类,便于快速查找。", + "step-2": "可使用筛选功能,快速定位所需模型。", + "step-3": "点击打开模型参数配置窗口。", + "step-4": "在此窗口中配置模型的相关参数。", + "step-5": "填写完必要参数后,点击按钮即可启动模型。" } } diff --git a/xinference/web/ui/src/scenes/launch_model/LaunchModel.js b/xinference/web/ui/src/scenes/launch_model/LaunchModel.js index 61644f7aa9..90d3620630 100644 --- a/xinference/web/ui/src/scenes/launch_model/LaunchModel.js +++ b/xinference/web/ui/src/scenes/launch_model/LaunchModel.js @@ -244,6 +244,7 @@ const LaunchModelComponent = ({ modelType, gpuAvailable, featureModels }) => { margin: '30px 2rem', alignItems: 'center', }} + className="step-2" > {featureModels.length > 0 && ( diff --git a/xinference/web/ui/src/scenes/launch_model/data/data.js b/xinference/web/ui/src/scenes/launch_model/data/data.js index 75f5cbe652..6c2bcf6f21 100644 --- a/xinference/web/ui/src/scenes/launch_model/data/data.js +++ b/xinference/web/ui/src/scenes/launch_model/data/data.js @@ -131,3 +131,5 @@ export const featureModels = [ feature_models: [], }, ] + +export const GUIDE_MODEL_NAME = 'deepseek-r1-0528-qwen3' diff --git a/xinference/web/ui/src/scenes/launch_model/index.js b/xinference/web/ui/src/scenes/launch_model/index.js index dafd384c47..1c77ac6860 100644 --- a/xinference/web/ui/src/scenes/launch_model/index.js +++ b/xinference/web/ui/src/scenes/launch_model/index.js @@ -65,7 +65,10 @@ const LaunchModel = () => { - + setHover(true)} onMouseLeave={() => setHover(false)} onClick={() => { @@ -1417,7 +1422,7 @@ const ModelCard = ({ width="100%" mx="auto" > - + @@ -2217,6 +2222,7 @@ const ModelCard = ({ {isShowProgress && }