feat: generate images
This commit is contained in:
@@ -33,6 +33,14 @@ export default [
|
|||||||
icon: 'Comment',
|
icon: 'Comment',
|
||||||
component: './playground/speech'
|
component: './playground/speech'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'text2images',
|
||||||
|
title: 'Text2Images',
|
||||||
|
path: '/playground/text-to-images',
|
||||||
|
key: 'text2images',
|
||||||
|
icon: 'Comment',
|
||||||
|
component: './playground/images'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'embedding',
|
name: 'embedding',
|
||||||
title: 'embedding',
|
title: 'embedding',
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
.toolbar-wrapper {
|
||||||
|
padding: 0 24px;
|
||||||
|
color: rgba(255, 255, 255, 65%);
|
||||||
|
font-size: 16px;
|
||||||
|
background-color: rgba(0, 0, 0, 10%);
|
||||||
|
border-radius: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-wrapper .anticon {
|
||||||
|
padding: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-wrapper .anticon[disabled] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-wrapper .anticon:hover {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
@@ -1,6 +1,17 @@
|
|||||||
import { Image as AntImage, ImageProps } from 'antd';
|
import {
|
||||||
|
DownloadOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
RotateLeftOutlined,
|
||||||
|
RotateRightOutlined,
|
||||||
|
SwapOutlined,
|
||||||
|
UndoOutlined,
|
||||||
|
ZoomInOutlined,
|
||||||
|
ZoomOutOutlined
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { Image as AntImage, ImageProps, Space } from 'antd';
|
||||||
import { round } from 'lodash';
|
import { round } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
const AutoImage: React.FC<ImageProps & { height: number }> = (props) => {
|
const AutoImage: React.FC<ImageProps & { height: number }> = (props) => {
|
||||||
const { height = 100, ...rest } = props;
|
const { height = 100, ...rest } = props;
|
||||||
@@ -24,11 +35,58 @@ const AutoImage: React.FC<ImageProps & { height: number }> = (props) => {
|
|||||||
setWidth(height * ratio);
|
setWidth(height * ratio);
|
||||||
}, [getImgRatio, height, props.src]);
|
}, [getImgRatio, height, props.src]);
|
||||||
|
|
||||||
|
const onDownload = () => {
|
||||||
|
const url = props.src || '';
|
||||||
|
const filename = Date.now() + '';
|
||||||
|
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = filename;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleOnLoad();
|
handleOnLoad();
|
||||||
}, [handleOnLoad]);
|
}, [handleOnLoad]);
|
||||||
|
|
||||||
return <AntImage {...rest} height={height} width={width} />;
|
return (
|
||||||
|
<AntImage
|
||||||
|
{...rest}
|
||||||
|
height={height}
|
||||||
|
width={width}
|
||||||
|
preview={{
|
||||||
|
mask: <EyeOutlined />,
|
||||||
|
toolbarRender: (
|
||||||
|
_,
|
||||||
|
{
|
||||||
|
transform: { scale },
|
||||||
|
actions: {
|
||||||
|
onFlipY,
|
||||||
|
onFlipX,
|
||||||
|
onRotateLeft,
|
||||||
|
onRotateRight,
|
||||||
|
onZoomOut,
|
||||||
|
onZoomIn,
|
||||||
|
onReset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) => (
|
||||||
|
<Space size={12} className="toolbar-wrapper">
|
||||||
|
<DownloadOutlined onClick={onDownload} />
|
||||||
|
<SwapOutlined rotate={90} onClick={onFlipY} />
|
||||||
|
<SwapOutlined onClick={onFlipX} />
|
||||||
|
<RotateLeftOutlined onClick={onRotateLeft} />
|
||||||
|
<RotateRightOutlined onClick={onRotateRight} />
|
||||||
|
<ZoomOutOutlined disabled={scale === 1} onClick={onZoomOut} />
|
||||||
|
<ZoomInOutlined disabled={scale === 50} onClick={onZoomIn} />
|
||||||
|
<UndoOutlined onClick={onReset} />
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AutoImage;
|
export default AutoImage;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export default {
|
|||||||
'menu.playground.embedding': 'Embedding',
|
'menu.playground.embedding': 'Embedding',
|
||||||
'menu.playground.chat': 'Chat',
|
'menu.playground.chat': 'Chat',
|
||||||
'menu.playground.speech': 'Speech',
|
'menu.playground.speech': 'Speech',
|
||||||
|
'menu.playground.text2images': 'Text to Images',
|
||||||
'menu.compare': 'Compare',
|
'menu.compare': 'Compare',
|
||||||
'menu.models': 'Models',
|
'menu.models': 'Models',
|
||||||
'menu.resources': 'Resources',
|
'menu.resources': 'Resources',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export default {
|
|||||||
'menu.playground.embedding': '文本嵌入',
|
'menu.playground.embedding': '文本嵌入',
|
||||||
'menu.playground.chat': '对话',
|
'menu.playground.chat': '对话',
|
||||||
'menu.playground.speech': '语音',
|
'menu.playground.speech': '语音',
|
||||||
|
'menu.playground.text2images': '文生图',
|
||||||
'menu.compare': '多模型对比',
|
'menu.compare': '多模型对比',
|
||||||
'menu.models': '模型',
|
'menu.models': '模型',
|
||||||
'menu.resources': '资源',
|
'menu.resources': '资源',
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { request } from '@umijs/max';
|
|||||||
|
|
||||||
export const CHAT_API = '/v1-openai/chat/completions';
|
export const CHAT_API = '/v1-openai/chat/completions';
|
||||||
|
|
||||||
|
export const CREAT_IMAGE_API = '/v1-openai/images/generations';
|
||||||
|
|
||||||
export const EMBEDDING_API = '/v1-openai/embeddings';
|
export const EMBEDDING_API = '/v1-openai/embeddings';
|
||||||
|
|
||||||
export const OPENAI_MODELS = '/v1-openai/models';
|
export const OPENAI_MODELS = '/v1-openai/models';
|
||||||
@@ -51,3 +53,19 @@ export const handleEmbedding = async (
|
|||||||
cancelToken: options?.cancelToken
|
cancelToken: options?.cancelToken
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createImages = async (
|
||||||
|
params: {
|
||||||
|
model: string;
|
||||||
|
prompt: string;
|
||||||
|
n: number;
|
||||||
|
size: string;
|
||||||
|
},
|
||||||
|
options?: any
|
||||||
|
) => {
|
||||||
|
return request(`${CREAT_IMAGE_API}`, {
|
||||||
|
method: 'POST',
|
||||||
|
data: params,
|
||||||
|
cancelToken: options?.cancelToken
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,310 @@
|
|||||||
|
import useOverlayScroller from '@/hooks/use-overlay-scroller';
|
||||||
|
import useRequestToken from '@/hooks/use-request-token';
|
||||||
|
import { useIntl, useSearchParams } from '@umijs/max';
|
||||||
|
import { Spin } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import 'overlayscrollbars/overlayscrollbars.css';
|
||||||
|
import {
|
||||||
|
forwardRef,
|
||||||
|
memo,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
|
import { createImages } from '../apis';
|
||||||
|
import { Roles, generateMessages } from '../config';
|
||||||
|
import { ImageParamsConfig as paramsConfig } from '../config/params-config';
|
||||||
|
import { MessageItem } from '../config/types';
|
||||||
|
import '../style/ground-left.less';
|
||||||
|
import '../style/system-message-wrap.less';
|
||||||
|
import MessageInput from './message-input';
|
||||||
|
import MessageContent from './multiple-chat/message-content';
|
||||||
|
import ReferenceParams from './reference-params';
|
||||||
|
import RerankerParams from './reranker-params';
|
||||||
|
import ViewCodeModal from './view-code-modal';
|
||||||
|
|
||||||
|
interface MessageProps {
|
||||||
|
modelList: Global.BaseOption<string>[];
|
||||||
|
loaded?: boolean;
|
||||||
|
ref?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
n: 1,
|
||||||
|
size: '512x512',
|
||||||
|
quality: 'standard',
|
||||||
|
style: 'vivid'
|
||||||
|
};
|
||||||
|
|
||||||
|
const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||||
|
const { modelList } = props;
|
||||||
|
const messageId = useRef<number>(0);
|
||||||
|
const [messageList, setMessageList] = useState<MessageItem[]>([]);
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
const requestSource = useRequestToken();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const selectModel = searchParams.get('model') || '';
|
||||||
|
const [parameters, setParams] = useState<any>({});
|
||||||
|
const [systemMessage, setSystemMessage] = useState('');
|
||||||
|
const [show, setShow] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [tokenResult, setTokenResult] = useState<any>(null);
|
||||||
|
const [collapse, setCollapse] = useState(false);
|
||||||
|
const contentRef = useRef<any>('');
|
||||||
|
const scroller = useRef<any>(null);
|
||||||
|
const currentMessageRef = useRef<any>(null);
|
||||||
|
const paramsRef = useRef<any>(null);
|
||||||
|
const messageListLengthCache = useRef<number>(0);
|
||||||
|
const requestToken = useRef<any>(null);
|
||||||
|
|
||||||
|
const { initialize, updateScrollerPosition } = useOverlayScroller();
|
||||||
|
const { initialize: innitializeParams } = useOverlayScroller();
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => {
|
||||||
|
return {
|
||||||
|
viewCode() {
|
||||||
|
setShow(true);
|
||||||
|
},
|
||||||
|
setCollapse() {
|
||||||
|
setCollapse(!collapse);
|
||||||
|
},
|
||||||
|
collapse: collapse
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const viewCodeMessage = useMemo(() => {
|
||||||
|
return generateMessages([
|
||||||
|
{ role: Roles.System, content: systemMessage },
|
||||||
|
...messageList
|
||||||
|
]);
|
||||||
|
}, [messageList, systemMessage]);
|
||||||
|
|
||||||
|
const setMessageId = () => {
|
||||||
|
messageId.current = messageId.current + 1;
|
||||||
|
return messageId.current;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNewMessage = (message?: { role: string; content: string }) => {
|
||||||
|
const newMessage = message || {
|
||||||
|
role:
|
||||||
|
_.last(messageList)?.role === Roles.User ? Roles.Assistant : Roles.User,
|
||||||
|
content: ''
|
||||||
|
};
|
||||||
|
messageList.push({
|
||||||
|
...newMessage,
|
||||||
|
uid: messageId.current + 1
|
||||||
|
});
|
||||||
|
setMessageId();
|
||||||
|
setMessageList([...messageList]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStopConversation = () => {
|
||||||
|
requestToken.current?.cancel?.();
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitMessage = async (current?: { role: string; content: string }) => {
|
||||||
|
if (!parameters.model) return;
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setMessageId();
|
||||||
|
|
||||||
|
requestToken.current?.cancel?.();
|
||||||
|
requestToken.current = requestSource();
|
||||||
|
|
||||||
|
currentMessageRef.current = current
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
...current,
|
||||||
|
uid: messageId.current
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
contentRef.current = '';
|
||||||
|
setMessageList((pre) => {
|
||||||
|
return [...pre, ...currentMessageRef.current];
|
||||||
|
});
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
prompt: current?.content || '',
|
||||||
|
...parameters
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await createImages(params, {
|
||||||
|
cancelToken: requestToken.current.token
|
||||||
|
});
|
||||||
|
|
||||||
|
const imgList = _.map(result.data, (item: any, index: number) => {
|
||||||
|
return {
|
||||||
|
dataUrl: `data:image/png;base64,${item.b64_json}`,
|
||||||
|
created: result.created,
|
||||||
|
uid: index
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setMessageList((pre) => {
|
||||||
|
return [
|
||||||
|
...pre,
|
||||||
|
{
|
||||||
|
content: '',
|
||||||
|
role: Roles.Assistant,
|
||||||
|
imgs: imgList,
|
||||||
|
uid: messageId.current
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
console.log('result:', imgList);
|
||||||
|
|
||||||
|
setMessageId();
|
||||||
|
} catch (error) {
|
||||||
|
// console.log('error:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleClear = () => {
|
||||||
|
if (!messageList.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setMessageId();
|
||||||
|
setMessageList([]);
|
||||||
|
setTokenResult(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSendMessage = (message: Omit<MessageItem, 'uid'>) => {
|
||||||
|
console.log('message:', message);
|
||||||
|
const currentMessage =
|
||||||
|
message.content || message.imgs?.length ? message : undefined;
|
||||||
|
submitMessage(currentMessage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseViewCode = () => {
|
||||||
|
setShow(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectModel = () => {};
|
||||||
|
|
||||||
|
const handlePresetPrompt = (list: { role: string; content: string }[]) => {
|
||||||
|
const sysMsg = list.filter((item) => item.role === 'system');
|
||||||
|
const userMsg = list
|
||||||
|
.filter((item) => item.role === 'user')
|
||||||
|
.map((item) => {
|
||||||
|
setMessageId();
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
uid: messageId.current
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setSystemMessage(sysMsg[0]?.content || '');
|
||||||
|
setMessageList(userMsg);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (scroller.current) {
|
||||||
|
initialize(scroller.current);
|
||||||
|
}
|
||||||
|
}, [scroller.current, initialize]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (paramsRef.current) {
|
||||||
|
innitializeParams(paramsRef.current);
|
||||||
|
}
|
||||||
|
}, [paramsRef.current, innitializeParams]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loading) {
|
||||||
|
updateScrollerPosition();
|
||||||
|
}
|
||||||
|
}, [messageList, loading]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (messageList.length > messageListLengthCache.current) {
|
||||||
|
updateScrollerPosition();
|
||||||
|
}
|
||||||
|
messageListLengthCache.current = messageList.length;
|
||||||
|
}, [messageList.length]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ground-left-wrapper">
|
||||||
|
<div className="ground-left">
|
||||||
|
<div className="message-list-wrap" ref={scroller}>
|
||||||
|
<>
|
||||||
|
<div className="content">
|
||||||
|
<MessageContent
|
||||||
|
spans={{
|
||||||
|
span: 24,
|
||||||
|
count: 1
|
||||||
|
}}
|
||||||
|
actions={[]}
|
||||||
|
messageList={messageList}
|
||||||
|
setMessageList={setMessageList}
|
||||||
|
editable={false}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
{loading && (
|
||||||
|
<Spin size="small">
|
||||||
|
<div style={{ height: '46px' }}></div>
|
||||||
|
</Spin>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
|
{tokenResult && (
|
||||||
|
<div style={{ height: 40 }}>
|
||||||
|
<ReferenceParams usage={tokenResult}></ReferenceParams>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="ground-left-footer">
|
||||||
|
<MessageInput
|
||||||
|
scope="chat"
|
||||||
|
placeholer="Type <kbd>/</kbd> to input prompt"
|
||||||
|
actions={['clear']}
|
||||||
|
loading={loading}
|
||||||
|
disabled={!parameters.model}
|
||||||
|
isEmpty={!messageList.length}
|
||||||
|
handleSubmit={handleSendMessage}
|
||||||
|
addMessage={handleNewMessage}
|
||||||
|
handleAbortFetch={handleStopConversation}
|
||||||
|
clearAll={handleClear}
|
||||||
|
setModelSelections={handleSelectModel}
|
||||||
|
presetPrompt={handlePresetPrompt}
|
||||||
|
modelList={modelList}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames('params-wrapper', {
|
||||||
|
collapsed: collapse
|
||||||
|
})}
|
||||||
|
ref={paramsRef}
|
||||||
|
>
|
||||||
|
<div className="box">
|
||||||
|
<RerankerParams
|
||||||
|
setParams={setParams}
|
||||||
|
paramsConfig={paramsConfig}
|
||||||
|
initialValues={initialValues}
|
||||||
|
params={parameters}
|
||||||
|
selectedModel={selectModel}
|
||||||
|
modelList={modelList}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ViewCodeModal
|
||||||
|
open={show}
|
||||||
|
payLoad={{
|
||||||
|
prompt: currentMessageRef.current?.[0]?.content
|
||||||
|
}}
|
||||||
|
parameters={parameters}
|
||||||
|
onCancel={handleCloseViewCode}
|
||||||
|
title={intl.formatMessage({ id: 'playground.viewcode' })}
|
||||||
|
></ViewCodeModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default memo(GroundImages);
|
||||||
@@ -281,6 +281,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
<div className="ground-left-footer">
|
<div className="ground-left-footer">
|
||||||
<MessageInput
|
<MessageInput
|
||||||
scope="reranker"
|
scope="reranker"
|
||||||
|
actions={[]}
|
||||||
submitIcon={<SearchOutlined className="font-size-16" />}
|
submitIcon={<SearchOutlined className="font-size-16" />}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={!parameters.model}
|
disabled={!parameters.model}
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ interface MessageProps {
|
|||||||
ref?: any;
|
ref?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
voice: 'Alloy',
|
||||||
|
response_format: 'mp3',
|
||||||
|
speed: 1
|
||||||
|
};
|
||||||
|
|
||||||
const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||||
const { modelList } = props;
|
const { modelList } = props;
|
||||||
const messageId = useRef<number>(0);
|
const messageId = useRef<number>(0);
|
||||||
@@ -57,12 +63,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
const { initialize, updateScrollerPosition } = useOverlayScroller();
|
const { initialize, updateScrollerPosition } = useOverlayScroller();
|
||||||
const { initialize: innitializeParams } = useOverlayScroller();
|
const { initialize: innitializeParams } = useOverlayScroller();
|
||||||
|
|
||||||
const initialValues = {
|
|
||||||
voice: 'Alloy',
|
|
||||||
response_format: 'mp3',
|
|
||||||
speed: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => {
|
useImperativeHandle(ref, () => {
|
||||||
return {
|
return {
|
||||||
viewCode() {
|
viewCode() {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import UploadImg from './upload-img';
|
|||||||
|
|
||||||
type CurrentMessage = Omit<MessageItem, 'uid'>;
|
type CurrentMessage = Omit<MessageItem, 'uid'>;
|
||||||
|
|
||||||
|
type ActionType = 'clear' | 'layout' | 'role' | 'upload' | 'add' | 'paste';
|
||||||
|
|
||||||
const layoutOptions = [
|
const layoutOptions = [
|
||||||
{
|
{
|
||||||
label: '2 columns',
|
label: '2 columns',
|
||||||
@@ -77,6 +79,7 @@ interface MessageInputProps {
|
|||||||
placeholer?: string;
|
placeholer?: string;
|
||||||
shouldResetMessage?: boolean;
|
shouldResetMessage?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
actions?: ActionType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const MessageInput: React.FC<MessageInputProps> = ({
|
const MessageInput: React.FC<MessageInputProps> = ({
|
||||||
@@ -97,7 +100,8 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
|||||||
placeholer,
|
placeholer,
|
||||||
tools,
|
tools,
|
||||||
style,
|
style,
|
||||||
shouldResetMessage = true
|
shouldResetMessage = true,
|
||||||
|
actions = ['clear', 'layout', 'role', 'upload', 'add', 'paste']
|
||||||
}) => {
|
}) => {
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -256,25 +260,8 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOnPaste = (e: any) => {
|
const handleOnPaste = (e: any) => {
|
||||||
// e.preventDefault();
|
|
||||||
|
|
||||||
const text = e.clipboardData.getData('text');
|
const text = e.clipboardData.getData('text');
|
||||||
if (text) {
|
if (!text) {
|
||||||
// const startPos = e.target.selectionStart;
|
|
||||||
// const endPos = e.target.selectionEnd;
|
|
||||||
// setMessage?.({
|
|
||||||
// ...message,
|
|
||||||
// content:
|
|
||||||
// message.content.slice(0, startPos) +
|
|
||||||
// text +
|
|
||||||
// message.content.slice(endPos)
|
|
||||||
// });
|
|
||||||
// if (endPos !== startPos) {
|
|
||||||
// setTimeout(() => {
|
|
||||||
// e.target.setSelectionRange(endPos, endPos);
|
|
||||||
// }, 0);
|
|
||||||
// }
|
|
||||||
} else {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
getPasteContent(e);
|
getPasteContent(e);
|
||||||
}
|
}
|
||||||
@@ -352,26 +339,30 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
|||||||
<div className="tool-bar">
|
<div className="tool-bar">
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
{tools}
|
{tools}
|
||||||
{scope !== 'reranker' && (
|
{
|
||||||
<>
|
<>
|
||||||
<Button
|
{actions.includes('role') && (
|
||||||
type="text"
|
<>
|
||||||
size="middle"
|
<Button
|
||||||
onClick={handleToggleRole}
|
type="text"
|
||||||
icon={<SwapOutlined rotate={90} />}
|
size="middle"
|
||||||
>
|
onClick={handleToggleRole}
|
||||||
{intl.formatMessage({ id: `playground.${message.role}` })}
|
icon={<SwapOutlined rotate={90} />}
|
||||||
</Button>
|
>
|
||||||
<Divider type="vertical" style={{ margin: 0 }} />
|
{intl.formatMessage({ id: `playground.${message.role}` })}
|
||||||
{message.role === Roles.User && (
|
</Button>
|
||||||
|
<Divider type="vertical" style={{ margin: 0 }} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{actions.includes('upload') && message.role === Roles.User && (
|
||||||
<UploadImg
|
<UploadImg
|
||||||
handleUpdateImgList={handleUpdateImgList}
|
handleUpdateImgList={handleUpdateImgList}
|
||||||
size="middle"
|
size="middle"
|
||||||
></UploadImg>
|
></UploadImg>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
}
|
||||||
{scope !== 'reranker' && (
|
{actions.includes('clear') && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={intl.formatMessage({ id: 'playground.toolbar.clearmsg' })}
|
title={intl.formatMessage({ id: 'playground.toolbar.clearmsg' })}
|
||||||
>
|
>
|
||||||
@@ -383,7 +374,7 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
|||||||
></Button>
|
></Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{updateLayout && (
|
{actions.includes('layout') && updateLayout && (
|
||||||
<>
|
<>
|
||||||
<Divider type="vertical" style={{ margin: 0 }} />
|
<Divider type="vertical" style={{ margin: 0 }} />
|
||||||
{layoutOptions.map((option) => (
|
{layoutOptions.map((option) => (
|
||||||
@@ -418,7 +409,7 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
|||||||
></Select>
|
></Select>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{scope !== 'reranker' && (
|
{actions.includes('add') && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<span>
|
<span>
|
||||||
@@ -471,7 +462,7 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
|||||||
onDelete={handleDeleteImg}
|
onDelete={handleDeleteImg}
|
||||||
></ThumbImg>
|
></ThumbImg>
|
||||||
<div className="input-box">
|
<div className="input-box">
|
||||||
{scope !== 'reranker' ? (
|
{actions.includes('paste') ? (
|
||||||
<TextArea
|
<TextArea
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
autoSize={{ minRows: 3, maxRows: 8 }}
|
autoSize={{ minRows: 3, maxRows: 8 }}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import classNames from 'classnames';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback, useRef } from 'react';
|
import React, { useCallback, useRef } from 'react';
|
||||||
import { Roles } from '../../config';
|
import { Roles } from '../../config';
|
||||||
import { MessageItem } from '../../config/types';
|
import { MessageItem, MessageItemAction } from '../../config/types';
|
||||||
import '../../style/content-item.less';
|
import '../../style/content-item.less';
|
||||||
import ThumbImg from '../thumb-img';
|
import ThumbImg from '../thumb-img';
|
||||||
import UploadImg from '../upload-img';
|
import UploadImg from '../upload-img';
|
||||||
@@ -15,7 +15,7 @@ interface MessageItemProps {
|
|||||||
data: MessageItem;
|
data: MessageItem;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
actions?: string[];
|
actions?: MessageItemAction[];
|
||||||
updateMessage?: (message: MessageItem) => void;
|
updateMessage?: (message: MessageItem) => void;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
}
|
}
|
||||||
@@ -105,23 +105,8 @@ const ContentItem: React.FC<MessageItemProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleOnPaste = (e: any) => {
|
const handleOnPaste = (e: any) => {
|
||||||
// e.preventDefault();
|
|
||||||
const text = e.clipboardData.getData('text');
|
const text = e.clipboardData.getData('text');
|
||||||
if (text) {
|
if (!text) {
|
||||||
// const startPos = e.target.selectionStart;
|
|
||||||
// const endPos = e.target.selectionEnd;
|
|
||||||
// updateMessage?.({
|
|
||||||
// role: data.role,
|
|
||||||
// content:
|
|
||||||
// data.content.slice(0, startPos) + text + data.content.slice(endPos),
|
|
||||||
// uid: data.uid
|
|
||||||
// });
|
|
||||||
// if (endPos !== startPos) {
|
|
||||||
// setTimeout(() => {
|
|
||||||
// e.target.setSelectionRange(endPos, endPos);
|
|
||||||
// }, 0);
|
|
||||||
// }
|
|
||||||
} else {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
getPasteContent(e);
|
getPasteContent(e);
|
||||||
}
|
}
|
||||||
@@ -221,6 +206,7 @@ const ContentItem: React.FC<MessageItemProps> = ({
|
|||||||
onClick={handleClickWrapper}
|
onClick={handleClickWrapper}
|
||||||
>
|
>
|
||||||
<ThumbImg
|
<ThumbImg
|
||||||
|
editable={editable}
|
||||||
dataList={data.imgs || []}
|
dataList={data.imgs || []}
|
||||||
onDelete={handleDeleteImg}
|
onDelete={handleDeleteImg}
|
||||||
></ThumbImg>
|
></ThumbImg>
|
||||||
@@ -239,7 +225,18 @@ const ContentItem: React.FC<MessageItemProps> = ({
|
|||||||
></Input.TextArea>
|
></Input.TextArea>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="content-item-content">{data.content}</div>
|
<div
|
||||||
|
className={classNames('content-item-content', {
|
||||||
|
'has-img': data.imgs?.length
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<ThumbImg
|
||||||
|
editable={editable}
|
||||||
|
dataList={data.imgs || []}
|
||||||
|
onDelete={handleDeleteImg}
|
||||||
|
></ThumbImg>
|
||||||
|
{data.content && <div className="text">{data.content}</div>}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import 'simplebar-react/dist/simplebar.min.css';
|
import 'simplebar-react/dist/simplebar.min.css';
|
||||||
import { MessageItem } from '../../config/types';
|
import { MessageItem, MessageItemAction } from '../../config/types';
|
||||||
import ContentItem from './content-item';
|
import ContentItem from './content-item';
|
||||||
|
|
||||||
interface MessageContentProps {
|
interface MessageContentProps {
|
||||||
@@ -9,7 +9,7 @@ interface MessageContentProps {
|
|||||||
span: number;
|
span: number;
|
||||||
count: number;
|
count: number;
|
||||||
};
|
};
|
||||||
actions?: string[];
|
actions?: MessageItemAction[];
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
messageList: MessageItem[];
|
messageList: MessageItem[];
|
||||||
setMessageList?: (list: any) => void;
|
setMessageList?: (list: any) => void;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { InfoCircleOutlined } from '@ant-design/icons';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form, InputNumber, Slider, Tooltip } from 'antd';
|
import { Form, InputNumber, Slider, Tooltip } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { memo, useCallback, useEffect, useId } from 'react';
|
import { memo, useCallback, useEffect, useId, useMemo } from 'react';
|
||||||
import { ParamsSchema } from '../config/types';
|
import { ParamsSchema } from '../config/types';
|
||||||
import CustomLabelStyles from '../style/custom-label.less';
|
import CustomLabelStyles from '../style/custom-label.less';
|
||||||
|
|
||||||
@@ -105,42 +105,41 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
|||||||
form.setFieldsValue(globalParams);
|
form.setFieldsValue(globalParams);
|
||||||
}, [globalParams]);
|
}, [globalParams]);
|
||||||
|
|
||||||
const renderLabel = (args: {
|
const renderLabel = useCallback(
|
||||||
field: string;
|
(args: { field: string; label: string; description: string }) => {
|
||||||
label: string;
|
return (
|
||||||
description: string;
|
<span
|
||||||
}) => {
|
className={CustomLabelStyles.label}
|
||||||
return (
|
style={{ width: INPUT_WIDTH.mini }}
|
||||||
<span
|
>
|
||||||
className={CustomLabelStyles.label}
|
<span className="text">
|
||||||
style={{ width: INPUT_WIDTH.mini }}
|
{args.description ? (
|
||||||
>
|
<Tooltip title={args.description}>
|
||||||
<span className="text">
|
<span> {args.label}</span>
|
||||||
{args.description ? (
|
<span className="m-l-5">
|
||||||
<Tooltip title={args.description}>
|
<InfoCircleOutlined />
|
||||||
<span> {args.label}</span>
|
</span>
|
||||||
<span className="m-l-5">
|
</Tooltip>
|
||||||
<InfoCircleOutlined />
|
) : (
|
||||||
</span>
|
<span>{args.label}</span>
|
||||||
</Tooltip>
|
)}
|
||||||
) : (
|
</span>
|
||||||
<span>{args.label}</span>
|
|
||||||
)}
|
<InputNumber
|
||||||
|
className="label-val"
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
value={form.getFieldValue(args.field)}
|
||||||
|
controls={false}
|
||||||
|
onChange={(val) => handleFieldValueChange(val, args.field)}
|
||||||
|
></InputNumber>
|
||||||
</span>
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[form, handleFieldValueChange]
|
||||||
|
);
|
||||||
|
|
||||||
<InputNumber
|
const renderFields = useMemo(() => {
|
||||||
className="label-val"
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
value={form.getFieldValue(args.field)}
|
|
||||||
controls={false}
|
|
||||||
onChange={(val) => handleFieldValueChange(val, args.field)}
|
|
||||||
></InputNumber>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderFields = useCallback(() => {
|
|
||||||
console.log('paramsConfig:', paramsConfig);
|
console.log('paramsConfig:', paramsConfig);
|
||||||
if (!paramsConfig?.length) {
|
if (!paramsConfig?.length) {
|
||||||
return null;
|
return null;
|
||||||
@@ -243,7 +242,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{renderFields()}
|
{renderFields}
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import AutoImage from '@/components/auto-image';
|
import AutoImage from '@/components/auto-image';
|
||||||
import { CloseCircleOutlined, EyeOutlined } from '@ant-design/icons';
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import '../style/thumb-img.less';
|
import '../style/thumb-img.less';
|
||||||
|
|
||||||
const ThumbImg: React.FC<{
|
const ThumbImg: React.FC<{
|
||||||
dataList: any[];
|
dataList: any[];
|
||||||
|
editable?: boolean;
|
||||||
onDelete: (uid: number) => void;
|
onDelete: (uid: number) => void;
|
||||||
}> = ({ dataList, onDelete }) => {
|
}> = ({ dataList, editable, onDelete }) => {
|
||||||
const handleOnDelete = useCallback(
|
const handleOnDelete = useCallback(
|
||||||
(uid: number) => {
|
(uid: number) => {
|
||||||
onDelete(uid);
|
onDelete(uid);
|
||||||
@@ -32,18 +33,14 @@ const ThumbImg: React.FC<{
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="img">
|
<span className="img">
|
||||||
<AutoImage
|
<AutoImage src={item.dataUrl} height={100} />
|
||||||
src={item.dataUrl}
|
|
||||||
height={100}
|
|
||||||
preview={{
|
|
||||||
mask: <EyeOutlined />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="del" onClick={() => handleOnDelete(item.uid)}>
|
{editable && (
|
||||||
<CloseCircleOutlined />
|
<span className="del" onClick={() => handleOnDelete(item.uid)}>
|
||||||
</span>
|
<CloseCircleOutlined />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -65,3 +65,77 @@ export const TTSParamsConfig: ParamsSchema[] = [
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const ImageParamsConfig: ParamsSchema[] = [
|
||||||
|
{
|
||||||
|
type: 'InputNumber',
|
||||||
|
name: 'n',
|
||||||
|
label: {
|
||||||
|
text: 'Counts',
|
||||||
|
isLocalized: false
|
||||||
|
},
|
||||||
|
attrs: {
|
||||||
|
min: 1,
|
||||||
|
max: 10
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'Select',
|
||||||
|
name: 'size',
|
||||||
|
options: [
|
||||||
|
{ label: '256x256', value: '256x256' },
|
||||||
|
{ label: '512x512', value: '512x512' },
|
||||||
|
{ label: '1024x1024', value: '1024x1024' },
|
||||||
|
{ label: '1792x1024', value: '1792x1024' },
|
||||||
|
{ label: '1024x1792', value: '1024x1792' }
|
||||||
|
],
|
||||||
|
label: {
|
||||||
|
text: 'Size',
|
||||||
|
isLocalized: false
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'Select',
|
||||||
|
name: 'quality',
|
||||||
|
options: [
|
||||||
|
{ label: 'standard', value: 'standard' },
|
||||||
|
{ label: 'hd', value: 'hd' }
|
||||||
|
],
|
||||||
|
label: {
|
||||||
|
text: 'Quality',
|
||||||
|
isLocalized: false
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'Select',
|
||||||
|
name: 'style',
|
||||||
|
options: [
|
||||||
|
{ label: 'vivid', value: 'vivid' },
|
||||||
|
{ label: 'natural', value: 'natural' }
|
||||||
|
],
|
||||||
|
label: {
|
||||||
|
text: 'Style',
|
||||||
|
isLocalized: false
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export interface ModelSelectionItem extends Global.BaseOption<string> {
|
|||||||
instanceId: symbol;
|
instanceId: symbol;
|
||||||
type?: string;
|
type?: string;
|
||||||
}
|
}
|
||||||
|
export type MessageItemAction = 'upload' | 'delete' | 'copy';
|
||||||
export interface MessageItem {
|
export interface MessageItem {
|
||||||
content: string;
|
content: string;
|
||||||
imgs?: { uid: string | number; dataUrl: string }[];
|
imgs?: { uid: string | number; dataUrl: string }[];
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
import IconFont from '@/components/icon-font';
|
||||||
|
import breakpoints from '@/config/breakpoints';
|
||||||
|
import HotKeys from '@/config/hotkeys';
|
||||||
|
import useWindowResize from '@/hooks/use-window-resize';
|
||||||
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Button, Space } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
import { queryModelsList } from './apis';
|
||||||
|
import GroundImages from './components/ground-images';
|
||||||
|
import './style/play-ground.less';
|
||||||
|
|
||||||
|
const TextToImages: React.FC = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { size } = useWindowResize();
|
||||||
|
const groundTabRef1 = useRef<any>(null);
|
||||||
|
const [modelList, setModelList] = useState<Global.BaseOption<string>[]>([]);
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
|
const handleViewCode = useCallback(() => {
|
||||||
|
groundTabRef1.current?.viewCode?.();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleToggleCollapse = useCallback(() => {
|
||||||
|
groundTabRef1.current?.setCollapse?.();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (size.width < breakpoints.lg) {
|
||||||
|
if (!groundTabRef1.current?.collapse) {
|
||||||
|
groundTabRef1.current?.setCollapse?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [size.width]);
|
||||||
|
|
||||||
|
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<string>[];
|
||||||
|
return list;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
const modelist = await getModelList();
|
||||||
|
setModelList(modelist);
|
||||||
|
} catch (error) {
|
||||||
|
setLoaded(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderExtra = () => {
|
||||||
|
return (
|
||||||
|
<Space key="buttons">
|
||||||
|
<Button
|
||||||
|
size="middle"
|
||||||
|
onClick={handleViewCode}
|
||||||
|
icon={<IconFont type="icon-code" className="font-size-16"></IconFont>}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'playground.viewcode' })}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="middle"
|
||||||
|
onClick={handleToggleCollapse}
|
||||||
|
icon={
|
||||||
|
<IconFont
|
||||||
|
type="icon-a-layout6-line"
|
||||||
|
className="font-size-16"
|
||||||
|
></IconFont>
|
||||||
|
}
|
||||||
|
></Button>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useHotkeys(
|
||||||
|
HotKeys.RIGHT.join(','),
|
||||||
|
() => {
|
||||||
|
groundTabRef1.current?.setCollapse?.();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preventDefault: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer
|
||||||
|
ghost
|
||||||
|
header={{
|
||||||
|
title: intl.formatMessage({ id: 'menu.playground.text2images' }),
|
||||||
|
breadcrumb: {}
|
||||||
|
}}
|
||||||
|
extra={renderExtra()}
|
||||||
|
className={classNames('playground-container chat')}
|
||||||
|
>
|
||||||
|
<div className="play-ground">
|
||||||
|
<div className="chat">
|
||||||
|
<GroundImages
|
||||||
|
ref={groundTabRef1}
|
||||||
|
modelList={modelList}
|
||||||
|
></GroundImages>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextToImages;
|
||||||
@@ -31,10 +31,19 @@
|
|||||||
|
|
||||||
&-content {
|
&-content {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
padding: 8px 14px;
|
|
||||||
min-height: 38px;
|
min-height: 38px;
|
||||||
border-radius: var(--border-radius-mini);
|
border-radius: var(--border-radius-mini);
|
||||||
background-color: var(--ant-color-fill-tertiary);
|
background-color: var(--ant-color-fill-tertiary);
|
||||||
|
|
||||||
|
.text {
|
||||||
|
padding: 8px 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-img {
|
||||||
|
border: 1px solid var(--ant-color-fill-secondary);
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content-input {
|
.message-content-input {
|
||||||
|
|||||||
Reference in New Issue
Block a user