diff --git a/src/assets/styles/common.less b/src/assets/styles/common.less index 87331088..eb76fa62 100644 --- a/src/assets/styles/common.less +++ b/src/assets/styles/common.less @@ -280,3 +280,29 @@ .color-white-light-4 { color: var(--color-white-light-4); } + +textarea { + padding-inline: 6px 0; + padding-block: 0; + background-color: transparent; +} + +textarea::-webkit-scrollbar { + width: var(--scrollbar-size); +} + +textarea::-webkit-scrollbar-track { + background-color: transparent; +} + +textarea::-webkit-scrollbar-thumb { + background-color: transparent; + border-radius: 6px; +} + +textarea:hover { + &::-webkit-scrollbar-thumb { + background-color: var(--color-scrollbar-thumb); + border-radius: 6px; + } +} diff --git a/src/components/editor-wrap/index.less b/src/components/editor-wrap/index.less index 9f6a8a47..f8059b42 100644 --- a/src/components/editor-wrap/index.less +++ b/src/components/editor-wrap/index.less @@ -9,8 +9,8 @@ .editor-header { display: flex; - height: 40px; - padding: 0 10px; + padding-block: 0; + padding-inline: 12px 10px; justify-content: space-between; align-items: center; background-color: rgb(56, 56, 56); diff --git a/src/components/editor-wrap/index.tsx b/src/components/editor-wrap/index.tsx index 556ad258..6c51ee47 100644 --- a/src/components/editor-wrap/index.tsx +++ b/src/components/editor-wrap/index.tsx @@ -1,33 +1,40 @@ -import { Select } from 'antd'; import React from 'react'; +import styled from 'styled-components'; import CopyButton from '../copy-button'; +import SegmentLine from '../segment-line'; import './index.less'; +const Wrapper = styled.div<{ $height: number }>` + height: ${(props) => props.$height}px; +`; + interface EditorwrapProps { + headerHeight?: number; header?: React.ReactNode; children: React.ReactNode; showHeader?: boolean; copyText: string; defaultValue?: string; - langOptions?: { label: string; value: string }[]; + langOptions?: Global.BaseOption[]; styles?: { wrapper?: React.CSSProperties; header?: React.CSSProperties; content?: React.CSSProperties; }; - onChangeLang?: (value: string) => void; + onChangeLang?: (value: string | number) => void; } const EditorWrap: React.FC = ({ + headerHeight = 40, header, children, copyText, - langOptions, + langOptions = [], onChangeLang, defaultValue, styles = {}, showHeader = true }) => { - const handleChangeLang = (value: string) => { + const handleChangeLang = (value: string | number) => { onChangeLang?.(value); }; const renderHeader = () => { @@ -36,15 +43,14 @@ const EditorWrap: React.FC = ({ } if (showHeader) { return ( -
- + > = ({ color: 'rgba(255,255,255,.7)' }} /> -
+ ); } return null; diff --git a/src/components/seal-form/seal-input.tsx b/src/components/seal-form/seal-input.tsx index d28fc3aa..442442d8 100644 --- a/src/components/seal-form/seal-input.tsx +++ b/src/components/seal-form/seal-input.tsx @@ -107,4 +107,7 @@ export default { Password: SealPassword, Number: SealInputNumber, Search: SealInputSearch -} as Record>; +} as Record< + string, + React.FC +>; diff --git a/src/components/seal-form/seal-textarea.tsx b/src/components/seal-form/seal-textarea.tsx index d4316636..ceceb866 100644 --- a/src/components/seal-form/seal-textarea.tsx +++ b/src/components/seal-form/seal-textarea.tsx @@ -1,11 +1,28 @@ import { Form, Input } from 'antd'; import type { TextAreaProps } from 'antd/es/input/TextArea'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState +} from 'react'; +import styled from 'styled-components'; import { SealFormItemProps } from './types'; import Wrapper from './wrapper'; import InputWrapper from './wrapper/input'; -const SealTextArea: React.FC = (props) => { +const LabelWrapper = styled.div` + background-color: var(--color-white-1); +`; + +interface InputTextareaProps extends TextAreaProps { + scaleSize?: boolean; +} + +const SealTextArea: React.FC = ( + props +) => { const { label, placeholder, @@ -21,6 +38,7 @@ const SealTextArea: React.FC = (props) => { extra, addAfter, trim, + scaleSize, ...rest } = props; const [isFocus, setIsFocus] = useState(false); @@ -37,6 +55,16 @@ const SealTextArea: React.FC = (props) => { } }, [props.value]); + const autoSize = useMemo(() => { + const focusRows = props.autoSize || { minRows: 2, maxRows: 5 }; + + if (scaleSize) { + return isFocus ? focusRows : { minRows: 1, maxRows: 1 }; + } + + return focusRows; + }, [props.autoSize, isFocus, scaleSize]); + const handleClickWrapper = useCallback(() => { if (!props.disabled && !isFocus) { inputRef.current?.focus?.({ @@ -68,7 +96,7 @@ const SealTextArea: React.FC = (props) => { onBlur?.(e); } }, - [onBlur] + [onBlur, scaleSize] ); const handleInput = useCallback( @@ -82,7 +110,7 @@ const SealTextArea: React.FC = (props) => { {label}} isFocus={isFocus} required={required} description={description} @@ -94,7 +122,7 @@ const SealTextArea: React.FC = (props) => { > ` + .ant-segmented.segment-line { + padding: 0; + background-color: transparent; + color: var(--color-white-tertiary); + border: none; + box-shadow: none; + height: ${(props) => props.$height}px; + display: flex; + align-items: center; + .ant-segmented-group { + gap: 16px; + height: 100%; + } + .ant-segmented-item:hover { + color: var(--color-white-secondary); + } + .ant-segmented-thumb { + height: 2px; + padding: 0px; + bottom: 0px; + top: unset; + } + .ant-segmented-item-label { + display: flex; + align-items: center; + padding: 0; + font-size: var(--font-size-small); + } + .ant-segmented-item { + background-color: transparent; + padding-bottom: 2px; + display: flex; + align-items: center; + &::after { + background-color: var(--color-white-primary) !important; + width: 100%; + height: 0px; + border-radius: 2px; + bottom: 0px; + top: unset; + } + } + .ant-segmented-item-selected { + background-color: transparent; + color: var(--color-white-primary); + &::after { + height: 2px; + } + } + } +`; + +const SegmentLine: React.FC = (props) => { + const { + height = 32, + size = 'small', + options = [], + className = 'segment-line', + ...rest + } = props; + return ( + + + + ); +}; + +SegmentLine.displayName = 'SegmentLine'; + +export default SegmentLine; diff --git a/src/locales/en-US/models.ts b/src/locales/en-US/models.ts index d22e14a9..4d9ce9c5 100644 --- a/src/locales/en-US/models.ts +++ b/src/locales/en-US/models.ts @@ -21,8 +21,7 @@ export default { 'model.form.ollama.model': 'Ollama Model', 'model.form.ollamaholder': 'Please select or input model name', 'model.deploy.sort': 'Sort', - 'model.deploy.search.placeholder': - 'Type / to search models from {source}', + 'model.deploy.search.placeholder': 'Type / to search models', 'model.form.ollamatips': 'Tip: The following are the preconfigured Ollama models in GPUStack. Please select the model you want, or directly enter the model you wish to deploy in the 【{name}】 input box on the right.', 'models.sort.name': 'Name', diff --git a/src/locales/ja-JP/models.ts b/src/locales/ja-JP/models.ts index 65e88974..3eaa8206 100644 --- a/src/locales/ja-JP/models.ts +++ b/src/locales/ja-JP/models.ts @@ -21,8 +21,7 @@ export default { 'model.form.ollama.model': 'Ollamaモデル', 'model.form.ollamaholder': 'モデル名を選択または入力してください', 'model.deploy.sort': '並び替え', - 'model.deploy.search.placeholder': - 'Type / to search models from {source}', + 'model.deploy.search.placeholder': 'Type / to search models', 'model.form.ollamatips': 'ヒント: 以下はGPUStackで事前設定されたOllamaモデルです。希望するモデルを選択するか、右側の【{name}】入力ボックスにデプロイしたいモデルを直接入力してください。', 'models.sort.name': '名前', diff --git a/src/locales/ru-RU/models.ts b/src/locales/ru-RU/models.ts index 5bb83633..c478f043 100644 --- a/src/locales/ru-RU/models.ts +++ b/src/locales/ru-RU/models.ts @@ -21,8 +21,7 @@ export default { 'model.form.ollama.model': 'Модель Ollama', 'model.form.ollamaholder': 'Выберите или введите название модели', 'model.deploy.sort': 'Сортировка', - 'model.deploy.search.placeholder': - 'Type / to search models from {source}', + 'model.deploy.search.placeholder': 'Type / to search models', 'model.form.ollamatips': 'Подсказка: ниже представлены предустановленные модели Ollama в GPUStack. Выберите нужную или введите модель для развертывания в поле 【{name}】 справа.', 'models.sort.name': 'По имени', diff --git a/src/locales/zh-CN/models.ts b/src/locales/zh-CN/models.ts index 0064d258..92a2d194 100644 --- a/src/locales/zh-CN/models.ts +++ b/src/locales/zh-CN/models.ts @@ -23,7 +23,7 @@ export default { 'model.form.ollama.model': 'Ollama 模型', 'model.form.ollamaholder': '请选择或输入模型名称', 'model.deploy.sort': '排序', - 'model.deploy.search.placeholder': '按 / 开始从 {source} 搜索模型', + 'model.deploy.search.placeholder': '按 / 开始搜索模型', 'model.form.ollamatips': '提示:以下为 GPUStack 预设的 Ollama 模型,请选择你想要的模型或者直接在右侧表单 【{name}】 输入框中输入你要部署的模型。', 'models.sort.name': '名称', diff --git a/src/pages/llmodels/components/compatible-alert.tsx b/src/pages/llmodels/components/compatible-alert.tsx index 7bf79882..c4e531d3 100644 --- a/src/pages/llmodels/components/compatible-alert.tsx +++ b/src/pages/llmodels/components/compatible-alert.tsx @@ -25,7 +25,8 @@ interface CompatibilityAlertProps { const DivWrapper = styled.div` position: relative; - padding-inline: 8px; + padding-inline: 24px; + padding-block: 10px 0; &:hover { .close-wrapper { display: block; @@ -36,11 +37,10 @@ const DivWrapper = styled.div` const CloseWrapper = styled.div` display: none; position: absolute; - top: 6px; - right: 12px; + top: 14px; + right: 28px; line-height: 1; cursor: pointer; - background-color: var(--ant-color-warning-bg); `; const MessageWrapper = styled.div` @@ -104,7 +104,7 @@ const CompatibilityAlert: React.FC = (props) => { type={type || 'warning'} icon={renderIcon} > - {showClose && !['transition', 'success'].includes(type) && ( + {showClose && !['transition'].includes(type) && (