diff --git a/config/config.ts b/config/config.ts index 67b39c79..43926d94 100644 --- a/config/config.ts +++ b/config/config.ts @@ -22,6 +22,7 @@ export default defineConfig({ token: { colorPrimary: '#2fbf85', borderRadius: 20, + fontSize: 12, motion: true } } diff --git a/src/assets/styles/common.less b/src/assets/styles/common.less new file mode 100644 index 00000000..9e1f6047 --- /dev/null +++ b/src/assets/styles/common.less @@ -0,0 +1,6 @@ +.m-b-20 { + margin-bottom: 20px; +} +.m-l-10 { + margin-left: 10px; +} diff --git a/src/components/copy-button/index.tsx b/src/components/copy-button/index.tsx new file mode 100644 index 00000000..bade7c4f --- /dev/null +++ b/src/components/copy-button/index.tsx @@ -0,0 +1,43 @@ +import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'; +import { CheckCircleFilled, CopyOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; + +type CopyButtonProps = { + text: string; + disabled?: boolean; + size?: 'small' | 'middle' | 'large'; +}; + +const CopyButton: React.FC = ({ + text, + disabled, + size = 'middle' +}) => { + const { copied, copyToClipboard } = useCopyToClipboard(); + + const handleCopy = async (e: any) => { + e.stopPropagation(); + await copyToClipboard(text); + }; + return ( + + ); +}; + +export default CopyButton; diff --git a/src/components/seal-form/components/label-info.tsx b/src/components/seal-form/components/label-info.tsx index 6db8f0e1..5213fc37 100644 --- a/src/components/seal-form/components/label-info.tsx +++ b/src/components/seal-form/components/label-info.tsx @@ -9,7 +9,6 @@ interface NoteInfoProps { description?: React.ReactNode; } const NoteInfo: React.FC = (props) => { - console.log('props+++++++++', props); const { required, description, label } = props || {}; return ( diff --git a/src/components/seal-form/components/wrapper.less b/src/components/seal-form/components/wrapper.less index c1090fd0..1cfd5267 100644 --- a/src/components/seal-form/components/wrapper.less +++ b/src/components/seal-form/components/wrapper.less @@ -167,4 +167,7 @@ top: -20px; height: @wrapheight - 2px; } + :global(.ant-input-group-addon) { + border: none; + } } diff --git a/src/constants/index.ts b/src/constants/index.ts index ef2b36c1..62b2dcb6 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,7 +1,8 @@ -export const DEFAULT_NAME = 'Umi Max'; +export const DEFAULT_NAME = 'seal'; export const INPUT_WIDTH = { default: 500, large: 600, - small: 400 + small: 400, + mini: 300 }; diff --git a/src/global.less b/src/global.less index 4588e12f..7458c07b 100644 --- a/src/global.less +++ b/src/global.less @@ -71,12 +71,6 @@ html { --ant-modal-footer-margin-top: 30px; } } - .css-var-ru.ant-btn { - --ant-button-content-font-size-lg: 13px; - --ant-button-content-font-size: 12px; - --ant-button-content-font-size-sm: 12px; - --ant-modal-content-padding: 24px 30px; - } .css-var-rf.ant-menu-css-var, .css-var-ri.ant-menu-css-var, @@ -236,5 +230,5 @@ body { } } // ======== menu style end ============ - +@import url('src/assets/styles/common.less'); @import url('src/assets/styles/menu.less'); diff --git a/src/hooks/use-copy-to-clipboard.ts b/src/hooks/use-copy-to-clipboard.ts new file mode 100644 index 00000000..a690f0b6 --- /dev/null +++ b/src/hooks/use-copy-to-clipboard.ts @@ -0,0 +1,23 @@ +import { useCallback, useState } from 'react'; + +const useCopyToClipboard = () => { + const [copied, setCopied] = useState(false); + + const copyToClipboard = useCallback(async (text: string) => { + try { + if (navigator.clipboard) { + await navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => { + setCopied(false); + }, 3000); + } + } catch (error) { + setCopied(false); + } + }, []); + + return { copied, copyToClipboard }; +}; + +export default useCopyToClipboard; diff --git a/src/pages/api-keys/components/add-apikey.tsx b/src/pages/api-keys/components/add-apikey.tsx index 06e15ec8..61653d93 100644 --- a/src/pages/api-keys/components/add-apikey.tsx +++ b/src/pages/api-keys/components/add-apikey.tsx @@ -1,7 +1,8 @@ +import CopyButton from '@/components/copy-button'; import ModalFooter from '@/components/modal-footer'; import SealInput from '@/components/seal-form/seal-input'; import { PageActionType } from '@/config/types'; -import { CopyOutlined, SyncOutlined } from '@ant-design/icons'; +import { SyncOutlined } from '@ant-design/icons'; import { Form, Modal } from 'antd'; type AddModalProps = { @@ -27,9 +28,6 @@ const AddModal: React.FC = ({ }} /> ); - const RenderCopyButton = () => { - return ; - }; return ( = ({ } + addonAfter={ + + } > diff --git a/src/pages/playground/components/chat-footer.tsx b/src/pages/playground/components/chat-footer.tsx new file mode 100644 index 00000000..18e74474 --- /dev/null +++ b/src/pages/playground/components/chat-footer.tsx @@ -0,0 +1,57 @@ +import { + CodeOutlined, + DeleteOutlined, + PlusOutlined, + SaveOutlined +} from '@ant-design/icons'; +import { Button, Col, Row, Space } from 'antd'; +import '../style/chat-footer.less'; + +interface ChatFooterProps { + onSubmit: () => void; + onClear: () => void; + onNewMessage: () => void; + onView: () => void; + feedback?: React.ReactNode; +} + +const ChatFooter: React.FC = (props) => { + const { onSubmit, onClear, onNewMessage, onView, feedback } = props; + return ( +
+ + + + + + + + {feedback} + + + + + + + +
+ ); +}; + +export default ChatFooter; diff --git a/src/pages/playground/components/chatContent.tsx b/src/pages/playground/components/chatContent.tsx index d9129da8..e029ee97 100644 --- a/src/pages/playground/components/chatContent.tsx +++ b/src/pages/playground/components/chatContent.tsx @@ -7,7 +7,7 @@ import { SyncOutlined } from '@ant-design/icons'; import { Button, Space, Tooltip } from 'antd'; -import chatStyle from './chat-style.less'; +import chatStyle from '../style/chat-style.less'; import ChatEmpty from './chatEmpty'; export type ChatContentProps = { diff --git a/src/pages/playground/components/chatEmpty.tsx b/src/pages/playground/components/chatEmpty.tsx index 35764947..2f549e82 100644 --- a/src/pages/playground/components/chatEmpty.tsx +++ b/src/pages/playground/components/chatEmpty.tsx @@ -1,5 +1,5 @@ import Logo from '@/assets/images/logo.png'; -import chatStyle from './chat-style.less'; +import chatStyle from '../style/chat-style.less'; const ChatEmpty: React.FC = () => { return ( diff --git a/src/pages/playground/components/ground-left.tsx b/src/pages/playground/components/ground-left.tsx new file mode 100644 index 00000000..596ee06b --- /dev/null +++ b/src/pages/playground/components/ground-left.tsx @@ -0,0 +1,78 @@ +import { PageContainer } from '@ant-design/pro-components'; +import { useState } from 'react'; +import '../style/ground-left.less'; +import ChatFooter from './chat-footer'; +import MessageItem from './message-item'; +import ReferenceParams from './reference-params'; + +const MessageList: React.FC = () => { + const [messageList, setMessageList] = useState([ + { + role: 'User', + message: 'hello' + }, + { + role: 'Assistant', + message: 'hello, nice to meet you!' + } + ]); + const [activeIndex, setActiveIndex] = useState(-1); + + const handleNewMessage = () => { + console.log('new message'); + messageList.push({ + role: 'User', + message: 'hello' + }); + setMessageList([...messageList]); + setActiveIndex(messageList.length - 1); + }; + + const handleClear = () => { + console.log('clear'); + }; + + const handleView = () => { + console.log('view'); + }; + + const handleSubmit = () => { + console.log('submit'); + }; + + const handleDelete = (index: number) => { + messageList.splice(index, 1); + setMessageList([...messageList]); + }; + + return ( +
+ +
+ {messageList.map((item, index) => { + return ( + handleDelete(index)} + message={item.message} + /> + ); + })} +
+
+
+ } + > +
+
+ ); +}; + +export default MessageList; diff --git a/src/pages/playground/components/message-item.tsx b/src/pages/playground/components/message-item.tsx new file mode 100644 index 00000000..2abc9759 --- /dev/null +++ b/src/pages/playground/components/message-item.tsx @@ -0,0 +1,69 @@ +import { MinusCircleOutlined } from '@ant-design/icons'; +import { Button, Input } from 'antd'; +import { useEffect, useRef, useState } from 'react'; +import { Roles } from '../config'; +import '../style/message-item.less'; + +const MessageContent: React.FC<{ + message: string; + role: string; + isFocus: boolean; + onDelete: () => void; +}> = ({ message, role, isFocus, onDelete }) => { + const [roleType, setRoleType] = useState(role); + const [messageContent, setMessageContent] = useState(message); + const inputRef = useRef(null); + + useEffect(() => { + if (inputRef.current && isFocus) { + inputRef.current.focus(); + } + }, [isFocus]); + + const handleMessageChange = (e: any) => { + setMessageContent(e.target.value); + }; + + const handleRoleChange = () => { + if (roleType === Roles.User) { + setRoleType(Roles.Assistant); + } + if (roleType === Roles.Assistant) { + setRoleType(Roles.User); + } + }; + + const handleDelete = () => { + onDelete(); + }; + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); +}; + +export default MessageContent; diff --git a/src/pages/playground/components/messageInput.tsx b/src/pages/playground/components/messageInput.tsx index 6c83d546..8563607a 100644 --- a/src/pages/playground/components/messageInput.tsx +++ b/src/pages/playground/components/messageInput.tsx @@ -1,7 +1,7 @@ import { SendOutlined } from '@ant-design/icons'; import { Button, Input } from 'antd'; import { useState } from 'react'; -import './message-input.less'; +import '../style/message-input.less'; const MessageInput: React.FC = () => { const { TextArea } = Input; diff --git a/src/pages/playground/components/params-settings.tsx b/src/pages/playground/components/params-settings.tsx index d9fc9516..60514952 100644 --- a/src/pages/playground/components/params-settings.tsx +++ b/src/pages/playground/components/params-settings.tsx @@ -1,22 +1,26 @@ +import FieldWrapper from '@/components/seal-form/field-wrapper'; import SealInput from '@/components/seal-form/seal-input'; import SealSelect from '@/components/seal-form/seal-select'; import { INPUT_WIDTH } from '@/constants'; -import { Button, Form, Select, Space } from 'antd'; +import { Form, Slider } from 'antd'; +import { useState } from 'react'; type ParamsSettingsProps = { seed?: number; stopSequence?: number; temperature?: number; - topK?: number; topP?: number; - repeatPenalty?: number; - repeatLastN?: number; - tfsZ?: number; - contextLength?: number; + model?: string; maxTokens?: number; }; +const dataList = [ + { value: 'llama3:latest', label: 'llama3:latest' }, + { value: 'wangfuyun/AnimateLCM', label: 'wangfuyun/AnimateLCM' }, + { value: 'Revanthraja/Text_to_Vision', label: 'Revanthraja/Text_to_Vision' } +]; const ParamsSettings: React.FC<{ onClose: () => void }> = ({ onClose }) => { + const [ModelList, setModelList] = useState(dataList); const initialValues = { seed: 1, stopSequence: 1, @@ -51,134 +55,60 @@ const ParamsSettings: React.FC<{ onClose: () => void }> = ({ onClose }) => { onFinish={handleOnFinish} onFinishFailed={handleOnFinishFailed} > -
+
+

Model

+ + name="model" + rules={[{ required: true }]} + > + + +

Parameters

+ + name="temperature" + rules={[{ required: true }]} + > + + + + name="maxTokens" + rules={[{ required: true }]} + > + + + + name="topP" + rules={[{ required: true }]} + > + + + + name="seed" rules={[{ required: true }]} > - - name="select" - rules={[{ required: true }]} - > - - 1 - 2 - 3 - - name="stopSequence" rules={[{ required: true }]} > - - name="temperature" - rules={[{ required: true }]} - > - - - - name="topK" - rules={[{ required: true }]} - > - - - - name="topP" - rules={[{ required: true }]} - > - - - - name="repeatPenalty" - rules={[{ required: true }]} - > - - - - name="repeatLastN" - rules={[{ required: true }]} - > - - - - name="tfsZ" - rules={[{ required: true }]} - > - - - - name="contextLength" - rules={[{ required: true }]} - > - - - - name="maxTokens" - rules={[{ required: true }]} - > - -
- - - - - - ); }; diff --git a/src/pages/playground/components/reference-params.tsx b/src/pages/playground/components/reference-params.tsx new file mode 100644 index 00000000..82230b89 --- /dev/null +++ b/src/pages/playground/components/reference-params.tsx @@ -0,0 +1,13 @@ +import '../style/reference-params.less'; + +const ReferenceParams = () => { + return ( +
+ Inference: 597 ms + + Tokens/s: 561 +
+ ); +}; + +export default ReferenceParams; diff --git a/src/pages/playground/config/index.ts b/src/pages/playground/config/index.ts new file mode 100644 index 00000000..3ef6fa46 --- /dev/null +++ b/src/pages/playground/config/index.ts @@ -0,0 +1,14 @@ +export const Roles = { + User: 'User', + Assistant: 'Assistant' +}; +export const playGroundRoles = [ + { + key: 'user', + label: 'User' + }, + { + key: 'assistant', + label: 'Assistant' + } +]; diff --git a/src/pages/playground/index.tsx b/src/pages/playground/index.tsx index d21f72bd..e0c62cb5 100644 --- a/src/pages/playground/index.tsx +++ b/src/pages/playground/index.tsx @@ -1,19 +1,10 @@ -import { ControlOutlined } from '@ant-design/icons'; -import { PageContainer } from '@ant-design/pro-components'; -import { Button, Popover, Select, Space } from 'antd'; +import { Divider } from 'antd'; import { useEffect, useState } from 'react'; -import ChatContent from './components/chatContent'; -import MessageInput from './components/messageInput'; +import GroundLeft from './components/ground-left'; import ParamsSettings from './components/params-settings'; - -const dataList = [ - { value: 'llama3:latest', label: 'llama3:latest' }, - { value: 'wangfuyun/AnimateLCM', label: 'wangfuyun/AnimateLCM' }, - { value: 'Revanthraja/Text_to_Vision', label: 'Revanthraja/Text_to_Vision' } -]; +import './style/play-ground.less'; const Playground: React.FC = () => { - const [ModelList, setModelList] = useState(dataList); const [messageList, setMessageList] = useState([]); const [selectedModel, setSelectedModel] = useState('llama3:latest'); const [showPopover, setShowPopover] = useState(false); @@ -41,49 +32,17 @@ const Playground: React.FC = () => { }, [selectedModel]); return ( - - - - } - destroyTooltipOnHide={true} - title="Params Settings" - trigger="click" - arrow={false} - open={showPopover} - overlayStyle={{ top: 70 }} - overlayInnerStyle={{ paddingRight: 0 }} - placement="bottomRight" - key="popover" - > - - - - ]} - footer={[]} - > -
- +
+
+
- +
+ +
+
+ +
+
); }; diff --git a/src/pages/playground/style/chat-footer.less b/src/pages/playground/style/chat-footer.less new file mode 100644 index 00000000..daa2fbef --- /dev/null +++ b/src/pages/playground/style/chat-footer.less @@ -0,0 +1,7 @@ +.chat-footer { + display: flex; + align-items: center; + padding-inline: 26px; + height: 100px; + // box-shadow: 0px -1px 2px 0px var(--ant-color-border); +} diff --git a/src/pages/playground/components/chat-style.less b/src/pages/playground/style/chat-style.less similarity index 100% rename from src/pages/playground/components/chat-style.less rename to src/pages/playground/style/chat-style.less diff --git a/src/pages/playground/style/ground-left.less b/src/pages/playground/style/ground-left.less new file mode 100644 index 00000000..a9201798 --- /dev/null +++ b/src/pages/playground/style/ground-left.less @@ -0,0 +1,14 @@ +.ground-left { + position: relative; + height: 100vh; + padding-bottom: 120px; + .message-list-wrap { + max-height: calc(100vh - 152px); + overflow-y: auto; + } + .ground-left-footer { + position: absolute; + bottom: 20px; + width: 100%; + } +} diff --git a/src/pages/playground/components/message-input.less b/src/pages/playground/style/message-input.less similarity index 100% rename from src/pages/playground/components/message-input.less rename to src/pages/playground/style/message-input.less diff --git a/src/pages/playground/style/message-item.less b/src/pages/playground/style/message-item.less new file mode 100644 index 00000000..c500d050 --- /dev/null +++ b/src/pages/playground/style/message-item.less @@ -0,0 +1,16 @@ +.message-item { + display: flex; + justify-content: flex-start; + align-items: flex-start; + margin-bottom: 12px; + .role-type { + margin-right: 12px; + .ant-btn { + text-align: left; + width: 100px; + } + } + .message-content-input { + flex: 1; + } +} diff --git a/src/pages/playground/style/play-ground.less b/src/pages/playground/style/play-ground.less new file mode 100644 index 00000000..9819f80f --- /dev/null +++ b/src/pages/playground/style/play-ground.less @@ -0,0 +1,20 @@ +.play-ground { + display: flex; + align-items: flex-start; + .chat { + flex: 1; + display: flex; + flex-direction: column; + } + .params { + // width: 465px; + padding: 20px; + } + .divider-line { + width: 1px; + .ant-divider { + margin: 0; + min-height: 100vh; + } + } +} diff --git a/src/pages/playground/style/reference-params.less b/src/pages/playground/style/reference-params.less new file mode 100644 index 00000000..447e9d88 --- /dev/null +++ b/src/pages/playground/style/reference-params.less @@ -0,0 +1,8 @@ +.reference-params { + display: flex; + cursor: pointer; + height: 40px; + justify-content: center; + align-items: center; + color: var(--ant-orange); +}