diff options
Diffstat (limited to 'src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx')
| -rw-r--r-- | src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx | 144 |
1 files changed, 90 insertions, 54 deletions
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx index a61705250..b22f2455e 100644 --- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx +++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx @@ -454,67 +454,31 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {}); }; - /** - * Event handler to manage citations click in the message components. - * @param citation The citation object clicked by the user. - */ @action - handleCitationClick = (citation: Citation) => { + handleCitationClick = async (citation: Citation) => { const currentLinkedDocs: Doc[] = this.linkedDocs; const chunkId = citation.chunk_id; - // Loop through the linked documents to find the matching chunk and handle its display for (const doc of currentLinkedDocs) { if (doc.chunk_simpl) { const docChunkSimpl = JSON.parse(StrCast(doc.chunk_simpl)) as { chunks: SimplifiedChunk[] }; const foundChunk = docChunkSimpl.chunks.find(chunk => chunk.chunkId === chunkId); + if (foundChunk) { - // Handle different types of chunks (image, text, table, etc.) - switch (foundChunk.chunkType) { - case CHUNK_TYPE.IMAGE: - case CHUNK_TYPE.TABLE: - { - const values = foundChunk.location?.replace(/[[\]]/g, '').split(','); - - if (values?.length !== 4) { - console.error('Location string must contain exactly 4 numbers'); - return; - } - - const x1 = parseFloat(values[0]) * Doc.NativeWidth(doc); - const y1 = parseFloat(values[1]) * Doc.NativeHeight(doc) + foundChunk.startPage * Doc.NativeHeight(doc); - const x2 = parseFloat(values[2]) * Doc.NativeWidth(doc); - const y2 = parseFloat(values[3]) * Doc.NativeHeight(doc) + foundChunk.startPage * Doc.NativeHeight(doc); - - const annotationKey = Doc.LayoutFieldKey(doc) + '_annotations'; - - const existingDoc = DocListCast(doc[DocData][annotationKey]).find(d => d.citation_id === citation.citation_id); - const highlightDoc = existingDoc ?? this.createImageCitationHighlight(x1, y1, x2, y2, citation, annotationKey, doc); - - DocumentManager.Instance.showDocument(highlightDoc, { willZoomCentered: true }, () => {}); - } - break; - case CHUNK_TYPE.TEXT: - this.citationPopup = { text: citation.direct_text ?? 'No text available', visible: true }; - setTimeout(() => (this.citationPopup.visible = false), 3000); // Hide after 3 seconds - - DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => { - const firstView = Array.from(doc[DocViews])[0] as DocumentView; - (firstView.ComponentView as PDFBox)?.gotoPage?.(foundChunk.startPage); - (firstView.ComponentView as PDFBox)?.search?.(citation.direct_text ?? ''); - }); - break; - case CHUNK_TYPE.URL: - DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {}); - - break; - case CHUNK_TYPE.CSV: - DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {}); - break; - default: - console.error('Chunk type not recognized:', foundChunk.chunkType); - break; + // Handle media chunks specifically + if (foundChunk.chunkType === CHUNK_TYPE.MEDIA) { + const directMatchSegment = this.getDirectMatchingSegment(doc, citation.direct_text || ''); + + if (directMatchSegment) { + // Navigate to the segment's start time in the media player + await this.goToMediaTimestamp(doc, directMatchSegment.start_time); + } else { + console.error('No direct matching segment found for the citation.'); + } + } else { + // Handle other chunk types as before + this.handleOtherChunkTypes(foundChunk, citation, doc); } } } @@ -522,6 +486,78 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }; /** + * Finds the first segment with a direct match to the citation text. + * A match occurs if the segment's text is a subset of the citation's direct text or vice versa. + * @param doc The document containing media metadata. + * @param citationText The citation text to find a matching segment for. + * @returns The segment with the direct match or null if no match is found. + */ + getDirectMatchingSegment = (doc: Doc, citationText: string): { start_time: number; end_time: number; text: string } | null => { + const mediaMetadata = JSON.parse(StrCast(doc.segments)); // Assuming segments are stored in metadata + + if (!Array.isArray(mediaMetadata) || mediaMetadata.length === 0) { + return null; + } + + for (const segment of mediaMetadata) { + const segmentText = segment.text || ''; + // Check if the segment's text is a subset of the citation text or vice versa + if (citationText.includes(segmentText) || segmentText.includes(citationText)) { + return segment; // Return the first matching segment + } + } + + return null; // No match found + }; + + /** + * Navigates to the given timestamp in the media player. + * @param doc The document containing the media file. + * @param timestamp The timestamp to navigate to. + */ + goToMediaTimestamp = async (doc: Doc, timestamp: number) => { + try { + // Show the media document in the viewer + await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }); + + // Simulate navigation to the timestamp + const firstView = Array.from(doc[DocViews])[0] as DocumentView; + (firstView.ComponentView as any)?.gotoTimestamp?.(timestamp); + + console.log(`Navigated to timestamp: ${timestamp}s in document ${doc.id}`); + } catch (error) { + console.error('Error navigating to media timestamp:', error); + } + }; + + /** + * Handles non-media chunk types as before. + * @param foundChunk The chunk object. + * @param citation The citation object. + * @param doc The document containing the chunk. + */ + handleOtherChunkTypes = (foundChunk: SimplifiedChunk, citation: Citation, doc: Doc) => { + switch (foundChunk.chunkType) { + case CHUNK_TYPE.TEXT: + this.citationPopup = { text: citation.direct_text ?? 'No text available', visible: true }; + setTimeout(() => (this.citationPopup.visible = false), 3000); + + DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => { + const firstView = Array.from(doc[DocViews])[0] as DocumentView; + (firstView.ComponentView as PDFBox)?.gotoPage?.(foundChunk.startPage ?? 0); + (firstView.ComponentView as PDFBox)?.search?.(citation.direct_text ?? ''); + }); + break; + case CHUNK_TYPE.CSV: + case CHUNK_TYPE.URL: + DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }); + break; + default: + console.error('Unhandled chunk type:', foundChunk.chunkType); + break; + } + }; + /** * Creates an annotation highlight on a PDF document for image citations. * @param x1 X-coordinate of the top-left corner of the highlight. * @param y1 Y-coordinate of the top-left corner of the highlight. @@ -610,10 +646,10 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { // Observe changes to linked documents and handle document addition observe(this.linked_docs_to_add, change => { if (change.type === 'add') { - if (PDFCast(change.newValue.data)) { - this.addDocToVectorstore(change.newValue); - } else if (CsvCast(change.newValue.data)) { + if (CsvCast(change.newValue.data)) { this.addCSVForAnalysis(change.newValue); + } else { + this.addDocToVectorstore(change.newValue); } } else if (change.type === 'delete') { // Handle document removal |
