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.tsx137
1 files changed, 104 insertions, 33 deletions
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index b22f2455e..baa4ad521 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -34,6 +34,11 @@ import './ChatBox.scss';
import MessageComponentBox from './MessageComponent';
import { ProgressBar } from './ProgressBar';
import { RichTextField } from '../../../../../fields/RichTextField';
+import { VideoBox } from '../../VideoBox';
+import { AudioBox } from '../../AudioBox';
+import { DiagramBox } from '../../DiagramBox';
+import { ImageField } from '../../../../../fields/URLField';
+import { DashUploadUtils } from '../../../../../server/DashUploadUtils';
dotenv.config();
@@ -402,13 +407,15 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
*/
@action
createDocInDash = async (doc_type: string, data: string | undefined, options: DocumentOptions, id: string) => {
- let doc;
+ let doc: Doc;
switch (doc_type.toLowerCase()) {
case 'text':
doc = Docs.Create.TextDocument(data || '', options);
break;
case 'image':
+ console.log('imageURL: ' + data);
+ //DashUploadUtils.UploadImage(data!);
doc = Docs.Create.ImageDocument(data || '', options);
break;
case 'pdf':
@@ -417,6 +424,13 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
case 'video':
doc = Docs.Create.VideoDocument(data || '', options);
break;
+ case 'mermaid_diagram':
+ doc = Docs.Create.DiagramDocument(data, options);
+ DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {
+ const firstView = Array.from(doc[DocViews])[0] as DocumentView;
+ (firstView.ComponentView as DiagramBox)?.renderMermaid?.(data!);
+ });
+ break;
case 'audio':
doc = Docs.Create.AudioDocument(data || '', options);
break;
@@ -426,12 +440,10 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
case 'equation':
doc = Docs.Create.EquationDocument(data || '', options);
break;
- case 'functionplot':
case 'function_plot':
doc = Docs.Create.FunctionPlotDocument([], options);
break;
case 'dataviz':
- case 'data_viz':
const { fileUrl, id } = await Networking.PostToServer('/createCSV', {
filename: (options.title as string).replace(/\s+/g, '') + '.csv',
data: data,
@@ -467,12 +479,13 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
if (foundChunk) {
// Handle media chunks specifically
- if (foundChunk.chunkType === CHUNK_TYPE.MEDIA) {
- const directMatchSegment = this.getDirectMatchingSegment(doc, citation.direct_text || '');
- if (directMatchSegment) {
+ if (doc.ai_type == 'video' || doc.ai_type == 'audio') {
+ const directMatchSegmentStart = this.getDirectMatchingSegmentStart(doc, citation.direct_text || '', foundChunk.indexes || []);
+
+ if (directMatchSegmentStart) {
// Navigate to the segment's start time in the media player
- await this.goToMediaTimestamp(doc, directMatchSegment.start_time);
+ await this.goToMediaTimestamp(doc, directMatchSegmentStart, doc.ai_type);
} else {
console.error('No direct matching segment found for the citation.');
}
@@ -485,29 +498,53 @@ 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
+ getDirectMatchingSegmentStart = (doc: Doc, citationText: string, indexesOfSegments: string[]): number => {
+ const originalSegments = JSON.parse(StrCast(doc.original_segments!)).map((segment: any, index: number) => ({
+ index: index.toString(),
+ text: segment.text,
+ start: segment.start,
+ end: segment.end,
+ }));
- if (!Array.isArray(mediaMetadata) || mediaMetadata.length === 0) {
- return null;
+ if (!Array.isArray(originalSegments) || originalSegments.length === 0 || !Array.isArray(indexesOfSegments)) {
+ return 0;
}
- 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
+ // Create itemsToSearch array based on indexesOfSegments
+ const itemsToSearch = indexesOfSegments.map((indexStr: string) => {
+ const index = parseInt(indexStr, 10);
+ const segment = originalSegments[index];
+ return { text: segment.text, start: segment.start };
+ });
+
+ console.log('Constructed itemsToSearch:', itemsToSearch);
+
+ // Helper function to calculate word overlap score
+ const calculateWordOverlap = (text1: string, text2: string): number => {
+ const words1 = new Set(text1.toLowerCase().split(/\W+/));
+ const words2 = new Set(text2.toLowerCase().split(/\W+/));
+ const intersection = new Set([...words1].filter(word => words2.has(word)));
+ return intersection.size / Math.max(words1.size, words2.size); // Jaccard similarity
+ };
+
+ // Search for the best matching segment
+ let bestMatchStart = 0;
+ let bestScore = 0;
+
+ console.log(`Searching for best match for query: "${citationText}"`);
+ itemsToSearch.forEach(item => {
+ const score = calculateWordOverlap(citationText, item.text);
+ console.log(`Comparing query to segment: "${item.text}" | Score: ${score}`);
+ if (score > bestScore) {
+ bestScore = score;
+ bestMatchStart = item.start;
}
- }
+ });
- return null; // No match found
+ console.log('Best match found with score:', bestScore, '| Start time:', bestMatchStart);
+
+ // Return the start time of the best match
+ return bestMatchStart;
};
/**
@@ -515,15 +552,20 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
* @param doc The document containing the media file.
* @param timestamp The timestamp to navigate to.
*/
- goToMediaTimestamp = async (doc: Doc, timestamp: number) => {
+ goToMediaTimestamp = async (doc: Doc, timestamp: number, type: 'video' | 'audio') => {
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);
-
+ if (type == 'video') {
+ DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {
+ const firstView = Array.from(doc[DocViews])[0] as DocumentView;
+ (firstView.ComponentView as VideoBox)?.Seek?.(timestamp);
+ });
+ } else {
+ DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {
+ const firstView = Array.from(doc[DocViews])[0] as DocumentView;
+ (firstView.ComponentView as AudioBox)?.playFrom?.(timestamp);
+ });
+ }
console.log(`Navigated to timestamp: ${timestamp}s in document ${doc.id}`);
} catch (error) {
console.error('Error navigating to media timestamp:', error);
@@ -538,6 +580,32 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
*/
handleOtherChunkTypes = (foundChunk: SimplifiedChunk, citation: Citation, doc: Doc) => {
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;
+ }
+ if (foundChunk.startPage === undefined || foundChunk.endPage === undefined) {
+ DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {});
+ 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);
@@ -686,7 +754,10 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
.map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
.map(d => DocCast(d?.annotationOn, d))
.filter(d => d)
- .filter(d => d.ai_doc_id)
+ .filter(d => {
+ console.log(d.ai_doc_id);
+ return d.ai_doc_id;
+ })
.map(d => StrCast(d.ai_doc_id));
}