feat: playground add submenu
This commit is contained in:
@@ -24,7 +24,7 @@ import ReferenceParams from './reference-params';
|
||||
import RerankMessage from './rerank-message';
|
||||
import RerankerParams from './reranker-params';
|
||||
import UploadFile from './upload-file';
|
||||
import ViewCodeModal from './view-code-modal';
|
||||
import ViewRerankCode from './view-rerank-code';
|
||||
|
||||
interface MessageProps {
|
||||
modelList: Global.BaseOption<string>[];
|
||||
@@ -34,6 +34,8 @@ interface MessageProps {
|
||||
|
||||
const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
const { modelList } = props;
|
||||
const acceptType =
|
||||
'.txt, .doc, .docx, .xls, .xlsx, .csv, .md, .pdf, .eml, .msg, .ppt, .pptx, .xml, .epub, .html';
|
||||
const messageId = useRef<number>(0);
|
||||
const [messageList, setMessageList] = useState<MessageItem[]>([]);
|
||||
|
||||
@@ -42,7 +44,6 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
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);
|
||||
@@ -63,7 +64,10 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
>([]);
|
||||
|
||||
const { initialize, updateScrollerPosition } = useOverlayScroller();
|
||||
const { initialize: innitializeParams } = useOverlayScroller();
|
||||
const {
|
||||
initialize: innitializeParams,
|
||||
updateScrollerPosition: updateDocumentScrollerPosition
|
||||
} = useOverlayScroller();
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
@@ -95,32 +99,15 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
requestToken.current?.cancel?.();
|
||||
requestToken.current = requestSource();
|
||||
|
||||
controllerRef.current?.abort?.();
|
||||
controllerRef.current = new AbortController();
|
||||
const signal = controllerRef.current.signal;
|
||||
|
||||
currentMessageRef.current = current
|
||||
? [
|
||||
{
|
||||
content: current.content,
|
||||
title: 'Query',
|
||||
uid: messageId.current
|
||||
}
|
||||
]
|
||||
: [];
|
||||
|
||||
contentRef.current = '';
|
||||
setMessageList((pre) => {
|
||||
return [...currentMessageRef.current];
|
||||
});
|
||||
contentRef.current = current?.content || '';
|
||||
|
||||
const documentList: any[] = [...textList, ...fileList];
|
||||
console.log('documentList:', documentList);
|
||||
|
||||
const result: any = await rerankerQuery(
|
||||
{
|
||||
model: parameters.model,
|
||||
top_n: parameters.top_n,
|
||||
query: current?.content || '',
|
||||
query: contentRef.current,
|
||||
documents: [
|
||||
...textList.map((item) => item.text),
|
||||
...fileList.map((item) => item.text)
|
||||
@@ -135,9 +122,9 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
setMessageId();
|
||||
setTokenResult(result.usage);
|
||||
setMessageList([
|
||||
...currentMessageRef.current,
|
||||
{
|
||||
title: 'Results',
|
||||
role: '',
|
||||
content: result.results?.map((item: any) => {
|
||||
return {
|
||||
uid: item.index,
|
||||
@@ -169,9 +156,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
};
|
||||
|
||||
const handleSendMessage = (message: Omit<MessageItem, 'uid'>) => {
|
||||
const currentMessage =
|
||||
message.content || message.imgs?.length ? message : undefined;
|
||||
submitMessage(currentMessage);
|
||||
submitMessage(message);
|
||||
};
|
||||
|
||||
const handleCloseViewCode = () => {
|
||||
@@ -223,17 +208,15 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
}, [paramsRef.current, innitializeParams]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
updateScrollerPosition();
|
||||
}
|
||||
}, [messageList, loading]);
|
||||
updateScrollerPosition();
|
||||
}, [messageList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (messageList.length > messageListLengthCache.current) {
|
||||
updateScrollerPosition();
|
||||
if (textList.length + fileList.length > messageListLengthCache.current) {
|
||||
updateDocumentScrollerPosition();
|
||||
}
|
||||
messageListLengthCache.current = messageList.length;
|
||||
}, [messageList.length]);
|
||||
messageListLengthCache.current = textList.length + fileList.length;
|
||||
}, [textList.length, fileList.length]);
|
||||
|
||||
return (
|
||||
<div className="ground-left-wrapper">
|
||||
@@ -241,7 +224,18 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
<div className="message-list-wrap" ref={scroller}>
|
||||
<>
|
||||
<div className="content">
|
||||
<RerankMessage dataList={messageList} />
|
||||
<RerankMessage
|
||||
dataList={messageList}
|
||||
header={
|
||||
<div className="result-header">
|
||||
<span className="title">Results</span>
|
||||
<ReferenceParams
|
||||
usage={tokenResult}
|
||||
showOutput={false}
|
||||
></ReferenceParams>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
{loading && (
|
||||
<Spin size="small">
|
||||
<div style={{ height: '46px' }}></div>
|
||||
@@ -250,24 +244,36 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
{tokenResult && (
|
||||
<div style={{ height: 40 }}>
|
||||
<ReferenceParams
|
||||
usage={tokenResult}
|
||||
showOutput={false}
|
||||
></ReferenceParams>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
height: 70,
|
||||
paddingLeft: 1
|
||||
}}
|
||||
>
|
||||
<UploadFile
|
||||
handleUpdateFileList={handleUpdateFileList}
|
||||
accept={acceptType}
|
||||
>
|
||||
<div style={{ backgroundColor: 'var(--color-fill-sider)' }}>
|
||||
<InboxOutlined className="font-size-16" />
|
||||
<span className="m-l-10">
|
||||
Click or drag file to this area to upload
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-tertiary">support {acceptType}</span>
|
||||
</UploadFile>
|
||||
</div>
|
||||
<div className="ground-left-footer">
|
||||
<MessageInput
|
||||
scope="reranker"
|
||||
loading={loading}
|
||||
disabled={!parameters.model}
|
||||
isEmpty={true}
|
||||
shouldResetMessage={false}
|
||||
handleSubmit={handleSendMessage}
|
||||
handleAbortFetch={handleStopConversation}
|
||||
clearAll={handleClear}
|
||||
modelList={modelList}
|
||||
modelList={[]}
|
||||
placeholer={intl.formatMessage({
|
||||
id: 'playground.input.keyword.holder'
|
||||
})}
|
||||
@@ -279,75 +285,62 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
collapsed: collapse
|
||||
})}
|
||||
style={{
|
||||
paddingBottom: 80
|
||||
overflow: 'hidden',
|
||||
paddingBottom: 16
|
||||
}}
|
||||
ref={paramsRef}
|
||||
>
|
||||
<div className="box" style={{ padding: collapse ? 0 : '0 16px' }}>
|
||||
<RerankerParams
|
||||
setParams={setParams}
|
||||
params={parameters}
|
||||
selectedModel={selectModel}
|
||||
modelList={modelList}
|
||||
/>
|
||||
<h3 className="m-b-20 m-l-10 flex-between flex-center font-size-14">
|
||||
<span>Documents</span>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ClearOutlined />}
|
||||
size="middle"
|
||||
onClick={handleClearDocuments}
|
||||
></Button>
|
||||
</h3>
|
||||
<InputList
|
||||
textList={textList}
|
||||
onChange={handleTextListChange}
|
||||
></InputList>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<FileList
|
||||
fileList={fileList}
|
||||
textListCount={textList.length || 0}
|
||||
onDelete={handleDeleteFile}
|
||||
></FileList>
|
||||
<div className="box" style={{ paddingInline: 0, height: '100%' }}>
|
||||
<div style={{ padding: collapse ? 0 : '0 16px' }}>
|
||||
<RerankerParams
|
||||
setParams={setParams}
|
||||
params={parameters}
|
||||
selectedModel={selectModel}
|
||||
modelList={modelList}
|
||||
/>
|
||||
<h3 className="m-b-20 m-l-10 flex-between flex-center font-size-14">
|
||||
<span>Documents</span>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ClearOutlined />}
|
||||
size="middle"
|
||||
onClick={handleClearDocuments}
|
||||
></Button>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
height: collapse ? 0 : 60,
|
||||
padding: '0 10px',
|
||||
position: 'absolute',
|
||||
overflow: 'hidden',
|
||||
bottom: 20,
|
||||
left: 0,
|
||||
right: 0
|
||||
}}
|
||||
>
|
||||
<UploadFile
|
||||
handleUpdateFileList={handleUpdateFileList}
|
||||
accept=".txt, .doc, .docx, .xls, .xlsx"
|
||||
<div
|
||||
className="docs-wrapper"
|
||||
ref={paramsRef}
|
||||
style={{
|
||||
height: 'calc(100% - 210px)',
|
||||
padding: '0 16px',
|
||||
overflowY: 'auto'
|
||||
}}
|
||||
>
|
||||
<div style={{ backgroundColor: 'var(--color-fill-sider)' }}>
|
||||
<InboxOutlined className="font-size-16" />
|
||||
<span className="m-l-10">
|
||||
Click or drag file to this area to upload
|
||||
</span>
|
||||
<InputList
|
||||
textList={textList}
|
||||
onChange={handleTextListChange}
|
||||
></InputList>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<FileList
|
||||
fileList={fileList}
|
||||
textListCount={textList.length || 0}
|
||||
onDelete={handleDeleteFile}
|
||||
></FileList>
|
||||
</div>
|
||||
<span className="text-tertiary">
|
||||
support .txt, .doc, .docx, .xls, .xlsx
|
||||
</span>
|
||||
</UploadFile>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ViewCodeModal
|
||||
<ViewRerankCode
|
||||
open={show}
|
||||
systemMessage={systemMessage}
|
||||
messageList={messageList}
|
||||
parameters={parameters}
|
||||
documentList={[...textList, ...fileList].map((item) => item.text)}
|
||||
parameters={{
|
||||
...parameters,
|
||||
query: contentRef.current
|
||||
}}
|
||||
onCancel={handleCloseViewCode}
|
||||
title={intl.formatMessage({ id: 'playground.viewcode' })}
|
||||
></ViewCodeModal>
|
||||
></ViewRerankCode>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -73,6 +73,7 @@ interface MessageInputProps {
|
||||
isEmpty?: boolean;
|
||||
scope: string;
|
||||
placeholer?: string;
|
||||
shouldResetMessage?: boolean;
|
||||
}
|
||||
|
||||
const MessageInput: React.FC<MessageInputProps> = ({
|
||||
@@ -89,7 +90,8 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
||||
disabled,
|
||||
isEmpty,
|
||||
scope,
|
||||
placeholer
|
||||
placeholer,
|
||||
shouldResetMessage = true
|
||||
}) => {
|
||||
const { TextArea } = Input;
|
||||
const intl = useIntl();
|
||||
@@ -126,7 +128,9 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
||||
};
|
||||
const handleSendMessage = () => {
|
||||
handleSubmit({ ...message });
|
||||
resetMessage();
|
||||
if (shouldResetMessage) {
|
||||
resetMessage();
|
||||
}
|
||||
};
|
||||
const onStop = () => {
|
||||
handleAbortFetch();
|
||||
@@ -360,17 +364,18 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Tooltip
|
||||
title={intl.formatMessage({ id: 'playground.toolbar.clearmsg' })}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ClearOutlined />}
|
||||
size="middle"
|
||||
disabled={loading}
|
||||
onClick={handleClearAll}
|
||||
></Button>
|
||||
</Tooltip>
|
||||
{scope !== 'reranker' && (
|
||||
<Tooltip
|
||||
title={intl.formatMessage({ id: 'playground.toolbar.clearmsg' })}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ClearOutlined />}
|
||||
size="middle"
|
||||
onClick={handleClearAll}
|
||||
></Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{updateLayout && (
|
||||
<>
|
||||
<Divider type="vertical" style={{ margin: 0 }} />
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { StarFilled } from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
@@ -6,17 +5,22 @@ import '../style/content-item.less';
|
||||
import '../style/rerank-message.less';
|
||||
|
||||
interface RerankMessageProps {
|
||||
header?: React.ReactNode;
|
||||
dataList: { title?: string; content: any; uid: number | string }[];
|
||||
}
|
||||
const RerankMessage: React.FC<RerankMessageProps> = ({ dataList }) => {
|
||||
const RerankMessage: React.FC<RerankMessageProps> = ({ header, dataList }) => {
|
||||
if (!dataList || dataList.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="rerank-message">
|
||||
{header}
|
||||
{dataList.map((item) => {
|
||||
return (
|
||||
<div className="content-item" key={item.uid}>
|
||||
<div className="content-item-role">
|
||||
{/* <div className="content-item-role">
|
||||
<span className="role">{item.title}</span>
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="content-item-content">
|
||||
{Array.isArray(item.content) ? (
|
||||
<div className="result">
|
||||
@@ -24,20 +28,15 @@ const RerankMessage: React.FC<RerankMessageProps> = ({ dataList }) => {
|
||||
return (
|
||||
<dl className="content-item-text" key={sItem.uid}>
|
||||
<dt className="rank">
|
||||
<span>[{sItem.docIndex + 1}]</span>
|
||||
<span className="score">
|
||||
<Tooltip
|
||||
title={
|
||||
<span>Score: {_.round(sItem.score, 2)}</span>
|
||||
}
|
||||
>
|
||||
<StarFilled className="m-r-5" />
|
||||
{_.round(sItem.score, 2)}
|
||||
</Tooltip>
|
||||
<span className="doc-index">
|
||||
{sItem.docIndex + 1}
|
||||
</span>
|
||||
</dt>
|
||||
<dd className="text">{sItem.text}</dd>
|
||||
{/* <dd className="doc-name">《{sItem.title}》</dd> */}
|
||||
<Tooltip
|
||||
title={<span>Score: {_.round(sItem.score, 2)}</span>}
|
||||
>
|
||||
<dd className="text">{sItem.text}</dd>
|
||||
</Tooltip>
|
||||
</dl>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { readBlob, readExcelContent, readWordContent } from '@/utils';
|
||||
import { readBlob } from '@/utils';
|
||||
import readEpubContent from '@/utils/epub-reader';
|
||||
import readExcelContent from '@/utils/excel-reader';
|
||||
import readPDFContent from '@/utils/pdf-reader';
|
||||
import readPptxContent from '@/utils/pptx-reader';
|
||||
import readHtmlContent from '@/utils/read-html';
|
||||
import readWordContent from '@/utils/word-reader';
|
||||
import { PaperClipOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Tooltip, Upload } from 'antd';
|
||||
@@ -26,16 +32,11 @@ const UploadImg: React.FC<UploadImgProps> = ({
|
||||
const uploadRef = useRef<any>(null);
|
||||
|
||||
const wordReg = /\.(doc|docx)$/;
|
||||
const pptReg = /\.(ppt|pptx)$/;
|
||||
const pdfReg = /\.(pdf)$/;
|
||||
const epubReg = /\.(epub)$/;
|
||||
const excelReg = /\.(xls|xlsx)$/;
|
||||
|
||||
const getBase64 = useCallback((file: RcFile): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result as string);
|
||||
reader.onerror = (error) => reject(error);
|
||||
});
|
||||
}, []);
|
||||
const htmlReg = /\.(html)$/;
|
||||
|
||||
const debouncedUpdate = useCallback(
|
||||
debounce(
|
||||
@@ -58,12 +59,33 @@ const UploadImg: React.FC<UploadImgProps> = ({
|
||||
const context = await readWordContent(
|
||||
item.originFileObj as RcFile
|
||||
);
|
||||
|
||||
item.url = context;
|
||||
} else if (excelReg.test(item.name)) {
|
||||
const context = await readExcelContent(
|
||||
item.originFileObj as RcFile
|
||||
);
|
||||
item.url = context;
|
||||
} else if (epubReg.test(item.name)) {
|
||||
const context = await readEpubContent(
|
||||
item.originFileObj as RcFile
|
||||
);
|
||||
item.url = context;
|
||||
} else if (pdfReg.test(item.name)) {
|
||||
const context = await readPDFContent(
|
||||
item.originFileObj as RcFile
|
||||
);
|
||||
item.url = context;
|
||||
} else if (pptReg.test(item.name)) {
|
||||
const context = await readPptxContent(
|
||||
item.originFileObj as RcFile
|
||||
);
|
||||
item.url = context;
|
||||
} else if (htmlReg.test(item.name)) {
|
||||
const context = await readHtmlContent(
|
||||
item.originFileObj as RcFile
|
||||
);
|
||||
item.url = context;
|
||||
} else {
|
||||
const context = await readBlob(item.originFileObj as RcFile);
|
||||
item.url = context;
|
||||
@@ -86,10 +108,10 @@ const UploadImg: React.FC<UploadImgProps> = ({
|
||||
debouncedUpdate(files);
|
||||
}
|
||||
} catch (error) {
|
||||
// console.log('error', error);
|
||||
console.log('error', error);
|
||||
}
|
||||
},
|
||||
[debouncedUpdate, getBase64]
|
||||
[debouncedUpdate]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
import EditorWrap from '@/components/editor-wrap';
|
||||
import HighlightCode from '@/components/highlight-code';
|
||||
import { BulbOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Modal } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
type ViewModalProps = {
|
||||
documentList: string[];
|
||||
parameters: any;
|
||||
title: string;
|
||||
open: boolean;
|
||||
apiType?: string;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const langMap = {
|
||||
shell: 'bash',
|
||||
python: 'python',
|
||||
javascript: 'javascript'
|
||||
};
|
||||
|
||||
const langOptions = [
|
||||
{ label: 'Curl', value: langMap.shell },
|
||||
{ label: 'Python', value: langMap.python },
|
||||
{ label: 'JavaScript', value: langMap.javascript }
|
||||
];
|
||||
|
||||
const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
||||
const {
|
||||
title,
|
||||
open,
|
||||
onCancel,
|
||||
documentList = [],
|
||||
parameters = {}
|
||||
} = props || {};
|
||||
|
||||
const intl = useIntl();
|
||||
const [codeValue, setCodeValue] = useState('');
|
||||
const [lang, setLang] = useState(langMap.shell);
|
||||
|
||||
const BaseURL = `${window.location.origin}/v1/rerank`;
|
||||
|
||||
const generateCode = () => {
|
||||
if (lang === langMap.shell) {
|
||||
const code = `curl ${window.location.origin}/v1-openai \\\n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify(
|
||||
{
|
||||
...parameters,
|
||||
documents: documentList
|
||||
},
|
||||
null,
|
||||
2
|
||||
)}'`;
|
||||
setCodeValue(code);
|
||||
} else if (lang === langMap.javascript) {
|
||||
const data = {
|
||||
...parameters,
|
||||
documents: documentList
|
||||
};
|
||||
const headers = {
|
||||
'Content-type': 'application/json',
|
||||
Authorization: `Bearer $\{YOUR_GPUSTACK_API_KEY}`
|
||||
};
|
||||
const code = `import axios from 'axios';\n\nconst url = "${BaseURL}";\n\nconst headers = ${JSON.stringify(headers, null, 2)};\n\nconst data = ${JSON.stringify(data, null, 2)};\n\naxios.post(url, data, { headers }).then((response) => {\n console.log(response.data);\n});`;
|
||||
setCodeValue(code);
|
||||
} else if (lang === langMap.python) {
|
||||
const data = {
|
||||
...parameters,
|
||||
documents: documentList
|
||||
};
|
||||
const headers = {
|
||||
'Content-type': 'application/json',
|
||||
Authorization: `Bearer $\{YOUR_GPUSTACK_API_KEY}`
|
||||
};
|
||||
const code = `import requests\n\nurl="${BaseURL}"\n\nheaders = ${JSON.stringify(headers, null, 2)}\n\ndata=${JSON.stringify(data, null, 2)}\n\nresponse = requests.post(url, headers=headers, json=data)\n\nprint(response.json())`;
|
||||
setCodeValue(code);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnChangeLang = (value: string) => {
|
||||
setLang(value);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setLang(langMap.shell);
|
||||
onCancel();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
generateCode();
|
||||
}, [lang, parameters, documentList]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title={title}
|
||||
open={open}
|
||||
centered={true}
|
||||
onCancel={handleClose}
|
||||
destroyOnClose={true}
|
||||
closeIcon={true}
|
||||
maskClosable={false}
|
||||
keyboard={false}
|
||||
width={600}
|
||||
footer={null}
|
||||
>
|
||||
<div style={{ marginBottom: '10px' }}>
|
||||
{intl.formatMessage({ id: 'playground.viewcode.info' })}
|
||||
</div>
|
||||
<div>
|
||||
<EditorWrap
|
||||
copyText={codeValue}
|
||||
langOptions={langOptions}
|
||||
defaultValue={langMap.shell}
|
||||
showHeader={true}
|
||||
onChangeLang={handleOnChangeLang}
|
||||
styles={{
|
||||
wrapper: {
|
||||
backgroundColor: 'var(--color-editor-dark)'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<HighlightCode
|
||||
height={380}
|
||||
theme="dark"
|
||||
code={codeValue}
|
||||
lang={lang}
|
||||
copyable={false}
|
||||
></HighlightCode>
|
||||
</EditorWrap>
|
||||
<div
|
||||
style={{ marginTop: 10, display: 'flex', alignItems: 'baseline' }}
|
||||
>
|
||||
<BulbOutlined className="m-r-8" />
|
||||
<span>
|
||||
{intl.formatMessage(
|
||||
{ id: 'playground.viewcode.tips' },
|
||||
{
|
||||
here: (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
href="#/api-keys"
|
||||
target="_blank"
|
||||
style={{ paddingInline: 2 }}
|
||||
>
|
||||
<span>
|
||||
{' '}
|
||||
{intl.formatMessage({
|
||||
id: 'playground.viewcode.here'
|
||||
})}
|
||||
</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewCodeModal;
|
||||
Reference in New Issue
Block a user