From beb395ce326f73a28251bae48dcba9160fdd97d1 Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 28 Apr 2025 18:13:45 +0800 Subject: [PATCH] fix(style): global theme settings in login --- ...redoc.standalon.js => redoc.standalone.js} | 0 src/assets/images/noise-bg.svg | 20 +++ .../images/noise_background_1f1f1f_style.svg | 8 -- src/atoms/settings.ts | 2 +- src/components/footer/index.tsx | 24 +++- src/components/theme-toggle/index.tsx | 42 ++++++ src/config/theme/index.ts | 1 + src/global.less | 2 + .../llmodels/components/deploy-modal.tsx | 21 ++- src/pages/llmodels/style/model-card.less | 1 - src/pages/login/components/login-form.tsx | 31 ++-- src/pages/login/index.tsx | 25 ++-- .../playground/components/ground-images.tsx | 10 +- .../playground/components/image-edit.tsx | 2 +- src/pages/profile/backup.tsx | 135 ------------------ src/pages/profile/components/appearance.tsx | 8 +- .../resources/components/model-files.tsx | 21 ++- 17 files changed, 165 insertions(+), 188 deletions(-) rename public/static/{redoc.standalon.js => redoc.standalone.js} (100%) create mode 100644 src/assets/images/noise-bg.svg delete mode 100644 src/assets/images/noise_background_1f1f1f_style.svg create mode 100644 src/components/theme-toggle/index.tsx delete mode 100644 src/pages/profile/backup.tsx diff --git a/public/static/redoc.standalon.js b/public/static/redoc.standalone.js similarity index 100% rename from public/static/redoc.standalon.js rename to public/static/redoc.standalone.js diff --git a/src/assets/images/noise-bg.svg b/src/assets/images/noise-bg.svg new file mode 100644 index 00000000..b146557c --- /dev/null +++ b/src/assets/images/noise-bg.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/src/assets/images/noise_background_1f1f1f_style.svg b/src/assets/images/noise_background_1f1f1f_style.svg deleted file mode 100644 index 55c39818..00000000 --- a/src/assets/images/noise_background_1f1f1f_style.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/atoms/settings.ts b/src/atoms/settings.ts index 3ed68009..e80f0759 100644 --- a/src/atoms/settings.ts +++ b/src/atoms/settings.ts @@ -5,7 +5,7 @@ import { atomWithStorage } from 'jotai/utils'; type UserSettings = { theme: 'light' | 'realDark' | 'auto'; colorPrimary: string; - isDarkTheme?: boolean; + isDarkTheme: boolean; }; export const userSettingsAtom = atomWithStorage('userSettings', { diff --git a/src/components/footer/index.tsx b/src/components/footer/index.tsx index 4f32b162..23392a6e 100644 --- a/src/components/footer/index.tsx +++ b/src/components/footer/index.tsx @@ -4,10 +4,28 @@ import VersionInfo, { modalConfig } from '@/components/version-info'; import externalLinks from '@/constants/external-links'; import { useIntl } from '@umijs/max'; import { Button, Modal, Space } from 'antd'; -import './index.less'; +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ token, css }) => ({ + footer: css` + bottom: 0; + width: 100%; + background-color: transparent; + padding: 20px 0; + text-align: center; + font-size: var(--font-size-middle); + color: ${token.colorTextTertiary}; + `, + 'footer-content-left-text': { + display: 'flex', + justifyContent: 'center', + alignItems: 'center' + } +})); const Footer: React.FC = () => { const intl = useIntl(); + const { styles } = useStyles(); const showVersion = () => { Modal.info({ @@ -18,10 +36,10 @@ const Footer: React.FC = () => { }; return ( -
+
-
+
© {new Date().getFullYear()} diff --git a/src/components/theme-toggle/index.tsx b/src/components/theme-toggle/index.tsx new file mode 100644 index 00000000..2b6dd95b --- /dev/null +++ b/src/components/theme-toggle/index.tsx @@ -0,0 +1,42 @@ +import useUserSettings from '@/hooks/use-user-settings'; +import { MoonOutlined, SunOutlined } from '@ant-design/icons'; +import { createStyles } from 'antd-style'; +import React from 'react'; + +const useStyles = createStyles(({ token, css }) => ({ + wrapper: css` + display: inline-flex; + justify-content: center; + align-items: center; + cursor: pointer; + color: ${token.colorText}; + font-size: var(--font-size-base); + padding: 0 8px; + gap: 8px; + &:hover { + color: ${token.colorTextTertiary}; + } + .anticon { + font-size: 16px; + } + ` +})); + +const ThemeToggle: React.FC = () => { + const { setTheme, userSettings } = useUserSettings(); + const { styles } = useStyles(); + + const handleChangeTheme = () => { + const currentTheme = + userSettings.theme === 'realDark' ? 'light' : 'realDark'; + setTheme(currentTheme); + }; + + return ( +
+ {userSettings.theme === 'realDark' ? : } +
+ ); +}; + +export default ThemeToggle; diff --git a/src/config/theme/index.ts b/src/config/theme/index.ts index 5eb68192..323f457a 100644 --- a/src/config/theme/index.ts +++ b/src/config/theme/index.ts @@ -2,6 +2,7 @@ import dark from './dark'; import light from './light'; export const colorPrimary = '#007BFF'; + export default { light, dark, diff --git a/src/global.less b/src/global.less index 88712554..ce681d72 100644 --- a/src/global.less +++ b/src/global.less @@ -74,6 +74,7 @@ html { --color-fill-spin-bg: rgba(255, 255, 255, 60%); --width-tooltip-max: 300px; --color-bg-tooltip: '#fff'; + --color-modal-content-bg: rgba(255, 255, 255, 90%); // ======== input ============ --box-shadow-base: 0 4px 6px rgba(227, 232, 240, 70%); --ant-border-radius-lg: 4px; @@ -93,6 +94,7 @@ html[data-theme='realDark'] { --color-bg-tooltip: #424242; --color-editor-header-bg: #292929; --color-progress-text: rgba(255, 255, 255, 80%); + --color-modal-content-bg: #1f1f1f; background: #141414; } diff --git a/src/pages/llmodels/components/deploy-modal.tsx b/src/pages/llmodels/components/deploy-modal.tsx index e1a9143a..7e25e2df 100644 --- a/src/pages/llmodels/components/deploy-modal.tsx +++ b/src/pages/llmodels/components/deploy-modal.tsx @@ -132,6 +132,7 @@ const AddModal: FC = (props) => { } return categories || null; }; + const handleSelectModelFile = (item: any, evaluate?: boolean) => { form.current?.form?.resetFields(resetFields); const modelInfo = onSelectModel(selectedModel, props.source); @@ -142,7 +143,11 @@ const AddModal: FC = (props) => { categories: getCategory(item) }); - if (item.fakeName) { + if ( + item.fakeName && + !isHolderRef.current.model && + !isHolderRef.current.file + ) { handleShowCompatibleAlert(item.evaluateResult); } }; @@ -153,7 +158,9 @@ const AddModal: FC = (props) => { setIsGGUF(false); form.current?.form?.resetFields(resetFields); const modelInfo = onSelectModel(item, props.source); - handleShowCompatibleAlert(item.evaluateResult); + if (!isHolderRef.current.model) { + handleShowCompatibleAlert(item.evaluateResult); + } form.current?.setFieldsValue?.({ ...getDefaultSpec(item), ...modelInfo, @@ -256,7 +263,15 @@ const AddModal: FC = (props) => { const displayEvaluateStatus = (data: { show?: boolean; flag: Record; - }) => {}; + }) => { + setIsHolderRef(data.flag); + setWarningStatus({ + show: isHolderRef.current.model || isHolderRef.current.file, + title: '', + type: 'transition', + message: intl.formatMessage({ id: 'models.form.evaluating' }) + }); + }; useEffect(() => { if (open) { diff --git a/src/pages/llmodels/style/model-card.less b/src/pages/llmodels/style/model-card.less index 2ecf7f8f..0575900f 100644 --- a/src/pages/llmodels/style/model-card.less +++ b/src/pages/llmodels/style/model-card.less @@ -28,7 +28,6 @@ .mkd-title { cursor: pointer; background-color: var(--ant-color-fill-tertiary); - border-bottom: 1px solid rgba(255, 255, 255, 10%); display: flex; justify-content: space-between; align-items: center; diff --git a/src/pages/login/components/login-form.tsx b/src/pages/login/components/login-form.tsx index 7f526d9f..4b236792 100644 --- a/src/pages/login/components/login-form.tsx +++ b/src/pages/login/components/login-form.tsx @@ -2,6 +2,7 @@ import LogoIcon from '@/assets/images/gpustack-logo.png'; import { initialPasswordAtom, userAtom } from '@/atoms/user'; import LangSelect from '@/components/lang-select'; import SealInput from '@/components/seal-form/seal-input'; +import ThemeToggle from '@/components/theme-toggle'; import { CRYPT_TEXT, REMEMBER_ME_KEY, @@ -12,6 +13,7 @@ import { import { LockOutlined, UserOutlined } from '@ant-design/icons'; import { useIntl, useModel } from '@umijs/max'; import { Button, Checkbox, Form } from 'antd'; +import { createStyles } from 'antd-style'; import CryptoJS from 'crypto-js'; import { useAtom } from 'jotai'; import { useEffect, useMemo } from 'react'; @@ -19,7 +21,26 @@ import { flushSync } from 'react-dom'; import { login } from '../apis'; import { checkDefaultPage } from '../utils'; +const useStyles = createStyles(({ token, css }) => ({ + header: css` + display: flex; + align-items: center; + gap: 8px; + position: fixed; + right: 0; + top: 0; + padding: 20px; + .anticon-global { + color: ${token.colorText}; + } + .anticon:hover { + color: ${token.colorTextTertiary}; + } + ` +})); + const LoginForm = () => { + const { styles } = useStyles(); const [userInfo, setUserInfo] = useAtom(userAtom); const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom); const { initialState, setInitialState } = useModel('@@initialState') || {}; @@ -130,14 +151,8 @@ const LoginForm = () => { return (
-
+
+
diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 64fa8085..aab1e6f2 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -11,7 +11,7 @@ import LoginForm from './components/login-form'; import PasswordForm from './components/password-form'; import { checkDefaultPage } from './utils'; -const Wrapper = styled.div` +const Wrapper = styled.div<{ $isDarkTheme: boolean }>` position: fixed; top: 0; left: 0; @@ -19,9 +19,11 @@ const Wrapper = styled.div` right: 0; min-height: 100vh; z-index: -1; - background: url(${Bg2}) center center no-repeat; - background-size: cover; - opacity: 0.6; + background: ${({ $isDarkTheme }) => + $isDarkTheme + ? `repeating-linear-gradient(45deg, #000, transparent 120px)` + : `url(${Bg2}) center center /cover no-repeat`}; + opacity: ${({ $isDarkTheme }) => ($isDarkTheme ? 1 : 0.6)}; `; const Box = styled.div` @@ -38,7 +40,7 @@ const FormWrapper = styled.div` width: max-content; height: max-content; padding: 32px; - background-color: var(--ant-modal-content-bg); + background-color: var(--color-modal-content-bg); .field-wrapper { background-color: transparent !important; @@ -50,15 +52,10 @@ const FormWrapper = styled.div` `; const Login = () => { - const { themeData, userSettings } = useUserSettings(); - const { token } = theme.useToken(); + const { themeData, userSettings, isDarkTheme } = useUserSettings(); const [userInfo, setUserInfo] = useAtom(userAtom); const { initialState, setInitialState } = useModel('@@initialState') || {}; - const globalCssvars: Record = { - '--ant-modal-content-bg': 'rgba(255, 255, 255, 0.9)' - }; - const gotoDefaultPage = async (userInfo: any) => { if (!userInfo || userInfo?.require_password_change) { return; @@ -72,9 +69,11 @@ const Login = () => { })); } }; + useEffect(() => { gotoDefaultPage(userInfo); }, [userInfo]); + useEffect(() => { document.title = 'GPUStack'; }, []); @@ -90,8 +89,8 @@ const Login = () => { ...themeData }} > -
- +
+ {userInfo?.require_password_change ? ( diff --git a/src/pages/playground/components/ground-images.tsx b/src/pages/playground/components/ground-images.tsx index 91d056c3..6ff8d8a4 100644 --- a/src/pages/playground/components/ground-images.tsx +++ b/src/pages/playground/components/ground-images.tsx @@ -158,15 +158,7 @@ const GroundImages: React.FC = forwardRef((props, ref) => { } const params = { - ..._.omitBy( - parameters, - (value: string, key: string) => - !value || ['width', 'height', 'seed'].includes(key) - ), - size: - parameters.size === 'custom' - ? `${parameters.width}x${parameters.height}` - : parameters.size, + ..._.omitBy(finalParameters, (value: string) => !value), seed: parameters.random_seed ? generateRandomNumber() : parameters.seed, stream: true, stream_options: { diff --git a/src/pages/playground/components/image-edit.tsx b/src/pages/playground/components/image-edit.tsx index de5dbedf..f1012c41 100644 --- a/src/pages/playground/components/image-edit.tsx +++ b/src/pages/playground/components/image-edit.tsx @@ -154,7 +154,7 @@ const GroundImages: React.FC = forwardRef((props, ref) => { prompt: currentPrompt } }); - }, [finalParameters, currentPrompt, parameters.size]); + }, [finalParameters, currentPrompt, isOpenaiCompatible]); const handleClear = () => { setCurrentPrompt(''); diff --git a/src/pages/profile/backup.tsx b/src/pages/profile/backup.tsx deleted file mode 100644 index c3cfd820..00000000 --- a/src/pages/profile/backup.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import FormButtons from '@/components/form-buttons'; -import SealInput from '@/components/seal-form/seal-input'; -import { PasswordReg } from '@/config'; -import { INPUT_WIDTH } from '@/constants'; -import { updatePassword } from '@/pages/login/apis'; -import { PageContainer } from '@ant-design/pro-components'; -import { useIntl } from '@umijs/max'; -import { Form, message } from 'antd'; -import React from 'react'; - -interface ProfileProps { - username?: string; - new_password: string; - current_password: string; -} -const Profile: React.FC = () => { - const [form] = Form.useForm(); - const intl = useIntl(); - - const handleOnFinish = async (values: any) => { - try { - await updatePassword({ - new_password: values.new_password, - current_password: values.current_password - }); - message.success(intl.formatMessage({ id: 'common.message.success' })); - } catch (error) {} - }; - - const handleOnFinishFailed = (errorInfo: any) => { - console.log('handleOnFinishFailed', errorInfo); - }; - const handleCancel = () => { - form.resetFields(); - }; - - return ( - -
- - name="current_password" - rules={[ - { - required: true, - message: intl.formatMessage( - { id: 'common.form.rule.input' }, - { - name: intl.formatMessage({ - id: 'users.form.currentpassword' - }) - } - ) - } - ]} - > - - - - name="new_password" - rules={[ - { - required: true, - pattern: PasswordReg, - message: intl.formatMessage({ - id: 'users.form.rule.password' - }) - } - ]} - > - - - ({ - validator(_, value) { - if (!value || getFieldValue('new_password') === value) { - return Promise.resolve(); - } - return Promise.reject( - new Error( - intl.formatMessage({ id: 'users.password.confirm.error' }) - ) - ); - } - }) - ]} - > - - - - -
- ); -}; - -export default Profile; diff --git a/src/pages/profile/components/appearance.tsx b/src/pages/profile/components/appearance.tsx index a3fb8eb7..859c1df0 100644 --- a/src/pages/profile/components/appearance.tsx +++ b/src/pages/profile/components/appearance.tsx @@ -17,14 +17,18 @@ const ThemeOptions = [ } ]; const Appearance: React.FC = () => { - const { setTheme } = useUserSettings(); + const { setTheme, userSettings } = useUserSettings(); const intl = useIntl(); const handleOnChange = (e: any) => { setTheme(e.target.value); }; return ( - + {ThemeOptions.map((item) => ( {intl.formatMessage({ id: item.label })} diff --git a/src/pages/resources/components/model-files.tsx b/src/pages/resources/components/model-files.tsx index 1e01113e..543f2bec 100644 --- a/src/pages/resources/components/model-files.tsx +++ b/src/pages/resources/components/model-files.tsx @@ -546,6 +546,9 @@ const ModelFiles = () => { { title: intl.formatMessage({ id: 'models.form.source' }), dataIndex: 'source', + ellipsis: { + showTitle: false + }, render: (text: string, record: ListItem) => { const modelInfo = getModelInfo(record); const { repo_id } = modelInfo; @@ -561,6 +564,9 @@ const ModelFiles = () => { { title: 'Worker', dataIndex: 'worker_name', + ellipsis: { + showTitle: false + }, render: (text: string, record: ListItem) => { return ( @@ -581,6 +587,9 @@ const ModelFiles = () => { title: intl.formatMessage({ id: 'resources.modelfiles.form.path' }), dataIndex: 'resolved_paths', width: '35%', + ellipsis: { + showTitle: false + }, render: (text: string, record: ListItem) => ( ) @@ -588,10 +597,12 @@ const ModelFiles = () => { { title: intl.formatMessage({ id: 'resources.modelfiles.size' }), dataIndex: 'size', - width: 100, + ellipsis: { + showTitle: false + }, render: (text: string, record: ListItem) => { return ( - + {convertFileSize(record.size, 1, true)} ); @@ -601,9 +612,11 @@ const ModelFiles = () => { title: intl.formatMessage({ id: 'common.table.createTime' }), dataIndex: 'created_at', sorter: false, - width: 180, + ellipsis: { + showTitle: false + }, render: (text: number) => ( - + {dayjs(text).format('YYYY-MM-DD HH:mm:ss')} )