mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-02 10:42:36 +08:00
### What problem does this PR solve? Feat: Refactoring the documentation page using shadcn. #10427 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -1,11 +1,23 @@
|
|||||||
import CopyToClipboard from '@/components/copy-to-clipboard';
|
import CopyToClipboard from '@/components/copy-to-clipboard';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from '@/components/ui/table';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { IToken } from '@/interfaces/database/chat';
|
|
||||||
import { formatDate } from '@/utils/date';
|
import { formatDate } from '@/utils/date';
|
||||||
import { DeleteOutlined } from '@ant-design/icons';
|
import { Trash2 } from 'lucide-react';
|
||||||
import type { TableProps } from 'antd';
|
|
||||||
import { Button, Modal, Space, Table } from 'antd';
|
|
||||||
import { useOperateApiKey } from '../hooks';
|
import { useOperateApiKey } from '../hooks';
|
||||||
|
|
||||||
const ChatApiKeyModal = ({
|
const ChatApiKeyModal = ({
|
||||||
@ -17,57 +29,59 @@ const ChatApiKeyModal = ({
|
|||||||
useOperateApiKey(idKey, dialogId);
|
useOperateApiKey(idKey, dialogId);
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
|
|
||||||
const columns: TableProps<IToken>['columns'] = [
|
|
||||||
{
|
|
||||||
title: 'Token',
|
|
||||||
dataIndex: 'token',
|
|
||||||
key: 'token',
|
|
||||||
render: (text) => <a>{text}</a>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('created'),
|
|
||||||
dataIndex: 'create_date',
|
|
||||||
key: 'create_date',
|
|
||||||
render: (text) => formatDate(text),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('action'),
|
|
||||||
key: 'action',
|
|
||||||
render: (_, record) => (
|
|
||||||
<Space size="middle">
|
|
||||||
<CopyToClipboard text={record.token}></CopyToClipboard>
|
|
||||||
<DeleteOutlined onClick={() => removeToken(record.token)} />
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal
|
<Dialog open onOpenChange={hideModal}>
|
||||||
title={t('apiKey')}
|
<DialogContent className="max-w-[50vw]">
|
||||||
open
|
<DialogHeader>
|
||||||
onCancel={hideModal}
|
<DialogTitle>{t('apiKey')}</DialogTitle>
|
||||||
cancelButtonProps={{ style: { display: 'none' } }}
|
</DialogHeader>
|
||||||
style={{ top: 300 }}
|
<div className="space-y-4">
|
||||||
onOk={hideModal}
|
{listLoading ? (
|
||||||
width={'50vw'}
|
<div className="flex justify-center py-8">Loading...</div>
|
||||||
>
|
) : (
|
||||||
<Table
|
<Table>
|
||||||
columns={columns}
|
<TableHeader>
|
||||||
dataSource={tokenList}
|
<TableRow>
|
||||||
rowKey={'token'}
|
<TableHead>Token</TableHead>
|
||||||
loading={listLoading}
|
<TableHead>{t('created')}</TableHead>
|
||||||
pagination={false}
|
<TableHead>{t('action')}</TableHead>
|
||||||
/>
|
</TableRow>
|
||||||
<Button
|
</TableHeader>
|
||||||
onClick={createToken}
|
<TableBody>
|
||||||
loading={creatingLoading}
|
{tokenList?.map((tokenItem) => (
|
||||||
disabled={tokenList?.length > 0}
|
<TableRow key={tokenItem.token}>
|
||||||
>
|
<TableCell className="font-medium break-all">
|
||||||
{t('createNewKey')}
|
{tokenItem.token}
|
||||||
</Button>
|
</TableCell>
|
||||||
</Modal>
|
<TableCell>{formatDate(tokenItem.create_date)}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<CopyToClipboard text={tokenItem.token} />
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => removeToken(tokenItem.token)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
onClick={createToken}
|
||||||
|
loading={creatingLoading}
|
||||||
|
disabled={tokenList?.length > 0}
|
||||||
|
>
|
||||||
|
{t('createNewKey')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,93 @@
|
|||||||
|
import React, { useSyncExternalStore } from 'react';
|
||||||
|
|
||||||
|
export interface AnchorItem {
|
||||||
|
key: string;
|
||||||
|
href: string;
|
||||||
|
title: string;
|
||||||
|
children?: AnchorItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SimpleAnchorProps {
|
||||||
|
items: AnchorItem[];
|
||||||
|
className?: string;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to URL hash changes
|
||||||
|
const subscribeHash = (callback: () => void) => {
|
||||||
|
window.addEventListener('hashchange', callback);
|
||||||
|
return () => window.removeEventListener('hashchange', callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getHash = () => window.location.hash;
|
||||||
|
|
||||||
|
const Anchor: React.FC<SimpleAnchorProps> = ({
|
||||||
|
items,
|
||||||
|
className = '',
|
||||||
|
style = {},
|
||||||
|
}) => {
|
||||||
|
// Sync with URL hash changes, to highlight the active item
|
||||||
|
const hash = useSyncExternalStore(subscribeHash, getHash);
|
||||||
|
|
||||||
|
// Handle menu item click
|
||||||
|
const handleClick = (
|
||||||
|
e: React.MouseEvent<HTMLAnchorElement>,
|
||||||
|
href: string,
|
||||||
|
) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const targetId = href.replace('#', '');
|
||||||
|
const targetElement = document.getElementById(targetId);
|
||||||
|
|
||||||
|
if (targetElement) {
|
||||||
|
// Update URL hash (triggers hashchange event)
|
||||||
|
window.location.hash = href;
|
||||||
|
// Smooth scroll to target
|
||||||
|
targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (items.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className={className} style={style}>
|
||||||
|
<ul className="list-none p-0 m-0">
|
||||||
|
{items.map((item) => (
|
||||||
|
<li key={item.key} className="mb-2">
|
||||||
|
<a
|
||||||
|
href={item.href}
|
||||||
|
onClick={(e) => handleClick(e, item.href)}
|
||||||
|
className={`block px-3 py-1.5 no-underline rounded cursor-pointer transition-all duration-300 hover:text-accent-primary/70 ${
|
||||||
|
hash === item.href
|
||||||
|
? 'text-accent-primary bg-accent-primary-5'
|
||||||
|
: 'text-text-secondary bg-transparent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</a>
|
||||||
|
{item.children && item.children.length > 0 && (
|
||||||
|
<ul className="list-none p-0 ml-4 mt-1">
|
||||||
|
{item.children.map((child) => (
|
||||||
|
<li key={child.key} className="mb-1">
|
||||||
|
<a
|
||||||
|
href={child.href}
|
||||||
|
onClick={(e) => handleClick(e, child.href)}
|
||||||
|
className={`block px-3 py-1 text-sm no-underline rounded cursor-pointer transition-all duration-300 hover:text-accent-primary/70 ${
|
||||||
|
hash === child.href
|
||||||
|
? 'text-accent-primary bg-accent-primary-5'
|
||||||
|
: 'text-text-secondary bg-transparent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{child.title}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Anchor;
|
||||||
@ -1,52 +1,26 @@
|
|||||||
import { useIsDarkTheme } from '@/components/theme-provider';
|
import { useIsDarkTheme } from '@/components/theme-provider';
|
||||||
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
import { LangfuseCard } from '@/pages/user-setting/setting-model/langfuse';
|
import { LangfuseCard } from '@/pages/user-setting/setting-model/langfuse';
|
||||||
import apiDoc from '@parent/docs/references/http_api_reference.md';
|
import apiDoc from '@parent/docs/references/http_api_reference.md';
|
||||||
import MarkdownPreview from '@uiw/react-markdown-preview';
|
import MarkdownPreview from '@uiw/react-markdown-preview';
|
||||||
import { Button, Card, Flex, Space } from 'antd';
|
|
||||||
import ChatApiKeyModal from '../chat-api-key-modal';
|
import ChatApiKeyModal from '../chat-api-key-modal';
|
||||||
import { usePreviewChat } from '../hooks';
|
|
||||||
import BackendServiceApi from './backend-service-api';
|
import BackendServiceApi from './backend-service-api';
|
||||||
import MarkdownToc from './markdown-toc';
|
import MarkdownToc from './markdown-toc';
|
||||||
|
|
||||||
const ApiContent = ({
|
const ApiContent = ({ id, idKey }: { id?: string; idKey: string }) => {
|
||||||
id,
|
|
||||||
idKey,
|
|
||||||
hideChatPreviewCard = false,
|
|
||||||
}: {
|
|
||||||
id?: string;
|
|
||||||
idKey: string;
|
|
||||||
hideChatPreviewCard?: boolean;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslate('chat');
|
|
||||||
const {
|
const {
|
||||||
visible: apiKeyVisible,
|
visible: apiKeyVisible,
|
||||||
hideModal: hideApiKeyModal,
|
hideModal: hideApiKeyModal,
|
||||||
showModal: showApiKeyModal,
|
showModal: showApiKeyModal,
|
||||||
} = useSetModalState();
|
} = useSetModalState();
|
||||||
// const { embedVisible, hideEmbedModal, showEmbedModal, embedToken } =
|
|
||||||
// useShowEmbedModal(idKey);
|
|
||||||
|
|
||||||
const { handlePreview } = usePreviewChat(idKey);
|
|
||||||
|
|
||||||
const isDarkTheme = useIsDarkTheme();
|
const isDarkTheme = useIsDarkTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pb-2">
|
<div className="pb-2">
|
||||||
<Flex vertical gap={'middle'}>
|
<section className="flex flex-col gap-2 pb-5">
|
||||||
<BackendServiceApi show={showApiKeyModal}></BackendServiceApi>
|
<BackendServiceApi show={showApiKeyModal}></BackendServiceApi>
|
||||||
{!hideChatPreviewCard && (
|
|
||||||
<Card title={`${name} Web App`}>
|
|
||||||
<Flex gap={8} vertical>
|
|
||||||
<Space size={'middle'}>
|
|
||||||
<Button onClick={handlePreview}>{t('preview')}</Button>
|
|
||||||
{/* <Button onClick={() => showEmbedModal(id)}>
|
|
||||||
{t('embedded')}
|
|
||||||
</Button> */}
|
|
||||||
</Space>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
<MarkdownToc content={apiDoc} />
|
<MarkdownToc content={apiDoc} />
|
||||||
</div>
|
</div>
|
||||||
@ -54,7 +28,8 @@ const ApiContent = ({
|
|||||||
source={apiDoc}
|
source={apiDoc}
|
||||||
wrapperElement={{ 'data-color-mode': isDarkTheme ? 'dark' : 'light' }}
|
wrapperElement={{ 'data-color-mode': isDarkTheme ? 'dark' : 'light' }}
|
||||||
></MarkdownPreview>
|
></MarkdownPreview>
|
||||||
</Flex>
|
</section>
|
||||||
|
<LangfuseCard></LangfuseCard>
|
||||||
{apiKeyVisible && (
|
{apiKeyVisible && (
|
||||||
<ChatApiKeyModal
|
<ChatApiKeyModal
|
||||||
hideModal={hideApiKeyModal}
|
hideModal={hideApiKeyModal}
|
||||||
@ -62,14 +37,6 @@ const ApiContent = ({
|
|||||||
idKey={idKey}
|
idKey={idKey}
|
||||||
></ChatApiKeyModal>
|
></ChatApiKeyModal>
|
||||||
)}
|
)}
|
||||||
{/* {embedVisible && (
|
|
||||||
<EmbedModal
|
|
||||||
token={embedToken}
|
|
||||||
visible={embedVisible}
|
|
||||||
hideModal={hideEmbedModal}
|
|
||||||
></EmbedModal>
|
|
||||||
)} */}
|
|
||||||
<LangfuseCard></LangfuseCard>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,33 +1,28 @@
|
|||||||
import { Button, Card, Flex, Space, Typography } from 'antd';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
|
||||||
|
import { CopyToClipboardWithText } from '@/components/copy-to-clipboard';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const { Paragraph } = Typography;
|
|
||||||
|
|
||||||
const BackendServiceApi = ({ show }: { show(): void }) => {
|
const BackendServiceApi = ({ show }: { show(): void }) => {
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card>
|
||||||
title={
|
<CardHeader>
|
||||||
<Space size={'large'}>
|
<div className="flex items-center gap-4">
|
||||||
<span>RAGFlow API</span>
|
<CardTitle>RAGFlow API</CardTitle>
|
||||||
<Button onClick={show} type="primary">
|
<Button onClick={show}>{t('apiKey')}</Button>
|
||||||
{t('apiKey')}
|
</div>
|
||||||
</Button>
|
</CardHeader>
|
||||||
</Space>
|
<CardContent>
|
||||||
}
|
<div className="flex items-center gap-2">
|
||||||
>
|
<b className="font-semibold">{t('backendServiceApi')}</b>
|
||||||
<Flex gap={8} align="center">
|
<CopyToClipboardWithText
|
||||||
<b>{t('backendServiceApi')}</b>
|
text={location.origin}
|
||||||
<Paragraph
|
></CopyToClipboardWithText>
|
||||||
copyable={{ text: `${location.origin}` }}
|
</div>
|
||||||
className={styles.apiLinkText}
|
</CardContent>
|
||||||
>
|
|
||||||
{location.origin}
|
|
||||||
</Paragraph>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
import { Modal } from 'antd';
|
|
||||||
import ApiContent from './api-content';
|
|
||||||
|
|
||||||
const ChatOverviewModal = ({
|
|
||||||
visible,
|
|
||||||
hideModal,
|
|
||||||
id,
|
|
||||||
idKey,
|
|
||||||
}: IModalProps<any> & { id: string; name?: string; idKey: string }) => {
|
|
||||||
const { t } = useTranslate('chat');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Modal
|
|
||||||
title={t('overview')}
|
|
||||||
open={visible}
|
|
||||||
onCancel={hideModal}
|
|
||||||
cancelButtonProps={{ style: { display: 'none' } }}
|
|
||||||
onOk={hideModal}
|
|
||||||
width={'100vw'}
|
|
||||||
okText={t('close', { keyPrefix: 'common' })}
|
|
||||||
>
|
|
||||||
<ApiContent id={id} idKey={idKey}></ApiContent>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChatOverviewModal;
|
|
||||||
@ -1,21 +1,27 @@
|
|||||||
import { Anchor } from 'antd';
|
|
||||||
import type { AnchorLinkItemProps } from 'antd/es/anchor/Anchor';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import Anchor, { AnchorItem } from './anchor';
|
||||||
|
|
||||||
interface MarkdownTocProps {
|
interface MarkdownTocProps {
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MarkdownToc: React.FC<MarkdownTocProps> = ({ content }) => {
|
const MarkdownToc: React.FC<MarkdownTocProps> = ({ content }) => {
|
||||||
const [items, setItems] = useState<AnchorLinkItemProps[]>([]);
|
const [items, setItems] = useState<AnchorItem[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const generateTocItems = () => {
|
const generateTocItems = () => {
|
||||||
const headings = document.querySelectorAll(
|
const headings = document.querySelectorAll(
|
||||||
'.wmde-markdown h2, .wmde-markdown h3',
|
'.wmde-markdown h2, .wmde-markdown h3',
|
||||||
);
|
);
|
||||||
const tocItems: AnchorLinkItemProps[] = [];
|
|
||||||
let currentH2Item: AnchorLinkItemProps | null = null;
|
// If headings haven't rendered yet, wait for next frame
|
||||||
|
if (headings.length === 0) {
|
||||||
|
requestAnimationFrame(generateTocItems);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tocItems: AnchorItem[] = [];
|
||||||
|
let currentH2Item: AnchorItem | null = null;
|
||||||
|
|
||||||
headings.forEach((heading) => {
|
headings.forEach((heading) => {
|
||||||
const title = heading.textContent || '';
|
const title = heading.textContent || '';
|
||||||
@ -23,7 +29,7 @@ const MarkdownToc: React.FC<MarkdownTocProps> = ({ content }) => {
|
|||||||
const isH2 = heading.tagName.toLowerCase() === 'h2';
|
const isH2 = heading.tagName.toLowerCase() === 'h2';
|
||||||
|
|
||||||
if (id && title) {
|
if (id && title) {
|
||||||
const item: AnchorLinkItemProps = {
|
const item: AnchorItem = {
|
||||||
key: id,
|
key: id,
|
||||||
href: `#${id}`,
|
href: `#${id}`,
|
||||||
title,
|
title,
|
||||||
@ -48,7 +54,10 @@ const MarkdownToc: React.FC<MarkdownTocProps> = ({ content }) => {
|
|||||||
setItems(tocItems.slice(1));
|
setItems(tocItems.slice(1));
|
||||||
};
|
};
|
||||||
|
|
||||||
setTimeout(generateTocItems, 100);
|
// Use requestAnimationFrame to ensure execution after DOM rendering
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(generateTocItems);
|
||||||
|
});
|
||||||
}, [content]);
|
}, [content]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -56,7 +65,7 @@ const MarkdownToc: React.FC<MarkdownTocProps> = ({ content }) => {
|
|||||||
className="markdown-toc bg-bg-base text-text-primary shadow shadow-text-secondary"
|
className="markdown-toc bg-bg-base text-text-primary shadow shadow-text-secondary"
|
||||||
style={{
|
style={{
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
right: 20,
|
right: 30,
|
||||||
top: 100,
|
top: 100,
|
||||||
bottom: 150,
|
bottom: 150,
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -66,7 +75,7 @@ const MarkdownToc: React.FC<MarkdownTocProps> = ({ content }) => {
|
|||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Anchor items={items} affix={false} />
|
<Anchor items={items} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
.codeCard {
|
|
||||||
.clearCardBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
.codeText {
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #ffffff09;
|
|
||||||
}
|
|
||||||
|
|
||||||
.id {
|
|
||||||
.linkText();
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkBg {
|
|
||||||
background-color: rgb(69, 68, 68);
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkId {
|
|
||||||
color: white;
|
|
||||||
.darkBg();
|
|
||||||
}
|
|
||||||
@ -1,170 +0,0 @@
|
|||||||
import CopyToClipboard from '@/components/copy-to-clipboard';
|
|
||||||
import HighLightMarkdown from '@/components/highlight-markdown';
|
|
||||||
import { SharedFrom } from '@/constants/chat';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
Checkbox,
|
|
||||||
Form,
|
|
||||||
Modal,
|
|
||||||
Select,
|
|
||||||
Tabs,
|
|
||||||
TabsProps,
|
|
||||||
Typography,
|
|
||||||
} from 'antd';
|
|
||||||
import { useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import { useIsDarkTheme } from '@/components/theme-provider';
|
|
||||||
import {
|
|
||||||
LanguageAbbreviation,
|
|
||||||
LanguageAbbreviationMap,
|
|
||||||
} from '@/constants/common';
|
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const { Paragraph, Link } = Typography;
|
|
||||||
|
|
||||||
const EmbedModal = ({
|
|
||||||
visible,
|
|
||||||
hideModal,
|
|
||||||
token = '',
|
|
||||||
form,
|
|
||||||
beta = '',
|
|
||||||
isAgent,
|
|
||||||
}: IModalProps<any> & {
|
|
||||||
token: string;
|
|
||||||
form: SharedFrom;
|
|
||||||
beta: string;
|
|
||||||
isAgent: boolean;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslate('chat');
|
|
||||||
const isDarkTheme = useIsDarkTheme();
|
|
||||||
|
|
||||||
const [visibleAvatar, setVisibleAvatar] = useState(false);
|
|
||||||
const [locale, setLocale] = useState('');
|
|
||||||
|
|
||||||
const languageOptions = useMemo(() => {
|
|
||||||
return Object.values(LanguageAbbreviation).map((x) => ({
|
|
||||||
label: LanguageAbbreviationMap[x],
|
|
||||||
value: x,
|
|
||||||
}));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const generateIframeSrc = () => {
|
|
||||||
let src = `${location.origin}/chat/share?shared_id=${token}&from=${form}&auth=${beta}`;
|
|
||||||
if (visibleAvatar) {
|
|
||||||
src += '&visible_avatar=1';
|
|
||||||
}
|
|
||||||
if (locale) {
|
|
||||||
src += `&locale=${locale}`;
|
|
||||||
}
|
|
||||||
return src;
|
|
||||||
};
|
|
||||||
|
|
||||||
const iframeSrc = generateIframeSrc();
|
|
||||||
|
|
||||||
const text = `
|
|
||||||
~~~ html
|
|
||||||
<iframe
|
|
||||||
src="${iframeSrc}"
|
|
||||||
style="width: 100%; height: 100%; min-height: 600px"
|
|
||||||
frameborder="0"
|
|
||||||
>
|
|
||||||
</iframe>
|
|
||||||
~~~
|
|
||||||
`;
|
|
||||||
|
|
||||||
const items: TabsProps['items'] = [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
label: t('fullScreenTitle'),
|
|
||||||
children: (
|
|
||||||
<Card
|
|
||||||
title={t('fullScreenDescription')}
|
|
||||||
extra={<CopyToClipboard text={text}></CopyToClipboard>}
|
|
||||||
className={styles.codeCard}
|
|
||||||
>
|
|
||||||
<div className="p-2">
|
|
||||||
<h2 className="mb-3">Option:</h2>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
label={t('avatarHidden')}
|
|
||||||
labelCol={{ span: 6 }}
|
|
||||||
wrapperCol={{ span: 18 }}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={visibleAvatar}
|
|
||||||
onChange={(e) => setVisibleAvatar(e.target.checked)}
|
|
||||||
></Checkbox>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('locale')}
|
|
||||||
labelCol={{ span: 6 }}
|
|
||||||
wrapperCol={{ span: 18 }}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
placeholder="Select a locale"
|
|
||||||
onChange={(value) => setLocale(value)}
|
|
||||||
options={languageOptions}
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
<HighLightMarkdown>{text}</HighLightMarkdown>
|
|
||||||
</Card>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
label: t('partialTitle'),
|
|
||||||
children: t('comingSoon'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '3',
|
|
||||||
label: t('extensionTitle'),
|
|
||||||
children: t('comingSoon'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const onChange = (key: string) => {
|
|
||||||
console.log(key);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={t('embedIntoSite', { keyPrefix: 'common' })}
|
|
||||||
open={visible}
|
|
||||||
style={{ top: 300 }}
|
|
||||||
width={'50vw'}
|
|
||||||
onOk={hideModal}
|
|
||||||
onCancel={hideModal}
|
|
||||||
>
|
|
||||||
<Tabs defaultActiveKey="1" items={items} onChange={onChange} />
|
|
||||||
<div className="text-base font-medium mt-4 mb-1">
|
|
||||||
{t(isAgent ? 'flow' : 'chat', { keyPrefix: 'header' })}
|
|
||||||
<span className="ml-1 inline-block">ID</span>
|
|
||||||
</div>
|
|
||||||
<Paragraph
|
|
||||||
copyable={{ text: token }}
|
|
||||||
className={cn(styles.id, {
|
|
||||||
[styles.darkId]: isDarkTheme,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{token}
|
|
||||||
</Paragraph>
|
|
||||||
<Link
|
|
||||||
href={
|
|
||||||
isAgent
|
|
||||||
? 'https://ragflow.io/docs/dev/http_api_reference#create-session-with-agent'
|
|
||||||
: 'https://ragflow.io/docs/dev/http_api_reference#create-session-with-chat-assistant'
|
|
||||||
}
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{t('howUseId', { keyPrefix: isAgent ? 'flow' : 'chat' })}
|
|
||||||
</Link>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EmbedModal;
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { SharedFrom } from '@/constants/chat';
|
|
||||||
import {
|
import {
|
||||||
useSetModalState,
|
useSetModalState,
|
||||||
useShowDeleteConfirm,
|
useShowDeleteConfirm,
|
||||||
@ -80,11 +79,6 @@ export const useShowBetaEmptyError = () => {
|
|||||||
return { showBetaEmptyError };
|
return { showBetaEmptyError };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUrlWithToken = (token: string, from: string = 'chat') => {
|
|
||||||
const { protocol, host } = window.location;
|
|
||||||
return `${protocol}//${host}/chat/share?shared_id=${token}&from=${from}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const useFetchTokenListBeforeOtherStep = () => {
|
const useFetchTokenListBeforeOtherStep = () => {
|
||||||
const { showTokenEmptyError } = useShowTokenEmptyError();
|
const { showTokenEmptyError } = useShowTokenEmptyError();
|
||||||
const { showBetaEmptyError } = useShowBetaEmptyError();
|
const { showBetaEmptyError } = useShowBetaEmptyError();
|
||||||
@ -149,31 +143,3 @@ export const useShowEmbedModal = () => {
|
|||||||
beta,
|
beta,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePreviewChat = (idKey: string) => {
|
|
||||||
const { handleOperate } = useFetchTokenListBeforeOtherStep();
|
|
||||||
|
|
||||||
const open = useCallback(
|
|
||||||
(t: string) => {
|
|
||||||
window.open(
|
|
||||||
getUrlWithToken(
|
|
||||||
t,
|
|
||||||
idKey === 'canvasId' ? SharedFrom.Agent : SharedFrom.Chat,
|
|
||||||
),
|
|
||||||
'_blank',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[idKey],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlePreview = useCallback(async () => {
|
|
||||||
const token = await handleOperate();
|
|
||||||
if (token) {
|
|
||||||
open(token);
|
|
||||||
}
|
|
||||||
}, [handleOperate, open]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
handlePreview,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import styles from './index.less';
|
|||||||
const ApiPage = () => {
|
const ApiPage = () => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.apiWrapper}>
|
<div className={styles.apiWrapper}>
|
||||||
<ApiContent idKey="dialogId" hideChatPreviewCard></ApiContent>
|
<ApiContent idKey="dialogId"></ApiContent>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -45,11 +45,7 @@ export function LangfuseCard() {
|
|||||||
<Eye /> {t('setting.view')}
|
<Eye /> {t('setting.view')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button size={'sm'} onClick={showSaveLangfuseConfigurationModal}>
|
||||||
size={'sm'}
|
|
||||||
onClick={showSaveLangfuseConfigurationModal}
|
|
||||||
className="bg-blue-500 hover:bg-blue-400"
|
|
||||||
>
|
|
||||||
<Settings2 />
|
<Settings2 />
|
||||||
{t('setting.configuration')}
|
{t('setting.configuration')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user