aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx')
-rw-r--r--src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx144
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