diff --git a/web/src/components/api-service/chat-api-key-modal/index.tsx b/web/src/components/api-service/chat-api-key-modal/index.tsx index 2497f0fa2..e597c9985 100644 --- a/web/src/components/api-service/chat-api-key-modal/index.tsx +++ b/web/src/components/api-service/chat-api-key-modal/index.tsx @@ -1,11 +1,23 @@ 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 { IModalProps } from '@/interfaces/common'; -import { IToken } from '@/interfaces/database/chat'; import { formatDate } from '@/utils/date'; -import { DeleteOutlined } from '@ant-design/icons'; -import type { TableProps } from 'antd'; -import { Button, Modal, Space, Table } from 'antd'; +import { Trash2 } from 'lucide-react'; import { useOperateApiKey } from '../hooks'; const ChatApiKeyModal = ({ @@ -17,57 +29,59 @@ const ChatApiKeyModal = ({ useOperateApiKey(idKey, dialogId); const { t } = useTranslate('chat'); - const columns: TableProps['columns'] = [ - { - title: 'Token', - dataIndex: 'token', - key: 'token', - render: (text) => {text}, - }, - { - title: t('created'), - dataIndex: 'create_date', - key: 'create_date', - render: (text) => formatDate(text), - }, - { - title: t('action'), - key: 'action', - render: (_, record) => ( - - - removeToken(record.token)} /> - - ), - }, - ]; - return ( <> - - - - + + + + {t('apiKey')} + +
+ {listLoading ? ( +
Loading...
+ ) : ( +
+ + + Token + {t('created')} + {t('action')} + + + + {tokenList?.map((tokenItem) => ( + + + {tokenItem.token} + + {formatDate(tokenItem.create_date)} + +
+ + +
+
+
+ ))} +
+
+ )} + + + + ); }; diff --git a/web/src/components/api-service/chat-overview-modal/anchor.tsx b/web/src/components/api-service/chat-overview-modal/anchor.tsx new file mode 100644 index 000000000..ed6f13507 --- /dev/null +++ b/web/src/components/api-service/chat-overview-modal/anchor.tsx @@ -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 = ({ + 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, + 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 ( + + ); +}; + +export default Anchor; diff --git a/web/src/components/api-service/chat-overview-modal/api-content.tsx b/web/src/components/api-service/chat-overview-modal/api-content.tsx index ebdc36581..9e12aac45 100644 --- a/web/src/components/api-service/chat-overview-modal/api-content.tsx +++ b/web/src/components/api-service/chat-overview-modal/api-content.tsx @@ -1,52 +1,26 @@ 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 apiDoc from '@parent/docs/references/http_api_reference.md'; import MarkdownPreview from '@uiw/react-markdown-preview'; -import { Button, Card, Flex, Space } from 'antd'; import ChatApiKeyModal from '../chat-api-key-modal'; -import { usePreviewChat } from '../hooks'; import BackendServiceApi from './backend-service-api'; import MarkdownToc from './markdown-toc'; -const ApiContent = ({ - id, - idKey, - hideChatPreviewCard = false, -}: { - id?: string; - idKey: string; - hideChatPreviewCard?: boolean; -}) => { - const { t } = useTranslate('chat'); +const ApiContent = ({ id, idKey }: { id?: string; idKey: string }) => { const { visible: apiKeyVisible, hideModal: hideApiKeyModal, showModal: showApiKeyModal, } = useSetModalState(); - // const { embedVisible, hideEmbedModal, showEmbedModal, embedToken } = - // useShowEmbedModal(idKey); - - const { handlePreview } = usePreviewChat(idKey); const isDarkTheme = useIsDarkTheme(); return (
- +
- {!hideChatPreviewCard && ( - - - - - {/* */} - - - - )} +
@@ -54,7 +28,8 @@ const ApiContent = ({ source={apiDoc} wrapperElement={{ 'data-color-mode': isDarkTheme ? 'dark' : 'light' }} > - +
+ {apiKeyVisible && ( )} - {/* {embedVisible && ( - - )} */} -
); }; diff --git a/web/src/components/api-service/chat-overview-modal/backend-service-api.tsx b/web/src/components/api-service/chat-overview-modal/backend-service-api.tsx index 2524000c1..07a2811c9 100644 --- a/web/src/components/api-service/chat-overview-modal/backend-service-api.tsx +++ b/web/src/components/api-service/chat-overview-modal/backend-service-api.tsx @@ -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 styles from './index.less'; - -const { Paragraph } = Typography; const BackendServiceApi = ({ show }: { show(): void }) => { const { t } = useTranslate('chat'); return ( - - RAGFlow API - - - } - > - - {t('backendServiceApi')} - - {location.origin} - - + + +
+ RAGFlow API + +
+
+ +
+ {t('backendServiceApi')} + +
+
); }; diff --git a/web/src/components/api-service/chat-overview-modal/index.tsx b/web/src/components/api-service/chat-overview-modal/index.tsx deleted file mode 100644 index f31b05c82..000000000 --- a/web/src/components/api-service/chat-overview-modal/index.tsx +++ /dev/null @@ -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 & { id: string; name?: string; idKey: string }) => { - const { t } = useTranslate('chat'); - - return ( - <> - - - - - ); -}; - -export default ChatOverviewModal; diff --git a/web/src/components/api-service/chat-overview-modal/markdown-toc.tsx b/web/src/components/api-service/chat-overview-modal/markdown-toc.tsx index 498026b09..7234f5a8f 100644 --- a/web/src/components/api-service/chat-overview-modal/markdown-toc.tsx +++ b/web/src/components/api-service/chat-overview-modal/markdown-toc.tsx @@ -1,21 +1,27 @@ -import { Anchor } from 'antd'; -import type { AnchorLinkItemProps } from 'antd/es/anchor/Anchor'; import React, { useEffect, useState } from 'react'; +import Anchor, { AnchorItem } from './anchor'; interface MarkdownTocProps { content: string; } const MarkdownToc: React.FC = ({ content }) => { - const [items, setItems] = useState([]); + const [items, setItems] = useState([]); useEffect(() => { const generateTocItems = () => { const headings = document.querySelectorAll( '.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) => { const title = heading.textContent || ''; @@ -23,7 +29,7 @@ const MarkdownToc: React.FC = ({ content }) => { const isH2 = heading.tagName.toLowerCase() === 'h2'; if (id && title) { - const item: AnchorLinkItemProps = { + const item: AnchorItem = { key: id, href: `#${id}`, title, @@ -48,7 +54,10 @@ const MarkdownToc: React.FC = ({ content }) => { setItems(tocItems.slice(1)); }; - setTimeout(generateTocItems, 100); + // Use requestAnimationFrame to ensure execution after DOM rendering + requestAnimationFrame(() => { + requestAnimationFrame(generateTocItems); + }); }, [content]); return ( @@ -56,7 +65,7 @@ const MarkdownToc: React.FC = ({ content }) => { className="markdown-toc bg-bg-base text-text-primary shadow shadow-text-secondary" style={{ position: 'fixed', - right: 20, + right: 30, top: 100, bottom: 150, width: 200, @@ -66,7 +75,7 @@ const MarkdownToc: React.FC = ({ content }) => { zIndex: 1000, }} > - + ); }; diff --git a/web/src/components/api-service/embed-modal/index.less b/web/src/components/api-service/embed-modal/index.less deleted file mode 100644 index 2c85068ca..000000000 --- a/web/src/components/api-service/embed-modal/index.less +++ /dev/null @@ -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(); -} diff --git a/web/src/components/api-service/embed-modal/index.tsx b/web/src/components/api-service/embed-modal/index.tsx deleted file mode 100644 index f4cb49ea1..000000000 --- a/web/src/components/api-service/embed-modal/index.tsx +++ /dev/null @@ -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 & { - 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 - -~~~ - `; - - const items: TabsProps['items'] = [ - { - key: '1', - label: t('fullScreenTitle'), - children: ( - } - className={styles.codeCard} - > -
-

Option:

- - - setVisibleAvatar(e.target.checked)} - > - - -