mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-06-04 11:24:00 +08:00
feat: add DocumentPreviewer for chunk of chat reference and remove duplicate \n from record.progress_msg (#97)
* feat: Remove duplicate \n from record.progress_msg * feat: add DocumentPreviewer for chunk of chat reference
This commit is contained in:
parent
8a57f2afd5
commit
07d76ea18d
12
web/src/components/pdf-previewer/index.less
Normal file
12
web/src/components/pdf-previewer/index.less
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.documentContainer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
:global(.PdfHighlighter) {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
:global(.Highlight--scrolledTo .Highlight__part) {
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: rgba(255, 226, 143, 1);
|
||||||
|
}
|
||||||
|
}
|
116
web/src/components/pdf-previewer/index.tsx
Normal file
116
web/src/components/pdf-previewer/index.tsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import {
|
||||||
|
useGetChunkHighlights,
|
||||||
|
useGetDocumentUrl,
|
||||||
|
} from '@/hooks/documentHooks';
|
||||||
|
import { IChunk } from '@/interfaces/database/knowledge';
|
||||||
|
import { Skeleton } from 'antd';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import {
|
||||||
|
AreaHighlight,
|
||||||
|
Highlight,
|
||||||
|
IHighlight,
|
||||||
|
PdfHighlighter,
|
||||||
|
PdfLoader,
|
||||||
|
Popup,
|
||||||
|
} from 'react-pdf-highlighter';
|
||||||
|
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
chunk: IChunk;
|
||||||
|
documentId: string;
|
||||||
|
visible: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HighlightPopup = ({
|
||||||
|
comment,
|
||||||
|
}: {
|
||||||
|
comment: { text: string; emoji: string };
|
||||||
|
}) =>
|
||||||
|
comment.text ? (
|
||||||
|
<div className="Highlight__popup">
|
||||||
|
{comment.emoji} {comment.text}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
|
||||||
|
const url = useGetDocumentUrl(documentId);
|
||||||
|
const state = useGetChunkHighlights(chunk);
|
||||||
|
const ref = useRef<(highlight: IHighlight) => void>(() => {});
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
|
const resetHash = () => {};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoaded(visible);
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state.length > 0 && loaded) {
|
||||||
|
setLoaded(false);
|
||||||
|
ref.current(state[0]);
|
||||||
|
}
|
||||||
|
}, [state, loaded]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.documentContainer}>
|
||||||
|
<PdfLoader url={url} beforeLoad={<Skeleton active />}>
|
||||||
|
{(pdfDocument) => (
|
||||||
|
<PdfHighlighter
|
||||||
|
pdfDocument={pdfDocument}
|
||||||
|
enableAreaSelection={(event) => event.altKey}
|
||||||
|
onScrollChange={resetHash}
|
||||||
|
scrollRef={(scrollTo) => {
|
||||||
|
ref.current = scrollTo;
|
||||||
|
setLoaded(true);
|
||||||
|
}}
|
||||||
|
onSelectionFinished={() => null}
|
||||||
|
highlightTransform={(
|
||||||
|
highlight,
|
||||||
|
index,
|
||||||
|
setTip,
|
||||||
|
hideTip,
|
||||||
|
viewportToScaled,
|
||||||
|
screenshot,
|
||||||
|
isScrolledTo,
|
||||||
|
) => {
|
||||||
|
const isTextHighlight = !Boolean(
|
||||||
|
highlight.content && highlight.content.image,
|
||||||
|
);
|
||||||
|
|
||||||
|
const component = isTextHighlight ? (
|
||||||
|
<Highlight
|
||||||
|
isScrolledTo={isScrolledTo}
|
||||||
|
position={highlight.position}
|
||||||
|
comment={highlight.comment}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<AreaHighlight
|
||||||
|
isScrolledTo={isScrolledTo}
|
||||||
|
highlight={highlight}
|
||||||
|
onChange={() => {}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popup
|
||||||
|
popupContent={<HighlightPopup {...highlight} />}
|
||||||
|
onMouseOver={(popupContent) =>
|
||||||
|
setTip(highlight, () => popupContent)
|
||||||
|
}
|
||||||
|
onMouseOut={hideTip}
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
{component}
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
highlights={state}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</PdfLoader>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DocumentPreviewer;
|
21
web/src/hooks/documentHooks.ts
Normal file
21
web/src/hooks/documentHooks.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { IChunk } from '@/interfaces/database/knowledge';
|
||||||
|
import { api_host } from '@/utils/api';
|
||||||
|
import { buildChunkHighlights } from '@/utils/documentUtils';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { IHighlight } from 'react-pdf-highlighter';
|
||||||
|
|
||||||
|
export const useGetDocumentUrl = (documentId: string) => {
|
||||||
|
const url = useMemo(() => {
|
||||||
|
return `${api_host}/document/get/${documentId}`;
|
||||||
|
}, [documentId]);
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetChunkHighlights = (selectedChunk: IChunk): IHighlight[] => {
|
||||||
|
const highlights: IHighlight[] = useMemo(() => {
|
||||||
|
return buildChunkHighlights(selectedChunk);
|
||||||
|
}, [selectedChunk]);
|
||||||
|
|
||||||
|
return highlights;
|
||||||
|
};
|
@ -1,4 +1,5 @@
|
|||||||
import { MessageType } from '@/constants/chat';
|
import { MessageType } from '@/constants/chat';
|
||||||
|
import { IChunk } from './knowledge';
|
||||||
|
|
||||||
export interface PromptConfig {
|
export interface PromptConfig {
|
||||||
empty_response: string;
|
empty_response: string;
|
||||||
@ -66,7 +67,7 @@ export interface Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IReference {
|
export interface IReference {
|
||||||
chunks: Chunk[];
|
chunks: IChunk[];
|
||||||
doc_aggs: Docagg[];
|
doc_aggs: Docagg[];
|
||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
@ -77,16 +78,16 @@ export interface Docagg {
|
|||||||
doc_name: string;
|
doc_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Chunk {
|
// interface Chunk {
|
||||||
chunk_id: string;
|
// chunk_id: string;
|
||||||
content_ltks: string;
|
// content_ltks: string;
|
||||||
content_with_weight: string;
|
// content_with_weight: string;
|
||||||
doc_id: string;
|
// doc_id: string;
|
||||||
docnm_kwd: string;
|
// docnm_kwd: string;
|
||||||
img_id: string;
|
// img_id: string;
|
||||||
important_kwd: any[];
|
// important_kwd: any[];
|
||||||
kb_id: string;
|
// kb_id: string;
|
||||||
similarity: number;
|
// similarity: number;
|
||||||
term_similarity: number;
|
// term_similarity: number;
|
||||||
vector_similarity: number;
|
// vector_similarity: number;
|
||||||
}
|
// }
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
.documentContainer {
|
.documentContainer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100vh - 284px);
|
height: calc(100vh - 284px);
|
||||||
// overflow-y: auto;
|
|
||||||
// overflow-x: hidden;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
:global(.PdfHighlighter) {
|
:global(.PdfHighlighter) {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
@ -16,7 +16,6 @@ import styles from './index.less';
|
|||||||
interface IProps {
|
interface IProps {
|
||||||
selectedChunkId: string;
|
selectedChunkId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HighlightPopup = ({
|
const HighlightPopup = ({
|
||||||
comment,
|
comment,
|
||||||
}: {
|
}: {
|
||||||
@ -28,6 +27,7 @@ const HighlightPopup = ({
|
|||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
// TODO: merge with DocumentPreviewer
|
||||||
const Preview = ({ selectedChunkId }: IProps) => {
|
const Preview = ({ selectedChunkId }: IProps) => {
|
||||||
const url = useGetDocumentUrl();
|
const url = useGetDocumentUrl();
|
||||||
const state = useGetChunkHighlights(selectedChunkId);
|
const state = useGetChunkHighlights(selectedChunkId);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||||
|
import { buildChunkHighlights } from '@/utils/documentUtils';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { IHighlight } from 'react-pdf-highlighter';
|
import { IHighlight } from 'react-pdf-highlighter';
|
||||||
import { useSelector } from 'umi';
|
import { useSelector } from 'umi';
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
export const useSelectDocumentInfo = () => {
|
export const useSelectDocumentInfo = () => {
|
||||||
const documentInfo: IKnowledgeFile = useSelector(
|
const documentInfo: IKnowledgeFile = useSelector(
|
||||||
@ -41,35 +41,7 @@ export const useGetChunkHighlights = (
|
|||||||
const selectedChunk: IChunk = useGetSelectedChunk(selectedChunkId);
|
const selectedChunk: IChunk = useGetSelectedChunk(selectedChunkId);
|
||||||
|
|
||||||
const highlights: IHighlight[] = useMemo(() => {
|
const highlights: IHighlight[] = useMemo(() => {
|
||||||
return Array.isArray(selectedChunk?.positions) &&
|
return buildChunkHighlights(selectedChunk);
|
||||||
selectedChunk.positions.every((x) => Array.isArray(x))
|
|
||||||
? selectedChunk?.positions?.map((x) => {
|
|
||||||
const actualPositions = x.map((y, index) =>
|
|
||||||
index !== 0 ? y / 0.7 : y,
|
|
||||||
);
|
|
||||||
const boundingRect = {
|
|
||||||
width: 849,
|
|
||||||
height: 1200,
|
|
||||||
x1: actualPositions[1],
|
|
||||||
x2: actualPositions[2],
|
|
||||||
y1: actualPositions[3],
|
|
||||||
y2: actualPositions[4],
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
id: uuid(),
|
|
||||||
comment: {
|
|
||||||
text: '',
|
|
||||||
emoji: '',
|
|
||||||
},
|
|
||||||
content: { text: selectedChunk.content_with_weight },
|
|
||||||
position: {
|
|
||||||
boundingRect: boundingRect,
|
|
||||||
rects: [boundingRect],
|
|
||||||
pageNumber: x[0],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
}, [selectedChunk]);
|
}, [selectedChunk]);
|
||||||
|
|
||||||
return highlights;
|
return highlights;
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
.popoverContentText {
|
.popoverContentText {
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow: auto;
|
||||||
.popoverContentErrorLabel {
|
.popoverContentErrorLabel {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,25 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PopoverContent = ({ record }: IProps) => {
|
const PopoverContent = ({ record }: IProps) => {
|
||||||
|
const replaceText = (text: string) => {
|
||||||
|
// Remove duplicate \n
|
||||||
|
const nextText = text.replace(/(\n)\1+/g, '$1');
|
||||||
|
|
||||||
|
const replacedText = reactStringReplace(
|
||||||
|
nextText,
|
||||||
|
/(\[ERROR\].+\s)/g,
|
||||||
|
(match, i) => {
|
||||||
|
return (
|
||||||
|
<span key={i} className={styles.popoverContentErrorLabel}>
|
||||||
|
{match}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return replacedText;
|
||||||
|
};
|
||||||
|
|
||||||
const items: DescriptionsProps['items'] = [
|
const items: DescriptionsProps['items'] = [
|
||||||
{
|
{
|
||||||
key: 'process_begin_at',
|
key: 'process_begin_at',
|
||||||
@ -35,17 +54,7 @@ const PopoverContent = ({ record }: IProps) => {
|
|||||||
{
|
{
|
||||||
key: 'progress_msg',
|
key: 'progress_msg',
|
||||||
label: 'Progress Msg',
|
label: 'Progress Msg',
|
||||||
children: reactStringReplace(
|
children: replaceText(record.progress_msg.trim()),
|
||||||
record.progress_msg.trim(),
|
|
||||||
/(\[ERROR\].+\s)/g,
|
|
||||||
(match, i) => {
|
|
||||||
return (
|
|
||||||
<span key={i} className={styles.popoverContentErrorLabel}>
|
|
||||||
{match}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -65,7 +65,11 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
|||||||
>
|
>
|
||||||
<Input placeholder="" />
|
<Input placeholder="" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name={['prompt_config', 'prologue']} label="Set an opener">
|
<Form.Item
|
||||||
|
name={['prompt_config', 'prologue']}
|
||||||
|
label="Set an opener"
|
||||||
|
initialValue={"Hi! I'm your assistant, what can I do for you?"}
|
||||||
|
>
|
||||||
<Input.TextArea autoSize={{ minRows: 5 }} />
|
<Input.TextArea autoSize={{ minRows: 5 }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -3,11 +3,21 @@ import { MessageType } from '@/constants/chat';
|
|||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
import { useSelectUserInfo } from '@/hooks/userSettingHook';
|
import { useSelectUserInfo } from '@/hooks/userSettingHook';
|
||||||
import { IReference, Message } from '@/interfaces/database/chat';
|
import { IReference, Message } from '@/interfaces/database/chat';
|
||||||
import { Avatar, Button, Flex, Input, List, Popover, Space } from 'antd';
|
import {
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
Drawer,
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
|
List,
|
||||||
|
Popover,
|
||||||
|
Space,
|
||||||
|
} from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
|
||||||
import reactStringReplace from 'react-string-replace';
|
import reactStringReplace from 'react-string-replace';
|
||||||
import {
|
import {
|
||||||
|
useClickDrawer,
|
||||||
useFetchConversationOnMount,
|
useFetchConversationOnMount,
|
||||||
useGetFileIcon,
|
useGetFileIcon,
|
||||||
useSendMessage,
|
useSendMessage,
|
||||||
@ -15,7 +25,9 @@ import {
|
|||||||
|
|
||||||
import Image from '@/components/image';
|
import Image from '@/components/image';
|
||||||
import NewDocumentLink from '@/components/new-document-link';
|
import NewDocumentLink from '@/components/new-document-link';
|
||||||
|
import DocumentPreviewer from '@/components/pdf-previewer';
|
||||||
import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
|
import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
|
||||||
|
import { IChunk } from '@/interfaces/database/knowledge';
|
||||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
import Markdown from 'react-markdown';
|
import Markdown from 'react-markdown';
|
||||||
import { visitParents } from 'unist-util-visit-parents';
|
import { visitParents } from 'unist-util-visit-parents';
|
||||||
@ -41,15 +53,24 @@ const rehypeWrapReference = () => {
|
|||||||
const MessageItem = ({
|
const MessageItem = ({
|
||||||
item,
|
item,
|
||||||
reference,
|
reference,
|
||||||
|
clickDocumentButton,
|
||||||
}: {
|
}: {
|
||||||
item: Message;
|
item: Message;
|
||||||
reference: IReference;
|
reference: IReference;
|
||||||
|
clickDocumentButton: (documentId: string, chunk: IChunk) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const userInfo = useSelectUserInfo();
|
const userInfo = useSelectUserInfo();
|
||||||
const fileThumbnails = useSelectFileThumbnails();
|
const fileThumbnails = useSelectFileThumbnails();
|
||||||
|
|
||||||
const isAssistant = item.role === MessageType.Assistant;
|
const isAssistant = item.role === MessageType.Assistant;
|
||||||
|
|
||||||
|
const handleDocumentButtonClick = useCallback(
|
||||||
|
(documentId: string, chunk: IChunk) => () => {
|
||||||
|
clickDocumentButton(documentId, chunk);
|
||||||
|
},
|
||||||
|
[clickDocumentButton],
|
||||||
|
);
|
||||||
|
|
||||||
const getPopoverContent = useCallback(
|
const getPopoverContent = useCallback(
|
||||||
(chunkIndex: number) => {
|
(chunkIndex: number) => {
|
||||||
const chunks = reference?.chunks ?? [];
|
const chunks = reference?.chunks ?? [];
|
||||||
@ -83,16 +104,19 @@ const MessageItem = ({
|
|||||||
{documentId && (
|
{documentId && (
|
||||||
<Flex gap={'middle'}>
|
<Flex gap={'middle'}>
|
||||||
<img src={fileThumbnails[documentId]} alt="" />
|
<img src={fileThumbnails[documentId]} alt="" />
|
||||||
<NewDocumentLink documentId={documentId}>
|
<Button
|
||||||
|
type="link"
|
||||||
|
onClick={handleDocumentButtonClick(documentId, chunkItem)}
|
||||||
|
>
|
||||||
{document?.doc_name}
|
{document?.doc_name}
|
||||||
</NewDocumentLink>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[reference, fileThumbnails],
|
[reference, fileThumbnails, handleDocumentButtonClick],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderReference = useCallback(
|
const renderReference = useCallback(
|
||||||
@ -191,6 +215,8 @@ const ChatContainer = () => {
|
|||||||
addNewestConversation,
|
addNewestConversation,
|
||||||
} = useFetchConversationOnMount();
|
} = useFetchConversationOnMount();
|
||||||
const { sendMessage } = useSendMessage();
|
const { sendMessage } = useSendMessage();
|
||||||
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
|
useClickDrawer();
|
||||||
|
|
||||||
const loading = useOneNamespaceEffectsLoading('chatModel', [
|
const loading = useOneNamespaceEffectsLoading('chatModel', [
|
||||||
'completeConversation',
|
'completeConversation',
|
||||||
@ -210,6 +236,7 @@ const ChatContainer = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Flex flex={1} className={styles.chatContainer} vertical>
|
<Flex flex={1} className={styles.chatContainer} vertical>
|
||||||
<Flex flex={1} vertical className={styles.messageContainer}>
|
<Flex flex={1} vertical className={styles.messageContainer}>
|
||||||
<div>
|
<div>
|
||||||
@ -226,6 +253,7 @@ const ChatContainer = () => {
|
|||||||
key={message.id}
|
key={message.id}
|
||||||
item={message}
|
item={message}
|
||||||
reference={reference}
|
reference={reference}
|
||||||
|
clickDocumentButton={clickDocumentButton}
|
||||||
></MessageItem>
|
></MessageItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -245,6 +273,19 @@ const ChatContainer = () => {
|
|||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Drawer
|
||||||
|
title="Document Previewer"
|
||||||
|
onClose={hideModal}
|
||||||
|
open={visible}
|
||||||
|
width={'50vw'}
|
||||||
|
>
|
||||||
|
<DocumentPreviewer
|
||||||
|
documentId={documentId}
|
||||||
|
chunk={selectedChunk}
|
||||||
|
visible={visible}
|
||||||
|
></DocumentPreviewer>
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import { fileIconMap } from '@/constants/common';
|
|||||||
import { useSetModalState } from '@/hooks/commonHooks';
|
import { useSetModalState } from '@/hooks/commonHooks';
|
||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
import { IConversation, IDialog } from '@/interfaces/database/chat';
|
import { IConversation, IDialog } from '@/interfaces/database/chat';
|
||||||
|
import { IChunk } from '@/interfaces/database/knowledge';
|
||||||
import { getFileExtension } from '@/utils';
|
import { getFileExtension } from '@/utils';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
@ -662,4 +663,28 @@ export const useRenameConversation = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useClickDrawer = () => {
|
||||||
|
const { visible, showModal, hideModal } = useSetModalState();
|
||||||
|
const [selectedChunk, setSelectedChunk] = useState<IChunk>({} as IChunk);
|
||||||
|
const [documentId, setDocumentId] = useState<string>('');
|
||||||
|
|
||||||
|
const clickDocumentButton = useCallback(
|
||||||
|
(documentId: string, chunk: IChunk) => {
|
||||||
|
showModal();
|
||||||
|
setSelectedChunk(chunk);
|
||||||
|
setDocumentId(documentId);
|
||||||
|
},
|
||||||
|
[showModal],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
clickDocumentButton,
|
||||||
|
visible,
|
||||||
|
showModal,
|
||||||
|
hideModal,
|
||||||
|
selectedChunk,
|
||||||
|
documentId,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -50,13 +50,7 @@ const Knowledge = () => {
|
|||||||
</ModalManager>
|
</ModalManager>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<Flex
|
<Flex gap={'large'} wrap="wrap" className={styles.knowledgeCardContainer}>
|
||||||
gap="large"
|
|
||||||
wrap="wrap"
|
|
||||||
flex={1}
|
|
||||||
// justify="center"
|
|
||||||
className={styles.knowledgeCardContainer}
|
|
||||||
>
|
|
||||||
{list.length > 0 ? (
|
{list.length > 0 ? (
|
||||||
list.map((item: any) => {
|
list.map((item: any) => {
|
||||||
return <KnowledgeCard item={item} key={item.name}></KnowledgeCard>;
|
return <KnowledgeCard item={item} key={item.name}></KnowledgeCard>;
|
||||||
|
34
web/src/utils/documentUtils.ts
Normal file
34
web/src/utils/documentUtils.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { IChunk } from '@/interfaces/database/knowledge';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
export const buildChunkHighlights = (selectedChunk: IChunk) => {
|
||||||
|
return Array.isArray(selectedChunk?.positions) &&
|
||||||
|
selectedChunk.positions.every((x) => Array.isArray(x))
|
||||||
|
? selectedChunk?.positions?.map((x) => {
|
||||||
|
const actualPositions = x.map((y, index) =>
|
||||||
|
index !== 0 ? y / 0.7 : y,
|
||||||
|
);
|
||||||
|
const boundingRect = {
|
||||||
|
width: 849,
|
||||||
|
height: 1200,
|
||||||
|
x1: actualPositions[1],
|
||||||
|
x2: actualPositions[2],
|
||||||
|
y1: actualPositions[3],
|
||||||
|
y2: actualPositions[4],
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
id: uuid(),
|
||||||
|
comment: {
|
||||||
|
text: '',
|
||||||
|
emoji: '',
|
||||||
|
},
|
||||||
|
content: { text: selectedChunk.content_with_weight },
|
||||||
|
position: {
|
||||||
|
boundingRect: boundingRect,
|
||||||
|
rects: [boundingRect],
|
||||||
|
pageNumber: x[0],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user