From 2e5f0d9210fc8d140cfdb1116792985e678e8a44 Mon Sep 17 00:00:00 2001 From: jialin Date: Fri, 6 Sep 2024 22:02:12 +0800 Subject: [PATCH] fix: submit message --- src/assets/styles/common.less | 9 + src/components/icon-font/index.tsx | 2 +- src/config/global.d.ts | 8 +- src/locales/en-US/menu.ts | 2 + src/locales/zh-CN/menu.ts | 2 + .../playground/components/chat-footer.tsx | 4 +- .../playground/components/ground-left.tsx | 3 +- .../playground/components/message-input.tsx | 153 ++++++++++++++ .../playground/components/messageInput.tsx | 43 ---- .../components/multiple-chat/content-item.tsx | 19 ++ .../components/multiple-chat/index.tsx | 126 ++++++++++++ .../multiple-chat/message-content.tsx | 22 +++ .../components/multiple-chat/model-item.tsx | 186 ++++++++++++++++++ .../playground/components/params-settings.tsx | 78 +++++--- .../playground/components/upload-img.tsx | 8 +- .../playground/hooks/use-chat-completion.ts | 115 +++++++++++ src/pages/playground/index.tsx | 163 ++++++++++----- src/pages/playground/style/message-input.less | 63 ++++-- src/pages/playground/style/model-item.less | 21 ++ src/pages/playground/style/multiple-chat.less | 12 ++ src/pages/playground/style/play-ground.less | 11 ++ 21 files changed, 906 insertions(+), 144 deletions(-) create mode 100644 src/pages/playground/components/message-input.tsx delete mode 100644 src/pages/playground/components/messageInput.tsx create mode 100644 src/pages/playground/components/multiple-chat/content-item.tsx create mode 100644 src/pages/playground/components/multiple-chat/index.tsx create mode 100644 src/pages/playground/components/multiple-chat/message-content.tsx create mode 100644 src/pages/playground/components/multiple-chat/model-item.tsx create mode 100644 src/pages/playground/hooks/use-chat-completion.ts create mode 100644 src/pages/playground/style/model-item.less create mode 100644 src/pages/playground/style/multiple-chat.less diff --git a/src/assets/styles/common.less b/src/assets/styles/common.less index fd0aac2a..4ea6854a 100644 --- a/src/assets/styles/common.less +++ b/src/assets/styles/common.less @@ -38,6 +38,10 @@ margin-left: 20px; } +.m-l-40 { + margin-left: 40px; +} + .m-r-10 { margin-right: 10px; } @@ -98,6 +102,11 @@ opacity: 0.8; } +.flex-end { + display: flex; + justify-content: flex-end; +} + .flex-column { display: flex; flex-direction: column; diff --git a/src/components/icon-font/index.tsx b/src/components/icon-font/index.tsx index 8d60a63d..58e12960 100644 --- a/src/components/icon-font/index.tsx +++ b/src/components/icon-font/index.tsx @@ -1,7 +1,7 @@ import { createFromIconfontCN } from '@ant-design/icons'; const IconFont = createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/c/font_4613488_3n3aubqzylv.js' + scriptUrl: '//at.alicdn.com/t/c/font_4613488_xpmv3m9655d.js' }); export default IconFont; diff --git a/src/config/global.d.ts b/src/config/global.d.ts index c24120aa..74334eb4 100644 --- a/src/config/global.d.ts +++ b/src/config/global.d.ts @@ -22,14 +22,14 @@ declare namespace Global { id: number; } - interface BaseListItem { + interface BaseListItem { key: string; - value: string | number; + value: T; } - interface BaseOption { + interface BaseOption { label: string; - value: string | number; + value: T; } type SearchParams = Pagination & { search?: string }; diff --git a/src/locales/en-US/menu.ts b/src/locales/en-US/menu.ts index f0063913..a8d219b1 100644 --- a/src/locales/en-US/menu.ts +++ b/src/locales/en-US/menu.ts @@ -1,6 +1,8 @@ export default { 'menu.dashboard': 'Dashboard', 'menu.playground': 'Playground', + 'menu.chat': 'Chat', + 'menu.compare': 'Compare', 'menu.models': 'Models', 'menu.resources': 'Resources', 'menu.apikeys': 'API Keys', diff --git a/src/locales/zh-CN/menu.ts b/src/locales/zh-CN/menu.ts index 60c41720..cc00f804 100644 --- a/src/locales/zh-CN/menu.ts +++ b/src/locales/zh-CN/menu.ts @@ -1,6 +1,8 @@ export default { 'menu.dashboard': '概览', 'menu.playground': '试验场', + 'menu.chat': '聊天', + 'menu.compare': '多模型对比', 'menu.models': '模型', 'menu.resources': '资源', 'menu.apikeys': 'API 密钥', diff --git a/src/pages/playground/components/chat-footer.tsx b/src/pages/playground/components/chat-footer.tsx index 6a4cb6e2..114fee9e 100644 --- a/src/pages/playground/components/chat-footer.tsx +++ b/src/pages/playground/components/chat-footer.tsx @@ -1,7 +1,7 @@ import IconFont from '@/components/icon-font'; import HotKeys from '@/config/hotkeys'; import { platformCall } from '@/utils'; -import { DeleteOutlined, EnterOutlined, PlusOutlined } from '@ant-design/icons'; +import { ClearOutlined, EnterOutlined, PlusOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Button, Col, Row, Space } from 'antd'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -64,7 +64,7 @@ const ChatFooter: React.FC = (props) => { {intl.formatMessage({ id: 'playground.newMessage' })} + + + + {layoutOptions.map((option) => ( + + ))} + +
+ + {!loading ? ( + + ) : ( + + )} +
+ + + + ); +}; + +export default MessageInput; diff --git a/src/pages/playground/components/messageInput.tsx b/src/pages/playground/components/messageInput.tsx deleted file mode 100644 index 8563607a..00000000 --- a/src/pages/playground/components/messageInput.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { SendOutlined } from '@ant-design/icons'; -import { Button, Input } from 'antd'; -import { useState } from 'react'; -import '../style/message-input.less'; - -const MessageInput: React.FC = () => { - const { TextArea } = Input; - const [message, setMessage] = useState(''); - const handleInputChange = (value: string) => { - setMessage(value); - }; - const handleSendMessage = () => { - console.log('send message:', message); - }; - const SendButton: React.FC = () => { - return ( - - ); - }; - return ( -
- - - - -
- ); -}; - -export default MessageInput; diff --git a/src/pages/playground/components/multiple-chat/content-item.tsx b/src/pages/playground/components/multiple-chat/content-item.tsx new file mode 100644 index 00000000..e1d53ca6 --- /dev/null +++ b/src/pages/playground/components/multiple-chat/content-item.tsx @@ -0,0 +1,19 @@ +import { useIntl } from '@umijs/max'; +import React from 'react'; + +const ContentItem: React.FC<{ data: { role: string; content: string } }> = ({ + data +}) => { + const intl = useIntl(); + return ( +
+
+ {' '} + {intl.formatMessage({ id: `playground.${data.role}` })} +
+
{data.content}
+
+ ); +}; + +export default React.memo(ContentItem); diff --git a/src/pages/playground/components/multiple-chat/index.tsx b/src/pages/playground/components/multiple-chat/index.tsx new file mode 100644 index 00000000..07d629aa --- /dev/null +++ b/src/pages/playground/components/multiple-chat/index.tsx @@ -0,0 +1,126 @@ +import { Col, Row } from 'antd'; +import { memo, useEffect, useMemo, useRef, useState } from 'react'; +import '../../style/multiple-chat.less'; +import MessageInput from '../message-input'; +import ModelItem from './model-item'; + +interface MultiCompareProps { + modelList: Global.BaseOption[]; + parmasSettings?: Record; + spans?: number; +} + +const MultiCompare: React.FC = ({ modelList }) => { + const [loadingStatus, setLoadingStatus] = useState([]); + const [parmasSettings, setParamsSettings] = useState>({}); + const [systemMessage, setSystemMessage] = useState(''); + const [currentMessage, setCurrentMessage] = useState< + { + role: 'user' | 'assistant'; + content: string; + }[] + >([]); + const [globalParams, setGlobalParams] = useState>({ + seed: null, + stop: null, + temperature: 1, + top_p: 1, + max_tokens: 1024 + }); + const [spans, setSpans] = useState<{ + span: number; + count: number; + }>({ + span: 12, + count: 2 + }); + const modelRefs = useRef([]); + + const isLoading = useMemo(() => { + return loadingStatus.some((status) => status); + }, [loadingStatus]); + + const modelSelections = useMemo(() => { + const list = modelList.slice?.(0, spans.count); + return list; + }, [modelList, spans.count]); + + useEffect(() => { + modelRefs.current = modelSelections.map(() => { + return {}; + }); + }, [modelSelections]); + + const handleSubmit = (message: string) => { + let msg: any[] = []; + if (message) { + msg = [ + { + role: 'user', + content: message + } + ]; + } + modelRefs.current.forEach(async (ref, index) => { + ref?.setMessageList((preList: any) => { + return [...preList, ...msg]; + }); + setLoadingStatus((preStatus) => { + const newState = [...preStatus]; + newState[index] = true; + return newState; + }); + await ref?.submit(); + setLoadingStatus((preStatus) => { + const newState = [...preStatus]; + newState[index] = false; + return newState; + }); + }); + }; + + const handleAbortFetch = () => { + modelRefs.current.forEach((ref) => { + ref?.abortFetch(); + }); + }; + + const setModelRefs = (index: number, ref: any) => { + modelRefs.current[index] = ref; + }; + + return ( +
+
+ + {modelSelections.map((model, index) => ( + + setModelRefs(index, el)} + modelList={modelSelections} + globalParams={{ + ...globalParams, + model: model.value + }} + systemMessage={systemMessage} + setGlobalParams={setGlobalParams} + /> + + ))} + +
+
+ +
+
+ ); +}; + +export default memo(MultiCompare); diff --git a/src/pages/playground/components/multiple-chat/message-content.tsx b/src/pages/playground/components/multiple-chat/message-content.tsx new file mode 100644 index 00000000..18b28d51 --- /dev/null +++ b/src/pages/playground/components/multiple-chat/message-content.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import ContentItem from './content-item'; + +interface MessageContentProps { + messageList: { + role: string; + uid?: string; + content: string; + }[]; +} + +const MessageContent: React.FC = ({ messageList }) => { + return ( +
+ {messageList.map((item, index) => ( + + ))} +
+ ); +}; + +export default React.memo(MessageContent); diff --git a/src/pages/playground/components/multiple-chat/model-item.tsx b/src/pages/playground/components/multiple-chat/model-item.tsx new file mode 100644 index 00000000..7e056b7b --- /dev/null +++ b/src/pages/playground/components/multiple-chat/model-item.tsx @@ -0,0 +1,186 @@ +import IconFont from '@/components/icon-font'; +import { + ClearOutlined, + CloseOutlined, + MoreOutlined, + SettingOutlined +} from '@ant-design/icons'; +import { useIntl } from '@umijs/max'; +import { Button, Checkbox, Dropdown, Popover, Select } from 'antd'; +import React, { + forwardRef, + useEffect, + useImperativeHandle, + useRef, + useState +} from 'react'; +import SimpleBar from 'simplebar-react'; +import 'simplebar-react/dist/simplebar.min.css'; +import useChatCompletion from '../../hooks/use-chat-completion'; +import '../../style/model-item.less'; +import ParamsSettings from '../params-settings'; +import MessageContent from './message-content'; + +interface ModelItemProps { + model?: string; + globalParams: Record; + setGlobalParams: (value: Record) => void; + modelList: Global.BaseOption[]; + systemMessage: string; + ref: any; +} + +const ModelItem: React.FC = forwardRef( + ({ model, systemMessage, modelList, globalParams, setGlobalParams }, ref) => { + const intl = useIntl(); + const isApplyToAllModels = useRef(false); + const [params, setParams] = useState>({}); + // const [messageList, setMessageList] = useState< + // { + // role: 'user' | 'assistant'; + // content: string; + // }[] + // >([]); + const { messageList, submitMessage, abortFetch, setMessageList, loading } = + useChatCompletion(systemMessage, params); + + useImperativeHandle(ref, () => { + return { + submit: submitMessage, + abortFetch, + setMessageList, + loading + }; + }); + + const actions = [ + { + label: intl.formatMessage({ id: 'common.button.clear' }), + key: 'clear', + icon: + }, + { + label: intl.formatMessage({ id: 'playground.viewcode' }), + key: 'viewcode', + icon: + } + ]; + + const handleModelChange = (value: string) => { + setParams({ + ...params, + model: value + }); + }; + + const handleApplyToAllModels = (e: any) => { + console.log('checkbox change:', e.target.checked); + isApplyToAllModels.current = e.target.checked; + if (e.target.checked) { + setGlobalParams({ + ...params + }); + } + }; + + const handleOnValuesChange = ( + changeValues: any, + allValues: Record + ) => { + console.log('value:', allValues, isApplyToAllModels.current); + if (isApplyToAllModels.current) { + setParams({ + ...params, + ...allValues + }); + setGlobalParams({ + ...allValues + }); + } else { + setParams({ + ...params, + ...changeValues + }); + } + }; + + const handleDropdownAction = ({ key }: { key: string }) => { + console.log('key:', key); + }; + + useEffect(() => { + console.log('globalParams:', globalParams.model, globalParams); + setParams({ + ...params, + ...globalParams + }); + }, [globalParams]); + + useEffect(() => { + return () => { + abortFetch(); + }; + }, []); + + return ( +
+
+ + + + + + + + + } + trigger={['click']} + arrow={false} + fresh={true} + title={ +
+ + Apply to all models + +
+ } + > + +
+ +
+
+ +
+ +
+
+
+ ); + } +); + +export default React.memo(ModelItem); diff --git a/src/pages/playground/components/params-settings.tsx b/src/pages/playground/components/params-settings.tsx index 937b9dc1..558b2797 100644 --- a/src/pages/playground/components/params-settings.tsx +++ b/src/pages/playground/components/params-settings.tsx @@ -6,7 +6,7 @@ import { InfoCircleOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Form, InputNumber, Slider, Tooltip } from 'antd'; import _ from 'lodash'; -import { useEffect, useState } from 'react'; +import { useEffect, useId, useState } from 'react'; import { queryModelsList } from '../apis'; import CustomLabelStyles from '../style/custom-label.less'; @@ -15,19 +15,27 @@ type ParamsSettingsFormProps = { stop?: number; temperature?: number; top_p?: number; - model?: string; max_tokens?: number; + model?: string; }; type ParamsSettingsProps = { selectedModel?: string; + showModelSelector?: boolean; params?: ParamsSettingsFormProps; + model?: string; + onValuesChange?: (changeValues: any, value: Record) => void; setParams: (params: any) => void; + globalParams?: ParamsSettingsFormProps; }; const ParamsSettings: React.FC = ({ selectedModel, - setParams + setParams, + globalParams, + onValuesChange, + model, + showModelSelector = true }) => { const intl = useIntl(); const [ModelList, setModelList] = useState([]); @@ -36,9 +44,11 @@ const ParamsSettings: React.FC = ({ stop: null, temperature: 1, top_p: 1, - max_tokens: 1024 + max_tokens: 1024, + ...globalParams }; const [form] = Form.useForm(); + const formId = useId(); useEffect(() => { const getModelList = async () => { @@ -86,7 +96,9 @@ const ParamsSettings: React.FC = ({ }; const handleValuesChange = (changedValues: any, allValues: any) => { + console.log('changedValues===', changedValues); setParams?.(allValues); + onValuesChange?.(changedValues, allValues); }; const handleFieldValueChange = (val: any, field: string) => { const values = form.getFieldsValue(); @@ -98,12 +110,24 @@ const ParamsSettings: React.FC = ({ ...values, [field]: val }); + onValuesChange?.( + { [field]: val }, + { + ...values, + [field]: val + } + ); }; const handleResetParams = () => { form.setFieldsValue(initialValues); setParams(initialValues); }; + + useEffect(() => { + form.setFieldsValue(globalParams); + console.log('globalParams=========11111', model, globalParams); + }, [globalParams]); const renderLabel = (args: { field: string; label: string; @@ -141,32 +165,40 @@ const ParamsSettings: React.FC = ({ return (
-

- {intl.formatMessage({ id: 'playground.model' })} -

- - name="model" - rules={[ - { - required: true, - message: intl.formatMessage( + {showModelSelector && ( + <> +

+ {intl.formatMessage({ id: 'playground.model' })} +

+ + name="model" + rules={[ { - id: 'common.form.rule.select' - }, - { name: intl.formatMessage({ id: 'playground.model' }) } - ) - } - ]} - > - - + required: true, + message: intl.formatMessage( + { + id: 'common.form.rule.select' + }, + { name: intl.formatMessage({ id: 'playground.model' }) } + ) + } + ]} + > + + + + )}

{intl.formatMessage({ id: 'playground.parameters' })}

diff --git a/src/pages/playground/components/upload-img.tsx b/src/pages/playground/components/upload-img.tsx index 49ebfea7..779d5cfd 100644 --- a/src/pages/playground/components/upload-img.tsx +++ b/src/pages/playground/components/upload-img.tsx @@ -1,4 +1,4 @@ -import { FileImageOutlined } from '@ant-design/icons'; +import { PictureOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Button, Tooltip, Upload } from 'antd'; import type { UploadFile } from 'antd/es/upload'; @@ -71,11 +71,7 @@ const UploadImg: React.FC = ({ handleUpdateImgList }) => { onChange={handleChange} > - + diff --git a/src/pages/playground/hooks/use-chat-completion.ts b/src/pages/playground/hooks/use-chat-completion.ts new file mode 100644 index 00000000..12490e31 --- /dev/null +++ b/src/pages/playground/hooks/use-chat-completion.ts @@ -0,0 +1,115 @@ +import { fetchChunkedData, readStreamData } from '@/utils/fetch-chunk-data'; +import _ from 'lodash'; +import { useRef, useState } from 'react'; +import { CHAT_API } from '../apis'; +import { Roles } from '../config'; + +interface MessageItemProps { + role: string | number; + content: string; + uid: number; +} + +const useChatCompletion = ( + systemMessage: string, + parameters: Record +) => { + const [loading, setLoading] = useState(false); + const messageId = useRef(0); + const [messageList, setMessageList] = useState([ + { + role: 'user', + content: '', + uid: messageId.current + } + ]); + const contentRef = useRef(''); + const controllerRef = useRef(null); + + const setMessageId = () => { + messageId.current = messageId.current + 1; + }; + + const abortFetch = () => { + controllerRef.current?.abort?.(); + }; + + const joinMessage = (chunk: any) => { + if (!chunk) { + return; + } + if (_.get(chunk, 'choices.0.finish_reason')) { + return; + } + contentRef.current = + contentRef.current + _.get(chunk, 'choices.0.delta.content', ''); + setMessageList([ + ...messageList, + { + role: Roles.Assistant, + content: contentRef.current, + uid: messageId.current + } + ]); + }; + + const submitMessage = async () => { + if (!parameters.model) return; + try { + setLoading(true); + setMessageId(); + + controllerRef.current?.abort?.(); + controllerRef.current = new AbortController(); + const signal = controllerRef.current.signal; + const messages = _.map(messageList, (item: MessageItemProps) => { + return { + role: item.role, + content: item.content + }; + }); + + contentRef.current = ''; + const chatParams = { + messages: systemMessage + ? [ + { + role: Roles.System, + content: systemMessage + }, + ...messages + ] + : [...messages], + ...parameters, + stream: true + }; + const result = await fetchChunkedData({ + data: chatParams, + url: CHAT_API, + signal + }); + + if (!result) { + return; + } + const { reader, decoder } = result; + await readStreamData(reader, decoder, (chunk: any) => { + joinMessage(chunk); + }); + setLoading(false); + } catch (error) { + console.log('error=====', error); + setLoading(false); + } + }; + + return { + loading, + messageList, + setMessageList, + submitMessage, + abortFetch + }; +}; + +export default useChatCompletion; diff --git a/src/pages/playground/index.tsx b/src/pages/playground/index.tsx index 1a54ceb2..1753163d 100644 --- a/src/pages/playground/index.tsx +++ b/src/pages/playground/index.tsx @@ -1,12 +1,16 @@ import IconFont from '@/components/icon-font'; import HotKeys from '@/config/hotkeys'; +import { MessageOutlined, OneToOneOutlined } from '@ant-design/icons'; import { PageContainer } from '@ant-design/pro-components'; import { useIntl, useSearchParams } from '@umijs/max'; -import { Button, Divider, Space } from 'antd'; +import { Button, Divider, Segmented, Space, Tabs, TabsProps } from 'antd'; import classNames from 'classnames'; -import { useRef, useState } from 'react'; +import _ from 'lodash'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; +import { queryModelsList } from './apis'; import GroundLeft from './components/ground-left'; +import MultipleChat from './components/multiple-chat'; import ParamsSettings from './components/params-settings'; import './style/play-ground.less'; @@ -16,10 +20,87 @@ const Playground: React.FC = () => { const selectModel = searchParams.get('model') || ''; const [params, setParams] = useState({}); const [collapse, setCollapse] = useState(false); + const [activeKey, setActiveKey] = useState('chat'); const groundLeftRef = useRef(null); + const [modelList, setModelList] = useState[]>([]); + const optionsList = [ + { + label: intl.formatMessage({ id: 'menu.chat' }), + value: 'chat', + icon: + }, + { + label: intl.formatMessage({ id: 'menu.compare' }), + value: 'compare', + icon: + } + ]; - const handleViewCode = () => { + const handleViewCode = useCallback(() => { groundLeftRef.current?.viewCode?.(); + }, [groundLeftRef]); + + const items: TabsProps['items'] = [ + { + key: 'chat', + label: 'Chat', + children: ( + + ) + }, + { + key: 'compare', + label: 'Compare', + children: + } + ]; + + useEffect(() => { + const getModelList = async () => { + try { + const params = { + embedding_only: false + }; + const res = await queryModelsList(params); + const list = _.map(res.data || [], (item: any) => { + return { + value: item.id, + label: item.id + }; + }) as Global.BaseOption[]; + setModelList(list); + } catch (error) { + console.error(error); + } + }; + getModelList(); + }, []); + + const renderExtra = () => { + if (activeKey === 'compare') { + return false; + } + return ( + + + + + ); }; useHotkeys( @@ -37,61 +118,55 @@ const Playground: React.FC = () => { return ( - - - - ]} - className="playground-container" + header={{ + title: ( +
+ {intl.formatMessage({ id: 'menu.playground' })} + setActiveKey(key)} + > +
+ ) + }} + extra={renderExtra()} + className={classNames('playground-container', { + compare: activeKey === 'compare' + })} >
- +
-
+ {activeKey === 'chat' && (
- -
-
- + +
+
+
+ +
-
+ )}
); diff --git a/src/pages/playground/style/message-input.less b/src/pages/playground/style/message-input.less index 16fe360a..5b6122df 100644 --- a/src/pages/playground/style/message-input.less +++ b/src/pages/playground/style/message-input.less @@ -2,6 +2,7 @@ padding-block: 20px; padding-bottom: 30px; border: none; + .ant-pro-footer-bar-right { width: 100%; display: flex; @@ -9,36 +10,54 @@ align-items: center; } } + .messageInput { position: relative; display: flex; align-items: center; - justify-content: center; - width: max(50%, 500px); - background-color: var(--color-fill-1); - border-radius: 30px; - padding-right: 50px; - border: 1px solid transparent; + flex-direction: column; + justify-content: space-between; + width: 100%; + padding-inline: 16px 16px; + padding-bottom: 10px; + border-top: 1px solid var(--ant-color-split); transition: all 0.2s ease; - &:focus-within { - border-color: var(--ant-input-active-border-color); - box-shadow: var(--ant-input-active-shadow); - background-color: var(--color-white-1); - transition: all 0.2s ease; - &:hover { - background-color: var(--color-white-1); + + .tool-bar { + display: flex; + justify-content: space-between; + align-items: center; + height: 40px; + width: 100%; + + .actions { + display: flex; + align-items: center; + gap: 5px; } } + &:focus-within { + // border-color: var(--ant-input-active-border-color); + // box-shadow: var(--ant-input-active-shadow); + // background-color: var(--color-white-1); + // transition: all 0.2s ease; + // &:hover { + // background-color: var(--color-white-1); + // } + } + &:hover { - background: var(--ant-color-fill-secondary); - transition: background 0.2s ease; + // background: var(--ant-color-fill-secondary); + // transition: background 0.2s ease; } + textarea { - padding: 16px; - border-radius: 30px; + padding-inline: 6px 0; + padding-block: 0; background-color: transparent; } + textarea::-webkit-scrollbar { width: 8px; } @@ -51,13 +70,17 @@ background-color: #d9d9d9; border-radius: 6px; } + .send-btn { - position: absolute; - right: 10px; - bottom: 11px; display: flex; justify-content: center; align-items: center; transform: rotate(-30deg); } + + .ant-select-selector { + .ant-select-selection-overflow { + justify-content: flex-end; + } + } } diff --git a/src/pages/playground/style/model-item.less b/src/pages/playground/style/model-item.less new file mode 100644 index 00000000..2f3f4981 --- /dev/null +++ b/src/pages/playground/style/model-item.less @@ -0,0 +1,21 @@ +.model-item { + display: flex; + flex-direction: column; + border: 1px solid var(--ant-color-border); + border-radius: var(--border-radius-base); + height: 100%; + + .header { + padding-inline: 2px 16px; + display: flex; + align-items: center; + justify-content: space-between; + height: 46px; + border-bottom: 1px solid var(--ant-color-border); + } + + .content { + flex: 1; + padding: 16px; + } +} diff --git a/src/pages/playground/style/multiple-chat.less b/src/pages/playground/style/multiple-chat.less new file mode 100644 index 00000000..5fb7c6c0 --- /dev/null +++ b/src/pages/playground/style/multiple-chat.less @@ -0,0 +1,12 @@ +.multiple-chat { + display: flex; + flex-direction: column; + justify-content: space-between; + height: calc(100vh - 72px); + + .chat-list { + flex: 1; + padding-bottom: 16px; + padding-inline: var(--layout-content-inlinepadding); + } +} diff --git a/src/pages/playground/style/play-ground.less b/src/pages/playground/style/play-ground.less index 95b5c571..d0636bd6 100644 --- a/src/pages/playground/style/play-ground.less +++ b/src/pages/playground/style/play-ground.less @@ -2,6 +2,10 @@ display: flex; align-items: flex-start; + .ant-tabs-nav { + display: none; + } + .left { position: relative; display: flex; @@ -71,4 +75,11 @@ .ant-pro-page-container-children-container { padding-right: 0; } + + &.compare { + .ant-pro-page-container-children-container { + padding-inline: 0; + padding-block: 0; + } + } }