balibabu commited on
Commit
ae21b62
·
1 Parent(s): b83edb4

feat: add DocumentPreviewer for chunk of chat reference and remove duplicate \n from record.progress_msg (#97)

Browse files

* feat: Remove duplicate \n from record.progress_msg

* feat: add DocumentPreviewer for chunk of chat reference

web/src/components/pdf-previewer/index.less ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .documentContainer {
2
+ width: 100%;
3
+ height: 100%;
4
+ position: relative;
5
+ :global(.PdfHighlighter) {
6
+ overflow-x: hidden;
7
+ }
8
+ :global(.Highlight--scrolledTo .Highlight__part) {
9
+ overflow-x: hidden;
10
+ background-color: rgba(255, 226, 143, 1);
11
+ }
12
+ }
web/src/components/pdf-previewer/index.tsx ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ useGetChunkHighlights,
3
+ useGetDocumentUrl,
4
+ } from '@/hooks/documentHooks';
5
+ import { IChunk } from '@/interfaces/database/knowledge';
6
+ import { Skeleton } from 'antd';
7
+ import { useEffect, useRef, useState } from 'react';
8
+ import {
9
+ AreaHighlight,
10
+ Highlight,
11
+ IHighlight,
12
+ PdfHighlighter,
13
+ PdfLoader,
14
+ Popup,
15
+ } from 'react-pdf-highlighter';
16
+
17
+ import styles from './index.less';
18
+
19
+ interface IProps {
20
+ chunk: IChunk;
21
+ documentId: string;
22
+ visible: boolean;
23
+ }
24
+
25
+ const HighlightPopup = ({
26
+ comment,
27
+ }: {
28
+ comment: { text: string; emoji: string };
29
+ }) =>
30
+ comment.text ? (
31
+ <div className="Highlight__popup">
32
+ {comment.emoji} {comment.text}
33
+ </div>
34
+ ) : null;
35
+
36
+ const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
37
+ const url = useGetDocumentUrl(documentId);
38
+ const state = useGetChunkHighlights(chunk);
39
+ const ref = useRef<(highlight: IHighlight) => void>(() => {});
40
+ const [loaded, setLoaded] = useState(false);
41
+
42
+ const resetHash = () => {};
43
+
44
+ useEffect(() => {
45
+ setLoaded(visible);
46
+ }, [visible]);
47
+
48
+ useEffect(() => {
49
+ if (state.length > 0 && loaded) {
50
+ setLoaded(false);
51
+ ref.current(state[0]);
52
+ }
53
+ }, [state, loaded]);
54
+
55
+ return (
56
+ <div className={styles.documentContainer}>
57
+ <PdfLoader url={url} beforeLoad={<Skeleton active />}>
58
+ {(pdfDocument) => (
59
+ <PdfHighlighter
60
+ pdfDocument={pdfDocument}
61
+ enableAreaSelection={(event) => event.altKey}
62
+ onScrollChange={resetHash}
63
+ scrollRef={(scrollTo) => {
64
+ ref.current = scrollTo;
65
+ setLoaded(true);
66
+ }}
67
+ onSelectionFinished={() => null}
68
+ highlightTransform={(
69
+ highlight,
70
+ index,
71
+ setTip,
72
+ hideTip,
73
+ viewportToScaled,
74
+ screenshot,
75
+ isScrolledTo,
76
+ ) => {
77
+ const isTextHighlight = !Boolean(
78
+ highlight.content && highlight.content.image,
79
+ );
80
+
81
+ const component = isTextHighlight ? (
82
+ <Highlight
83
+ isScrolledTo={isScrolledTo}
84
+ position={highlight.position}
85
+ comment={highlight.comment}
86
+ />
87
+ ) : (
88
+ <AreaHighlight
89
+ isScrolledTo={isScrolledTo}
90
+ highlight={highlight}
91
+ onChange={() => {}}
92
+ />
93
+ );
94
+
95
+ return (
96
+ <Popup
97
+ popupContent={<HighlightPopup {...highlight} />}
98
+ onMouseOver={(popupContent) =>
99
+ setTip(highlight, () => popupContent)
100
+ }
101
+ onMouseOut={hideTip}
102
+ key={index}
103
+ >
104
+ {component}
105
+ </Popup>
106
+ );
107
+ }}
108
+ highlights={state}
109
+ />
110
+ )}
111
+ </PdfLoader>
112
+ </div>
113
+ );
114
+ };
115
+
116
+ export default DocumentPreviewer;
web/src/hooks/documentHooks.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { IChunk } from '@/interfaces/database/knowledge';
2
+ import { api_host } from '@/utils/api';
3
+ import { buildChunkHighlights } from '@/utils/documentUtils';
4
+ import { useMemo } from 'react';
5
+ import { IHighlight } from 'react-pdf-highlighter';
6
+
7
+ export const useGetDocumentUrl = (documentId: string) => {
8
+ const url = useMemo(() => {
9
+ return `${api_host}/document/get/${documentId}`;
10
+ }, [documentId]);
11
+
12
+ return url;
13
+ };
14
+
15
+ export const useGetChunkHighlights = (selectedChunk: IChunk): IHighlight[] => {
16
+ const highlights: IHighlight[] = useMemo(() => {
17
+ return buildChunkHighlights(selectedChunk);
18
+ }, [selectedChunk]);
19
+
20
+ return highlights;
21
+ };
web/src/interfaces/database/chat.ts CHANGED
@@ -1,4 +1,5 @@
1
  import { MessageType } from '@/constants/chat';
 
2
 
3
  export interface PromptConfig {
4
  empty_response: string;
@@ -66,7 +67,7 @@ export interface Message {
66
  }
67
 
68
  export interface IReference {
69
- chunks: Chunk[];
70
  doc_aggs: Docagg[];
71
  total: number;
72
  }
@@ -77,16 +78,16 @@ export interface Docagg {
77
  doc_name: string;
78
  }
79
 
80
- interface Chunk {
81
- chunk_id: string;
82
- content_ltks: string;
83
- content_with_weight: string;
84
- doc_id: string;
85
- docnm_kwd: string;
86
- img_id: string;
87
- important_kwd: any[];
88
- kb_id: string;
89
- similarity: number;
90
- term_similarity: number;
91
- vector_similarity: number;
92
- }
 
1
  import { MessageType } from '@/constants/chat';
2
+ import { IChunk } from './knowledge';
3
 
4
  export interface PromptConfig {
5
  empty_response: string;
 
67
  }
68
 
69
  export interface IReference {
70
+ chunks: IChunk[];
71
  doc_aggs: Docagg[];
72
  total: number;
73
  }
 
78
  doc_name: string;
79
  }
80
 
81
+ // interface Chunk {
82
+ // chunk_id: string;
83
+ // content_ltks: string;
84
+ // content_with_weight: string;
85
+ // doc_id: string;
86
+ // docnm_kwd: string;
87
+ // img_id: string;
88
+ // important_kwd: any[];
89
+ // kb_id: string;
90
+ // similarity: number;
91
+ // term_similarity: number;
92
+ // vector_similarity: number;
93
+ // }
web/src/pages/add-knowledge/components/knowledge-chunk/components/document-preview/index.less CHANGED
@@ -1,8 +1,6 @@
1
  .documentContainer {
2
  width: 100%;
3
  height: calc(100vh - 284px);
4
- // overflow-y: auto;
5
- // overflow-x: hidden;
6
  position: relative;
7
  :global(.PdfHighlighter) {
8
  overflow-x: hidden;
 
1
  .documentContainer {
2
  width: 100%;
3
  height: calc(100vh - 284px);
 
 
4
  position: relative;
5
  :global(.PdfHighlighter) {
6
  overflow-x: hidden;
web/src/pages/add-knowledge/components/knowledge-chunk/components/document-preview/preview.tsx CHANGED
@@ -16,7 +16,6 @@ import styles from './index.less';
16
  interface IProps {
17
  selectedChunkId: string;
18
  }
19
-
20
  const HighlightPopup = ({
21
  comment,
22
  }: {
@@ -28,6 +27,7 @@ const HighlightPopup = ({
28
  </div>
29
  ) : null;
30
 
 
31
  const Preview = ({ selectedChunkId }: IProps) => {
32
  const url = useGetDocumentUrl();
33
  const state = useGetChunkHighlights(selectedChunkId);
 
16
  interface IProps {
17
  selectedChunkId: string;
18
  }
 
19
  const HighlightPopup = ({
20
  comment,
21
  }: {
 
27
  </div>
28
  ) : null;
29
 
30
+ // TODO: merge with DocumentPreviewer
31
  const Preview = ({ selectedChunkId }: IProps) => {
32
  const url = useGetDocumentUrl();
33
  const state = useGetChunkHighlights(selectedChunkId);
web/src/pages/add-knowledge/components/knowledge-chunk/hooks.ts CHANGED
@@ -1,8 +1,8 @@
1
  import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
 
2
  import { useCallback, useMemo, useState } from 'react';
3
  import { IHighlight } from 'react-pdf-highlighter';
4
  import { useSelector } from 'umi';
5
- import { v4 as uuid } from 'uuid';
6
 
7
  export const useSelectDocumentInfo = () => {
8
  const documentInfo: IKnowledgeFile = useSelector(
@@ -41,35 +41,7 @@ export const useGetChunkHighlights = (
41
  const selectedChunk: IChunk = useGetSelectedChunk(selectedChunkId);
42
 
43
  const highlights: IHighlight[] = useMemo(() => {
44
- return Array.isArray(selectedChunk?.positions) &&
45
- selectedChunk.positions.every((x) => Array.isArray(x))
46
- ? selectedChunk?.positions?.map((x) => {
47
- const actualPositions = x.map((y, index) =>
48
- index !== 0 ? y / 0.7 : y,
49
- );
50
- const boundingRect = {
51
- width: 849,
52
- height: 1200,
53
- x1: actualPositions[1],
54
- x2: actualPositions[2],
55
- y1: actualPositions[3],
56
- y2: actualPositions[4],
57
- };
58
- return {
59
- id: uuid(),
60
- comment: {
61
- text: '',
62
- emoji: '',
63
- },
64
- content: { text: selectedChunk.content_with_weight },
65
- position: {
66
- boundingRect: boundingRect,
67
- rects: [boundingRect],
68
- pageNumber: x[0],
69
- },
70
- };
71
- })
72
- : [];
73
  }, [selectedChunk]);
74
 
75
  return highlights;
 
1
  import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
2
+ import { buildChunkHighlights } from '@/utils/documentUtils';
3
  import { useCallback, useMemo, useState } from 'react';
4
  import { IHighlight } from 'react-pdf-highlighter';
5
  import { useSelector } from 'umi';
 
6
 
7
  export const useSelectDocumentInfo = () => {
8
  const documentInfo: IKnowledgeFile = useSelector(
 
41
  const selectedChunk: IChunk = useGetSelectedChunk(selectedChunkId);
42
 
43
  const highlights: IHighlight[] = useMemo(() => {
44
+ return buildChunkHighlights(selectedChunk);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }, [selectedChunk]);
46
 
47
  return highlights;
web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.less CHANGED
@@ -8,6 +8,8 @@
8
 
9
  .popoverContentText {
10
  white-space: pre-line;
 
 
11
  .popoverContentErrorLabel {
12
  color: red;
13
  }
 
8
 
9
  .popoverContentText {
10
  white-space: pre-line;
11
+ max-height: 50vh;
12
+ overflow: auto;
13
  .popoverContentErrorLabel {
14
  color: red;
15
  }
web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx CHANGED
@@ -21,6 +21,25 @@ interface IProps {
21
  }
22
 
23
  const PopoverContent = ({ record }: IProps) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  const items: DescriptionsProps['items'] = [
25
  {
26
  key: 'process_begin_at',
@@ -35,17 +54,7 @@ const PopoverContent = ({ record }: IProps) => {
35
  {
36
  key: 'progress_msg',
37
  label: 'Progress Msg',
38
- children: reactStringReplace(
39
- record.progress_msg.trim(),
40
- /(\[ERROR\].+\s)/g,
41
- (match, i) => {
42
- return (
43
- <span key={i} className={styles.popoverContentErrorLabel}>
44
- {match}
45
- </span>
46
- );
47
- },
48
- ),
49
  },
50
  ];
51
 
 
21
  }
22
 
23
  const PopoverContent = ({ record }: IProps) => {
24
+ const replaceText = (text: string) => {
25
+ // Remove duplicate \n
26
+ const nextText = text.replace(/(\n)\1+/g, '$1');
27
+
28
+ const replacedText = reactStringReplace(
29
+ nextText,
30
+ /(\[ERROR\].+\s)/g,
31
+ (match, i) => {
32
+ return (
33
+ <span key={i} className={styles.popoverContentErrorLabel}>
34
+ {match}
35
+ </span>
36
+ );
37
+ },
38
+ );
39
+
40
+ return replacedText;
41
+ };
42
+
43
  const items: DescriptionsProps['items'] = [
44
  {
45
  key: 'process_begin_at',
 
54
  {
55
  key: 'progress_msg',
56
  label: 'Progress Msg',
57
+ children: replaceText(record.progress_msg.trim()),
 
 
 
 
 
 
 
 
 
 
58
  },
59
  ];
60
 
web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx CHANGED
@@ -65,7 +65,11 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
65
  >
66
  <Input placeholder="" />
67
  </Form.Item>
68
- <Form.Item name={['prompt_config', 'prologue']} label="Set an opener">
 
 
 
 
69
  <Input.TextArea autoSize={{ minRows: 5 }} />
70
  </Form.Item>
71
  <Form.Item
 
65
  >
66
  <Input placeholder="" />
67
  </Form.Item>
68
+ <Form.Item
69
+ name={['prompt_config', 'prologue']}
70
+ label="Set an opener"
71
+ initialValue={"Hi! I'm your assistant, what can I do for you?"}
72
+ >
73
  <Input.TextArea autoSize={{ minRows: 5 }} />
74
  </Form.Item>
75
  <Form.Item
web/src/pages/chat/chat-container/index.tsx CHANGED
@@ -3,11 +3,21 @@ import { MessageType } from '@/constants/chat';
3
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
4
  import { useSelectUserInfo } from '@/hooks/userSettingHook';
5
  import { IReference, Message } from '@/interfaces/database/chat';
6
- import { Avatar, Button, Flex, Input, List, Popover, Space } from 'antd';
 
 
 
 
 
 
 
 
 
7
  import classNames from 'classnames';
8
  import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
9
  import reactStringReplace from 'react-string-replace';
10
  import {
 
11
  useFetchConversationOnMount,
12
  useGetFileIcon,
13
  useSendMessage,
@@ -15,7 +25,9 @@ import {
15
 
16
  import Image from '@/components/image';
17
  import NewDocumentLink from '@/components/new-document-link';
 
18
  import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
 
19
  import { InfoCircleOutlined } from '@ant-design/icons';
20
  import Markdown from 'react-markdown';
21
  import { visitParents } from 'unist-util-visit-parents';
@@ -41,15 +53,24 @@ const rehypeWrapReference = () => {
41
  const MessageItem = ({
42
  item,
43
  reference,
 
44
  }: {
45
  item: Message;
46
  reference: IReference;
 
47
  }) => {
48
  const userInfo = useSelectUserInfo();
49
  const fileThumbnails = useSelectFileThumbnails();
50
 
51
  const isAssistant = item.role === MessageType.Assistant;
52
 
 
 
 
 
 
 
 
53
  const getPopoverContent = useCallback(
54
  (chunkIndex: number) => {
55
  const chunks = reference?.chunks ?? [];
@@ -83,16 +104,19 @@ const MessageItem = ({
83
  {documentId && (
84
  <Flex gap={'middle'}>
85
  <img src={fileThumbnails[documentId]} alt="" />
86
- <NewDocumentLink documentId={documentId}>
 
 
 
87
  {document?.doc_name}
88
- </NewDocumentLink>
89
  </Flex>
90
  )}
91
  </Space>
92
  </Flex>
93
  );
94
  },
95
- [reference, fileThumbnails],
96
  );
97
 
98
  const renderReference = useCallback(
@@ -191,6 +215,8 @@ const ChatContainer = () => {
191
  addNewestConversation,
192
  } = useFetchConversationOnMount();
193
  const { sendMessage } = useSendMessage();
 
 
194
 
195
  const loading = useOneNamespaceEffectsLoading('chatModel', [
196
  'completeConversation',
@@ -210,41 +236,56 @@ const ChatContainer = () => {
210
  };
211
 
212
  return (
213
- <Flex flex={1} className={styles.chatContainer} vertical>
214
- <Flex flex={1} vertical className={styles.messageContainer}>
215
- <div>
216
- {conversation?.message?.map((message) => {
217
- const assistantMessages = conversation?.message
218
- ?.filter((x) => x.role === MessageType.Assistant)
219
- .slice(1);
220
- const referenceIndex = assistantMessages.findIndex(
221
- (x) => x.id === message.id,
222
- );
223
- const reference = conversation.reference[referenceIndex];
224
- return (
225
- <MessageItem
226
- key={message.id}
227
- item={message}
228
- reference={reference}
229
- ></MessageItem>
230
- );
231
- })}
232
- </div>
233
- <div ref={ref} />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  </Flex>
235
- <Input
236
- size="large"
237
- placeholder="Message Resume Assistant..."
238
- value={value}
239
- suffix={
240
- <Button type="primary" onClick={handlePressEnter} loading={loading}>
241
- Send
242
- </Button>
243
- }
244
- onPressEnter={handlePressEnter}
245
- onChange={handleInputChange}
246
- />
247
- </Flex>
248
  );
249
  };
250
 
 
3
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
4
  import { useSelectUserInfo } from '@/hooks/userSettingHook';
5
  import { IReference, Message } from '@/interfaces/database/chat';
6
+ import {
7
+ Avatar,
8
+ Button,
9
+ Drawer,
10
+ Flex,
11
+ Input,
12
+ List,
13
+ Popover,
14
+ Space,
15
+ } from 'antd';
16
  import classNames from 'classnames';
17
  import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
18
  import reactStringReplace from 'react-string-replace';
19
  import {
20
+ useClickDrawer,
21
  useFetchConversationOnMount,
22
  useGetFileIcon,
23
  useSendMessage,
 
25
 
26
  import Image from '@/components/image';
27
  import NewDocumentLink from '@/components/new-document-link';
28
+ import DocumentPreviewer from '@/components/pdf-previewer';
29
  import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
30
+ import { IChunk } from '@/interfaces/database/knowledge';
31
  import { InfoCircleOutlined } from '@ant-design/icons';
32
  import Markdown from 'react-markdown';
33
  import { visitParents } from 'unist-util-visit-parents';
 
53
  const MessageItem = ({
54
  item,
55
  reference,
56
+ clickDocumentButton,
57
  }: {
58
  item: Message;
59
  reference: IReference;
60
+ clickDocumentButton: (documentId: string, chunk: IChunk) => void;
61
  }) => {
62
  const userInfo = useSelectUserInfo();
63
  const fileThumbnails = useSelectFileThumbnails();
64
 
65
  const isAssistant = item.role === MessageType.Assistant;
66
 
67
+ const handleDocumentButtonClick = useCallback(
68
+ (documentId: string, chunk: IChunk) => () => {
69
+ clickDocumentButton(documentId, chunk);
70
+ },
71
+ [clickDocumentButton],
72
+ );
73
+
74
  const getPopoverContent = useCallback(
75
  (chunkIndex: number) => {
76
  const chunks = reference?.chunks ?? [];
 
104
  {documentId && (
105
  <Flex gap={'middle'}>
106
  <img src={fileThumbnails[documentId]} alt="" />
107
+ <Button
108
+ type="link"
109
+ onClick={handleDocumentButtonClick(documentId, chunkItem)}
110
+ >
111
  {document?.doc_name}
112
+ </Button>
113
  </Flex>
114
  )}
115
  </Space>
116
  </Flex>
117
  );
118
  },
119
+ [reference, fileThumbnails, handleDocumentButtonClick],
120
  );
121
 
122
  const renderReference = useCallback(
 
215
  addNewestConversation,
216
  } = useFetchConversationOnMount();
217
  const { sendMessage } = useSendMessage();
218
+ const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
219
+ useClickDrawer();
220
 
221
  const loading = useOneNamespaceEffectsLoading('chatModel', [
222
  'completeConversation',
 
236
  };
237
 
238
  return (
239
+ <>
240
+ <Flex flex={1} className={styles.chatContainer} vertical>
241
+ <Flex flex={1} vertical className={styles.messageContainer}>
242
+ <div>
243
+ {conversation?.message?.map((message) => {
244
+ const assistantMessages = conversation?.message
245
+ ?.filter((x) => x.role === MessageType.Assistant)
246
+ .slice(1);
247
+ const referenceIndex = assistantMessages.findIndex(
248
+ (x) => x.id === message.id,
249
+ );
250
+ const reference = conversation.reference[referenceIndex];
251
+ return (
252
+ <MessageItem
253
+ key={message.id}
254
+ item={message}
255
+ reference={reference}
256
+ clickDocumentButton={clickDocumentButton}
257
+ ></MessageItem>
258
+ );
259
+ })}
260
+ </div>
261
+ <div ref={ref} />
262
+ </Flex>
263
+ <Input
264
+ size="large"
265
+ placeholder="Message Resume Assistant..."
266
+ value={value}
267
+ suffix={
268
+ <Button type="primary" onClick={handlePressEnter} loading={loading}>
269
+ Send
270
+ </Button>
271
+ }
272
+ onPressEnter={handlePressEnter}
273
+ onChange={handleInputChange}
274
+ />
275
  </Flex>
276
+ <Drawer
277
+ title="Document Previewer"
278
+ onClose={hideModal}
279
+ open={visible}
280
+ width={'50vw'}
281
+ >
282
+ <DocumentPreviewer
283
+ documentId={documentId}
284
+ chunk={selectedChunk}
285
+ visible={visible}
286
+ ></DocumentPreviewer>
287
+ </Drawer>
288
+ </>
289
  );
290
  };
291
 
web/src/pages/chat/hooks.ts CHANGED
@@ -4,6 +4,7 @@ import { fileIconMap } from '@/constants/common';
4
  import { useSetModalState } from '@/hooks/commonHooks';
5
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
6
  import { IConversation, IDialog } from '@/interfaces/database/chat';
 
7
  import { getFileExtension } from '@/utils';
8
  import omit from 'lodash/omit';
9
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -662,4 +663,28 @@ export const useRenameConversation = () => {
662
  };
663
  };
664
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
665
  //#endregion
 
4
  import { useSetModalState } from '@/hooks/commonHooks';
5
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
6
  import { IConversation, IDialog } from '@/interfaces/database/chat';
7
+ import { IChunk } from '@/interfaces/database/knowledge';
8
  import { getFileExtension } from '@/utils';
9
  import omit from 'lodash/omit';
10
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
 
663
  };
664
  };
665
 
666
+ export const useClickDrawer = () => {
667
+ const { visible, showModal, hideModal } = useSetModalState();
668
+ const [selectedChunk, setSelectedChunk] = useState<IChunk>({} as IChunk);
669
+ const [documentId, setDocumentId] = useState<string>('');
670
+
671
+ const clickDocumentButton = useCallback(
672
+ (documentId: string, chunk: IChunk) => {
673
+ showModal();
674
+ setSelectedChunk(chunk);
675
+ setDocumentId(documentId);
676
+ },
677
+ [showModal],
678
+ );
679
+
680
+ return {
681
+ clickDocumentButton,
682
+ visible,
683
+ showModal,
684
+ hideModal,
685
+ selectedChunk,
686
+ documentId,
687
+ };
688
+ };
689
+
690
  //#endregion
web/src/pages/knowledge/index.tsx CHANGED
@@ -50,13 +50,7 @@ const Knowledge = () => {
50
  </ModalManager>
51
  </Space>
52
  </div>
53
- <Flex
54
- gap="large"
55
- wrap="wrap"
56
- flex={1}
57
- // justify="center"
58
- className={styles.knowledgeCardContainer}
59
- >
60
  {list.length > 0 ? (
61
  list.map((item: any) => {
62
  return <KnowledgeCard item={item} key={item.name}></KnowledgeCard>;
 
50
  </ModalManager>
51
  </Space>
52
  </div>
53
+ <Flex gap={'large'} wrap="wrap" className={styles.knowledgeCardContainer}>
 
 
 
 
 
 
54
  {list.length > 0 ? (
55
  list.map((item: any) => {
56
  return <KnowledgeCard item={item} key={item.name}></KnowledgeCard>;
web/src/utils/documentUtils.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { IChunk } from '@/interfaces/database/knowledge';
2
+ import { v4 as uuid } from 'uuid';
3
+
4
+ export const buildChunkHighlights = (selectedChunk: IChunk) => {
5
+ return Array.isArray(selectedChunk?.positions) &&
6
+ selectedChunk.positions.every((x) => Array.isArray(x))
7
+ ? selectedChunk?.positions?.map((x) => {
8
+ const actualPositions = x.map((y, index) =>
9
+ index !== 0 ? y / 0.7 : y,
10
+ );
11
+ const boundingRect = {
12
+ width: 849,
13
+ height: 1200,
14
+ x1: actualPositions[1],
15
+ x2: actualPositions[2],
16
+ y1: actualPositions[3],
17
+ y2: actualPositions[4],
18
+ };
19
+ return {
20
+ id: uuid(),
21
+ comment: {
22
+ text: '',
23
+ emoji: '',
24
+ },
25
+ content: { text: selectedChunk.content_with_weight },
26
+ position: {
27
+ boundingRect: boundingRect,
28
+ rects: [boundingRect],
29
+ pageNumber: x[0],
30
+ },
31
+ };
32
+ })
33
+ : [];
34
+ };