chore: playground layout
This commit is contained in:
@@ -22,6 +22,7 @@ export default defineConfig({
|
||||
token: {
|
||||
colorPrimary: '#2fbf85',
|
||||
borderRadius: 20,
|
||||
fontSize: 12,
|
||||
motion: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
.m-b-20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.m-l-10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
@@ -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<CopyButtonProps> = ({
|
||||
text,
|
||||
disabled,
|
||||
size = 'middle'
|
||||
}) => {
|
||||
const { copied, copyToClipboard } = useCopyToClipboard();
|
||||
|
||||
const handleCopy = async (e: any) => {
|
||||
e.stopPropagation();
|
||||
await copyToClipboard(text);
|
||||
};
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
shape="circle"
|
||||
size={size}
|
||||
onClick={handleCopy}
|
||||
disabled={!!disabled}
|
||||
>
|
||||
{copied ? (
|
||||
<CheckCircleFilled
|
||||
style={{ color: 'var(--ant-color-primary)', fontSize: '14px' }}
|
||||
/>
|
||||
) : (
|
||||
<CopyOutlined
|
||||
style={{ color: 'var(--ant-color-primary)', fontSize: '14px' }}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default CopyButton;
|
||||
@@ -9,7 +9,6 @@ interface NoteInfoProps {
|
||||
description?: React.ReactNode;
|
||||
}
|
||||
const NoteInfo: React.FC<NoteInfoProps> = (props) => {
|
||||
console.log('props+++++++++', props);
|
||||
const { required, description, label } = props || {};
|
||||
return (
|
||||
<span className="label-text">
|
||||
|
||||
@@ -167,4 +167,7 @@
|
||||
top: -20px;
|
||||
height: @wrapheight - 2px;
|
||||
}
|
||||
:global(.ant-input-group-addon) {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
+1
-7
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
@@ -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<AddModalProps> = ({
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const RenderCopyButton = () => {
|
||||
return <CopyOutlined></CopyOutlined>;
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -52,7 +50,9 @@ const AddModal: React.FC<AddModalProps> = ({
|
||||
<Form.Item name="secretkey" rules={[{ required: true }]}>
|
||||
<SealInput.Input
|
||||
label="Secret Key"
|
||||
addonAfter={<RenderCopyButton></RenderCopyButton>}
|
||||
addonAfter={
|
||||
<CopyButton text={form.getFieldValue('secretKey')}></CopyButton>
|
||||
}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
@@ -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<ChatFooterProps> = (props) => {
|
||||
const { onSubmit, onClear, onNewMessage, onView, feedback } = props;
|
||||
return (
|
||||
<div className="chat-footer">
|
||||
<Row style={{ width: '100%' }}>
|
||||
<Col span={8}>
|
||||
<Space size={20}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={onNewMessage}
|
||||
>
|
||||
New Message
|
||||
</Button>
|
||||
<Button icon={<DeleteOutlined></DeleteOutlined>} onClick={onClear}>
|
||||
Clear
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={8}>{feedback}</Col>
|
||||
<Col span={8} style={{ textAlign: 'right' }}>
|
||||
<Space size={20}>
|
||||
<Button icon={<CodeOutlined></CodeOutlined>} onClick={onView}>
|
||||
View
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined></SaveOutlined>}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatFooter;
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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<any[]>([
|
||||
{
|
||||
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 (
|
||||
<div className="ground-left">
|
||||
<PageContainer title={false} className="message-list-wrap">
|
||||
<div>
|
||||
{messageList.map((item, index) => {
|
||||
return (
|
||||
<MessageItem
|
||||
key={index}
|
||||
role={item.role}
|
||||
isFocus={index === activeIndex}
|
||||
onDelete={() => handleDelete(index)}
|
||||
message={item.message}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</PageContainer>
|
||||
<div className="ground-left-footer">
|
||||
<ChatFooter
|
||||
onClear={handleClear}
|
||||
onNewMessage={handleNewMessage}
|
||||
onSubmit={handleSubmit}
|
||||
onView={handleView}
|
||||
feedback={<ReferenceParams></ReferenceParams>}
|
||||
></ChatFooter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MessageList;
|
||||
@@ -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<any>(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 (
|
||||
<div className="message-item">
|
||||
<div className="role-type">
|
||||
<Button onClick={handleRoleChange} type="text">
|
||||
{roleType}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="message-content-input">
|
||||
<Input.TextArea
|
||||
ref={inputRef}
|
||||
style={{ paddingBlock: '12px' }}
|
||||
value={messageContent}
|
||||
autoSize={true}
|
||||
variant="filled"
|
||||
onChange={handleMessageChange}
|
||||
></Input.TextArea>
|
||||
</div>
|
||||
<div className="delete-btn">
|
||||
<Button
|
||||
type="text"
|
||||
shape="circle"
|
||||
style={{ color: 'var(--ant-color-primary' }}
|
||||
onClick={handleDelete}
|
||||
icon={<MinusCircleOutlined />}
|
||||
></Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MessageContent;
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
maxHeight: '450px',
|
||||
overflow: 'auto',
|
||||
paddingRight: 'var(--ant-popover-inner-padding)'
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<h3 className="m-b-20 m-l-10">Model</h3>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="model"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealSelect options={ModelList} label="Model"></SealSelect>
|
||||
</Form.Item>
|
||||
<h3 className="m-b-20 m-l-10">Parameters</h3>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="temperature"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="Temperature"
|
||||
style={{ width: INPUT_WIDTH.mini }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="maxTokens"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="Max Tokens"
|
||||
style={{ width: INPUT_WIDTH.mini }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="topP"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<FieldWrapper label="TopP">
|
||||
<Slider defaultValue={50}></Slider>
|
||||
</FieldWrapper>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="seed"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="Seed"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
style={{ width: INPUT_WIDTH.mini }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="select"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealSelect label="Select" style={{ width: INPUT_WIDTH.default }}>
|
||||
<Select.Option value={1}>1</Select.Option>
|
||||
<Select.Option value={2}>2</Select.Option>
|
||||
<Select.Option value={3}>3</Select.Option>
|
||||
</SealSelect>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="stopSequence"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="Stop Sequence"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
style={{ width: INPUT_WIDTH.mini }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="temperature"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="Temperature"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
disabled={true}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="topK"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="TopK"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="topP"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="TopP"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="repeatPenalty"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="Repeat Penalty"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="repeatLastN"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Search
|
||||
label="Repeat Last N"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
disabled={true}
|
||||
></SealInput.Search>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="tfsZ"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label="TFSZ"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="contextLength"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.Number
|
||||
label="Context Length"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
></SealInput.Number>
|
||||
</Form.Item>
|
||||
<Form.Item<ParamsSettingsProps>
|
||||
name="maxTokens"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<SealInput.TextArea
|
||||
label="Max Tokens"
|
||||
style={{ width: INPUT_WIDTH.default }}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item noStyle>
|
||||
<Space
|
||||
size={20}
|
||||
align="end"
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'end',
|
||||
paddingRight: 'var(--ant-popover-inner-padding)',
|
||||
paddingTop: 'var(--ant-popover-inner-padding)'
|
||||
}}
|
||||
>
|
||||
<Button onClick={handleCancel}>Cancel</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import '../style/reference-params.less';
|
||||
|
||||
const ReferenceParams = () => {
|
||||
return (
|
||||
<div className="reference-params">
|
||||
<span>Inference: 597 ms</span>
|
||||
<span style={{ padding: '10px' }}></span>
|
||||
<span>Tokens/s: 561</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReferenceParams;
|
||||
@@ -0,0 +1,14 @@
|
||||
export const Roles = {
|
||||
User: 'User',
|
||||
Assistant: 'Assistant'
|
||||
};
|
||||
export const playGroundRoles = [
|
||||
{
|
||||
key: 'user',
|
||||
label: 'User'
|
||||
},
|
||||
{
|
||||
key: 'assistant',
|
||||
label: 'Assistant'
|
||||
}
|
||||
];
|
||||
@@ -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<any[]>([]);
|
||||
const [selectedModel, setSelectedModel] = useState('llama3:latest');
|
||||
const [showPopover, setShowPopover] = useState(false);
|
||||
@@ -41,49 +32,17 @@ const Playground: React.FC = () => {
|
||||
}, [selectedModel]);
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
title={false}
|
||||
extra={[
|
||||
<Space size={20} key="space">
|
||||
<Select
|
||||
showSearch
|
||||
options={ModelList}
|
||||
style={{ width: 300 }}
|
||||
value={selectedModel}
|
||||
onChange={handleSelectChange}
|
||||
variant="filled"
|
||||
key="model-select"
|
||||
></Select>
|
||||
<Popover
|
||||
content={
|
||||
<ParamsSettings onClose={handleClosePopover}></ParamsSettings>
|
||||
}
|
||||
destroyTooltipOnHide={true}
|
||||
title="Params Settings"
|
||||
trigger="click"
|
||||
arrow={false}
|
||||
open={showPopover}
|
||||
overlayStyle={{ top: 70 }}
|
||||
overlayInnerStyle={{ paddingRight: 0 }}
|
||||
placement="bottomRight"
|
||||
key="popover"
|
||||
>
|
||||
<Button
|
||||
onClick={handleTogglePopover}
|
||||
type="primary"
|
||||
shape="circle"
|
||||
key="button"
|
||||
icon={<ControlOutlined />}
|
||||
></Button>
|
||||
</Popover>
|
||||
</Space>
|
||||
]}
|
||||
footer={[<MessageInput key="message" />]}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<ChatContent messageList={messageList}></ChatContent>
|
||||
<div className="play-ground">
|
||||
<div className="chat">
|
||||
<GroundLeft></GroundLeft>
|
||||
</div>
|
||||
</PageContainer>
|
||||
<div className="divider-line">
|
||||
<Divider type="vertical" />
|
||||
</div>
|
||||
<div className="params">
|
||||
<ParamsSettings onClose={handleClosePopover} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
.reference-params {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--ant-orange);
|
||||
}
|
||||
Reference in New Issue
Block a user