From ec0ab50aad9fbb55477476998c6932488b149f45 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 21 Jan 2025 12:03:44 -0500
Subject: trying to cleanup chatBox code and types

---
 .../nodes/chatbot/chatboxcomponents/ChatBox.tsx    | 472 +++++++++------------
 1 file changed, 197 insertions(+), 275 deletions(-)

(limited to 'src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx')

diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index b89498d7d..83b50c8c6 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -14,12 +14,12 @@ import OpenAI, { ClientOptions } from 'openai';
 import * as React from 'react';
 import { v4 as uuidv4 } from 'uuid';
 import { ClientUtils } from '../../../../../ClientUtils';
-import { Doc, DocListCast } from '../../../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../../../fields/Doc';
 import { DocData, DocViews } from '../../../../../fields/DocSymbols';
-import { CsvCast, DocCast, PDFCast, RTFCast, StrCast, NumCast } from '../../../../../fields/Types';
+import { CsvCast, DocCast, NumCast, PDFCast, RTFCast, StrCast } from '../../../../../fields/Types';
 import { Networking } from '../../../../Network';
 import { DocUtils } from '../../../../documents/DocUtils';
-import { DocumentType } from '../../../../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../../../../documents/DocumentTypes';
 import { Docs, DocumentOptions } from '../../../../documents/Documents';
 import { DocumentManager } from '../../../../util/DocumentManager';
 import { LinkManager } from '../../../../util/LinkManager';
@@ -33,7 +33,6 @@ import { Vectorstore } from '../vectorstore/Vectorstore';
 import './ChatBox.scss';
 import MessageComponentBox from './MessageComponent';
 import { ProgressBar } from './ProgressBar';
-import { RichTextField } from '../../../../../fields/RichTextField';
 
 dotenv.config();
 
@@ -45,17 +44,17 @@ dotenv.config();
 @observer
 export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // MobX observable properties to track UI state and data
-    @observable history: AssistantMessage[] = [];
-    @observable.deep current_message: AssistantMessage | undefined = undefined;
-    @observable isLoading: boolean = false;
-    @observable uploadProgress: number = 0;
-    @observable currentStep: string = '';
-    @observable expandedScratchpadIndex: number | null = null;
-    @observable inputValue: string = '';
-    @observable private linked_docs_to_add: ObservableSet = observable.set();
-    @observable private linked_csv_files: { filename: string; id: string; text: string }[] = [];
-    @observable private isUploadingDocs: boolean = false;
-    @observable private citationPopup: { text: string; visible: boolean } = { text: '', visible: false };
+    @observable private _history: AssistantMessage[] = [];
+    @observable.deep private _current_message: AssistantMessage | undefined = undefined;
+    @observable private _isLoading: boolean = false;
+    @observable private _uploadProgress: number = 0;
+    @observable private _currentStep: string = '';
+    @observable private _expandedScratchpadIndex: number | null = null;
+    @observable private _inputValue: string = '';
+    @observable private _linked_docs_to_add: ObservableSet = observable.set();
+    @observable private _linked_csv_files: { filename: string; id: string; text: string }[] = [];
+    @observable private _isUploadingDocs: boolean = false;
+    @observable private _citationPopup: { text: string; visible: boolean } = { text: '', visible: false };
 
     // Private properties for managing OpenAI API, vector store, agent, and UI elements
     private openai: OpenAI;
@@ -96,7 +95,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         // Reaction to update dataDoc when chat history changes
         reaction(
             () =>
-                this.history.map((msg: AssistantMessage) => ({
+                this._history.map((msg: AssistantMessage) => ({
                     role: msg.role,
                     content: msg.content,
                     follow_up_questions: msg.follow_up_questions,
@@ -115,20 +114,20 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      */
     @action
     addDocToVectorstore = async (newLinkedDoc: Doc) => {
-        this.uploadProgress = 0;
-        this.currentStep = 'Initializing...';
-        this.isUploadingDocs = true;
+        this._uploadProgress = 0;
+        this._currentStep = 'Initializing...';
+        this._isUploadingDocs = true;
 
         try {
             // Add the document to the vectorstore
             await this.vectorstore.addAIDoc(newLinkedDoc, this.updateProgress);
         } catch (error) {
             console.error('Error uploading document:', error);
-            this.currentStep = 'Error during upload';
+            this._currentStep = 'Error during upload';
         } finally {
-            this.isUploadingDocs = false;
-            this.uploadProgress = 0;
-            this.currentStep = '';
+            this._isUploadingDocs = false;
+            this._uploadProgress = 0;
+            this._currentStep = '';
         }
     };
 
@@ -139,8 +138,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      */
     @action
     updateProgress = (progress: number, step: string) => {
-        this.uploadProgress = progress;
-        this.currentStep = step;
+        this._uploadProgress = progress;
+        this._currentStep = step;
     };
 
     /**
@@ -177,7 +176,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             const csvId = id ?? uuidv4();
 
             // Add CSV details to linked files
-            this.linked_csv_files.push({
+            this._linked_csv_files.push({
                 filename: CsvCast(newLinkedDoc.data).url.pathname,
                 id: csvId,
                 text: csvData,
@@ -199,7 +198,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      */
     @action
     toggleToolLogs = (index: number) => {
-        this.expandedScratchpadIndex = this.expandedScratchpadIndex === index ? null : index;
+        this._expandedScratchpadIndex = this._expandedScratchpadIndex === index ? null : index;
     };
 
     /**
@@ -258,7 +257,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @action
     askGPT = async (event: React.FormEvent): Promise<void> => {
         event.preventDefault();
-        this.inputValue = '';
+        this._inputValue = '';
 
         // Extract the user's message
         const textInput = (event.currentTarget as HTMLFormElement).elements.namedItem('messageInput') as HTMLInputElement;
@@ -268,13 +267,13 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             try {
                 textInput.value = '';
                 // Add the user's message to the history
-                this.history.push({
+                this._history.push({
                     role: ASSISTANT_ROLE.USER,
                     content: [{ index: 0, type: TEXT_TYPE.NORMAL, text: trimmedText, citation_ids: null }],
                     processing_info: [],
                 });
-                this.isLoading = true;
-                this.current_message = {
+                this._isLoading = true;
+                this._current_message = {
                     role: ASSISTANT_ROLE.ASSISTANT,
                     content: [],
                     citations: [],
@@ -284,9 +283,9 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 // Define callbacks for real-time processing updates
                 const onProcessingUpdate = (processingUpdate: ProcessingInfo[]) => {
                     runInAction(() => {
-                        if (this.current_message) {
-                            this.current_message = {
-                                ...this.current_message,
+                        if (this._current_message) {
+                            this._current_message = {
+                                ...this._current_message,
                                 processing_info: processingUpdate,
                             };
                         }
@@ -296,9 +295,9 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
                 const onAnswerUpdate = (answerUpdate: string) => {
                     runInAction(() => {
-                        if (this.current_message) {
-                            this.current_message = {
-                                ...this.current_message,
+                        if (this._current_message) {
+                            this._current_message = {
+                                ...this._current_message,
                                 content: [{ text: answerUpdate, type: TEXT_TYPE.NORMAL, index: 0, citation_ids: [] }],
                             };
                         }
@@ -310,22 +309,22 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
                 // Update the history with the final assistant message
                 runInAction(() => {
-                    if (this.current_message) {
-                        this.history.push({ ...finalMessage });
-                        this.current_message = undefined;
-                        this.dataDoc.data = JSON.stringify(this.history);
+                    if (this._current_message) {
+                        this._history.push({ ...finalMessage });
+                        this._current_message = undefined;
+                        this.dataDoc.data = JSON.stringify(this._history);
                     }
                 });
             } catch (err) {
                 console.error('Error:', err);
                 // Handle error in processing
-                this.history.push({
+                this._history.push({
                     role: ASSISTANT_ROLE.ASSISTANT,
                     content: [{ index: 0, type: TEXT_TYPE.ERROR, text: 'Sorry, I encountered an error while processing your request.', citation_ids: null }],
                     processing_info: [],
                 });
             } finally {
-                this.isLoading = false;
+                this._isLoading = false;
                 this.scrollToBottom();
             }
         }
@@ -339,8 +338,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      */
     @action
     updateMessageCitations = (index: number, citations: Citation[]) => {
-        if (this.history[index]) {
-            this.history[index].citations = citations;
+        if (this._history[index]) {
+            this._history[index].citations = citations;
         }
     };
 
@@ -381,17 +380,14 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      * @param data The CSV data content.
      */
     @action
-    createCSVInDash = async (url: string, title: string, id: string, data: string) => {
-        const doc = DocCast(await DocUtils.DocumentFromType('csv', url, { title: title, text: RTFCast(data) }));
-
-        const linkDoc = Docs.Create.LinkDocument(this.Document, doc);
-        LinkManager.Instance.addLink(linkDoc);
-
-        doc && this._props.addDocument?.(doc);
-        await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {});
-
-        this.addCSVForAnalysis(doc, id);
-    };
+    createCSVInDash = (url: string, title: string, id: string, data: string) =>
+        DocUtils.DocumentFromType('csv', url, { title: title, text: RTFCast(data) }).then(doc => {
+            if (doc) {
+                LinkManager.Instance.addLink(Docs.Create.LinkDocument(this.Document, doc));
+                this._props.addDocument?.(doc);
+                DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {}).then(() => this.addCSVForAnalysis(doc, id));
+            }
+        });
 
     /**
      * Creates a text document in the dashboard and adds it for analysis.
@@ -401,40 +397,35 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      * @param id The unique ID for the document.
      */
     @action
-    private createCollectionWithChildren = async (data: any, insideCol: boolean): Promise<Doc[]> => {
-        // Create an array of promises for each document
-        const childDocPromises = data.map(async doc => {
-            const parsedDoc = doc;
-            if (parsedDoc.doc_type !== 'collection') {
-                // Handle non-collection documents
-                return await this.whichDoc(parsedDoc.doc_type, parsedDoc.data, { backgroundColor: parsedDoc.backgroundColor, _width: parsedDoc.width, _height: parsedDoc.height }, parsedDoc.id, insideCol);
-            } else {
-                // Recursively process collections
-                const nestedDocs = await this.createCollectionWithChildren(parsedDoc.data, true);
-                const collectionOptions: DocumentOptions = {
-                    title: parsedDoc.title,
-                    backgroundColor: parsedDoc.backgroundColor,
-                    _width: parsedDoc.width,
-                    _height: parsedDoc.height,
-                    _layout_fitWidth: true,
-                    _freeform_backgroundGrid: true,
-                };
-                const collectionDoc = DocCast(Docs.Create.FreeformDocument(nestedDocs, collectionOptions));
-                return collectionDoc;
-            }
-        });
-
-        // Await all child document creations concurrently
-        const nestedResults = await Promise.all(childDocPromises);
-        // Flatten any nested arrays from recursive collection calls
-        const childDocs = nestedResults.flat() as Doc[];
-        childDocs.forEach(doc => {
-            console.log(DocCast(doc));
-            console.log(DocCast(doc)[DocData].data);
-            console.log(DocCast(doc)[DocData].data);
-        });
-        return childDocs;
-    };
+    private createCollectionWithChildren = (data: { doc_type: string; id: string; data: any; title: string; width: number; height: number; backgroundColor: string }[], insideCol: boolean): Promise<Doc[]> =>
+        Promise.all(
+            data.map(doc =>
+                doc.doc_type !== 'collection' // Handle non-collection documents
+                    ? this.whichDoc(doc.doc_type, doc.data, { backgroundColor: doc.backgroundColor, _width: doc.width, _height: doc.height }, doc.id, insideCol)
+                    : // Recursively process collections
+                        this.createCollectionWithChildren(doc.data, true).then(nestedDocs =>
+                            Docs.Create.FreeformDocument(nestedDocs, {
+                                title: doc.title,
+                                backgroundColor: doc.backgroundColor,
+                                _width: doc.width,
+                                _height: doc.height,
+                                _layout_fitWidth: true,
+                                _freeform_backgroundGrid: true,
+                            })
+                        )
+            )
+            .flat() // prettier-ignore
+        ).then(childDocs => childDocs.filter(doc => doc).map(doc => doc!));
+    // .then(nestedResults => {
+    //     // Flatten any nested arrays from recursive collection calls
+    //     const childDocs = nestedResults.flat() as Doc[];
+    //     childDocs.forEach(doc => {
+    //         console.log(DocCast(doc));
+    //         console.log(DocCast(doc)[DocData].data);
+    //         console.log(DocCast(doc)[DocData].data);
+    //     });
+    //     return childDocs;
+    // });
 
     // @action
     // createSingleFlashcard = (data: any, options: DocumentOptions) => {
@@ -442,101 +433,53 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // }
 
     @action
-    whichDoc = async (doc_type: string, data: string, options: DocumentOptions, id: string, insideCol: boolean): Promise<Doc> => {
-        let doc;
-        switch (doc_type) {
-            case 'text':
-                doc = DocCast(Docs.Create.TextDocument(data, options));
-                break;
-            case 'flashcard':
-                doc = this.createFlashcard(data, options);
-                break;
-            case 'deck':
-                doc = this.createDeck(data, options);
-                break;
-            case 'image':
-                doc = DocCast(Docs.Create.ImageDocument(data, options));
-                break;
-            case 'equation':
-                doc = DocCast(Docs.Create.EquationDocument(data, options));
-                break;
-            case 'noteboard':
-                doc = DocCast(Docs.Create.NoteTakingDocument([], options));
-                break;
-            case 'simulation':
-                doc = DocCast(Docs.Create.SimulationDocument(options));
-                break;
-            case 'collection': {
-                const arr = await this.createCollectionWithChildren(data, true);
-                options._layout_fitWidth = true;
-                options._freeform_backgroundGrid = true;
-                if (options.type_collection == 'tree') {
-                    doc = DocCast(Docs.Create.TreeDocument(arr, options));
-                } else if (options.type_collection == 'masonry') {
-                    doc = DocCast(Docs.Create.MasonryDocument(arr, options));
-                } else if (options.type_collection == 'card') {
-                    doc = DocCast(Docs.Create.CardDeckDocument(arr, options));
-                } else if (options.type_collection == 'carousel') {
-                    doc = DocCast(Docs.Create.CarouselDocument(arr, options));
-                } else if (options.type_collection == '3d-carousel') {
-                    doc = DocCast(Docs.Create.Carousel3DDocument(arr, options));
-                } else if (options.type_collection == 'multicolumn') {
-                    doc = DocCast(Docs.Create.CarouselDocument(arr, options));
-                } else {
-                    doc = DocCast(Docs.Create.FreeformDocument(arr, options));
-                }
-                break;
+    whichDoc = (doc_type: string, data: string, options: DocumentOptions, id: string, insideCol: boolean): Promise<Opt<Doc>> =>
+        (async () => {
+            switch (doc_type) {
+                case 'text':       return Docs.Create.TextDocument(data, options);
+                case 'flashcard':  return this.createFlashcard(data, options);
+                case 'deck':       return this.createDeck(data, options);
+                case 'image':      return Docs.Create.ImageDocument(data, options);
+                case 'equation':   return Docs.Create.EquationDocument(data, options);
+                case 'noteboard':  return Docs.Create.NoteTakingDocument([], options);
+                case 'simulation': return Docs.Create.SimulationDocument(options);
+                case 'collection': return this.createCollectionWithChildren(data as any, true).
+                    then((arr, collOpts = { ...options, _layout_fitWidth: true, _freeform_backgroundGrid: true }) =>
+                        (() => {
+                            switch (options.type_collection) {
+                                case CollectionViewType.Tree:        return Docs.Create.TreeDocument(arr, collOpts);
+                                case CollectionViewType.Masonry:     return Docs.Create.MasonryDocument(arr, collOpts);
+                                case CollectionViewType.Card:        return Docs.Create.CardDeckDocument(arr, collOpts);
+                                case CollectionViewType.Carousel:    return Docs.Create.CarouselDocument(arr, collOpts);
+                                case CollectionViewType.Carousel3D:  return Docs.Create.Carousel3DDocument(arr, collOpts);
+                                case CollectionViewType.Multicolumn: return Docs.Create.CarouselDocument(arr, collOpts);
+                                default:                             return Docs.Create.FreeformDocument(arr, collOpts);
+                            }
+                        })()
+                    );
+                case 'web':        return Docs.Create.WebDocument(data, { ...options, data_useCors: true });
+                case 'comparison': return this.createComparison(data, options);
+                case 'diagram':    return Docs.Create.DiagramDocument(options);
+                case 'audio':      return Docs.Create.AudioDocument(data, options);
+                case 'map':        return Docs.Create.MapDocument([], options);
+                case 'screengrab': return Docs.Create.ScreenshotDocument(options);
+                case 'webcam':     return Docs.Create.WebCamDocument('', options);
+                case 'button':     return Docs.Create.ButtonDocument(options);
+                case 'script':     return Docs.Create.ScriptingDocument(null, options);
+                case 'dataviz':    return Docs.Create.DataVizDocument('/users/rz/Downloads/addresses.csv', options);
+                case 'chat':       return Docs.Create.ChatDocument(options);
+                case 'trail':      return Docs.Create.PresDocument(options);
+                case 'tab':        return Docs.Create.FreeformDocument([], options);
+                case 'slide':      return Docs.Create.TreeDocument([], options);
+                default:           return Docs.Create.TextDocument(data, options);
+            } // prettier-ignore
+        })().then(doc => {
+            if (doc) {
+                doc.x = NumCast((options.x as number) ?? 0) + (insideCol ? 0 : NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc.width)) + 100;
+                doc.y = NumCast(options.y as number) + (insideCol ? 0 : NumCast(this.layoutDoc.y));
             }
-            case 'web':
-                options.data_useCors = true;
-                doc = DocCast(Docs.Create.WebDocument(data, options));
-                break;
-            case 'comparison':
-                doc = this.createComparison(data, options);
-                break;
-            case 'diagram':
-                doc = Docs.Create.DiagramDocument(options);
-                break;
-            case 'audio':
-                doc = Docs.Create.AudioDocument(data, options);
-                break;
-            case 'map':
-                doc = Docs.Create.MapDocument([], options);
-                break;
-            case 'screengrab':
-                doc = Docs.Create.ScreenshotDocument(options);
-                break;
-            case 'webcam':
-                doc = Docs.Create.WebCamDocument('', options);
-                break;
-            case 'button':
-                doc = Docs.Create.ButtonDocument(options);
-                break;
-            case 'script':
-                doc = Docs.Create.ScriptingDocument(null, options);
-                break;
-            case 'dataviz':
-                doc = Docs.Create.DataVizDocument('/users/rz/Downloads/addresses.csv', options);
-                break;
-            case 'chat':
-                doc = Docs.Create.ChatDocument(options);
-                break;
-            case 'trail':
-                doc = Docs.Create.PresDocument(options);
-                break;
-            case 'tab':
-                doc = Docs.Create.FreeformDocument([], options);
-                break;
-            case 'slide':
-                doc = Docs.Create.TreeDocument([], options);
-                break;
-            default:
-                doc = DocCast(Docs.Create.TextDocument(data, options));
-        }
-        doc!.x = NumCast(options.x ?? 0) + (insideCol ? 0 : NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc.width)) + 100;
-        doc!.y = NumCast(options.y) + (insideCol ? 0 : NumCast(this.layoutDoc.y));
-        return doc;
-    };
+            return doc;
+        });
 
     /**
      * Creates a document in the dashboard.
@@ -548,57 +491,42 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      * @returns {Promise<void>} A promise that resolves once the document is created and displayed.
      */
     @action
-    createDocInDash = async (doc_type: string, data: string | undefined, options: DocumentOptions, id: string) => {
-        let doc;
-
-        switch (doc_type.toLowerCase()) {
-            case 'text':
-                doc = Docs.Create.TextDocument(data || '', options);
-                break;
-            case 'image':
-                doc = Docs.Create.ImageDocument(data || '', options);
-                break;
-            case 'pdf':
-                doc = Docs.Create.PdfDocument(data || '', options);
-                break;
-            case 'video':
-                doc = Docs.Create.VideoDocument(data || '', options);
-                break;
-            case 'audio':
-                doc = Docs.Create.AudioDocument(data || '', options);
-                break;
-            case 'web':
-                doc = Docs.Create.WebDocument(data || '', options);
-                break;
-            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,
-                });
-                doc = Docs.Create.DataVizDocument(fileUrl, { ...options, text: RTFCast(data) });
-                this.addCSVForAnalysis(doc, id);
-                break;
+    createDocInDash = (doc_type: string, data: string | undefined, options: DocumentOptions, id: string) => {
+        const linkAndShowDoc = (doc: Opt<Doc>) => {
+            if (doc) {
+                LinkManager.Instance.addLink(Docs.Create.LinkDocument(this.Document, doc));
+                this._props.addDocument?.(doc);
+                DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {});
             }
-            case 'chat':
-                doc = Docs.Create.ChatDocument(options);
-                break;
-            // Add more cases for other document types
-            default:
-                console.error('Unknown or unsupported document type:', doc_type);
-                return;
-        }
-        const linkDoc = Docs.Create.LinkDocument(this.Document, doc);
-        LinkManager.Instance.addLink(linkDoc);
-        doc && this._props.addDocument?.(doc);
-        await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {});
+        };
+        const doc = (() => {
+            switch (doc_type.toLowerCase()) {
+                case 'text':          return Docs.Create.TextDocument(data || '', options);
+                case 'image':         return Docs.Create.ImageDocument(data || '', options);
+                case 'pdf':           return Docs.Create.PdfDocument(data || '', options);
+                case 'video':         return Docs.Create.VideoDocument(data || '', options);
+                case 'audio':         return Docs.Create.AudioDocument(data || '', options);
+                case 'web':           return Docs.Create.WebDocument(data || '', options);
+                case 'equation':      return Docs.Create.EquationDocument(data || '', options);
+                case 'chat':          return Docs.Create.ChatDocument(options);
+                case 'functionplot':
+                case 'function_plot': return Docs.Create.FunctionPlotDocument([], options);
+                case 'dataviz':
+                case 'data_viz':      Networking.PostToServer('/createCSV', {
+                                                filename: (options.title as string).replace(/\s+/g, '') + '.csv',
+                                                data: data,
+                                            })?.then(({ fileUrl, id }) => {
+                                                const vdoc = Docs.Create.DataVizDocument(fileUrl, { ...options, text: RTFCast(data) });
+                                                this.addCSVForAnalysis(vdoc, id);
+                                                linkAndShowDoc(vdoc);
+                                            });
+                                      return undefined;
+                // Add more cases for other document types
+                default: console.error('Unknown or unsupported document type:', doc_type);
+                     return undefined;
+            } // prettier-ignore
+        })();
+        if (doc) linkAndShowDoc(doc);
     };
 
     /**
@@ -625,16 +553,13 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         }
 
         // Create a carousel to contain the flashcard deck
-        const carouselDoc = DocCast(
-            Docs.Create.CarouselDocument(flashcardDeck, {
-                title: options.title || 'Flashcard Deck',
-                _width: options._width || 300,
-                _height: options._height || 300,
-                _layout_fitWidth: false,
-                _layout_autoHeight: true,
-            })
-        );
-        return carouselDoc;
+        return Docs.Create.CarouselDocument(flashcardDeck, {
+            title: options.title || 'Flashcard Deck',
+            _width: options._width || 300,
+            _height: options._height || 300,
+            _layout_fitWidth: false,
+            _layout_autoHeight: true,
+        });
     };
 
     /**
@@ -662,8 +587,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             const side2 = Docs.Create.CenteredTextCreator(back.title, back.data, sideOptions);
 
             // Create the flashcard document with both sides
-            const flashcardDoc = DocCast(Docs.Create.FlashcardDocument(data.title, side1, side2, sideOptions));
-            return flashcardDoc;
+            return Docs.Create.FlashcardDocument(data.title, side1, side2, sideOptions);
         }
     };
 
@@ -675,15 +599,14 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      * @returns {Doc} The created comparison document.
      */
     @action
-    createComparison = (doc: any, options: any) => {
-        const comp = Docs.Create.ComparisonDocument(options.title, { _width: options.width, _height: options.height | 300, backgroundColor: options.backgroundColor });
-        const [left, right] = doc;
-        const docLeft = DocCast(Docs.Create.TextDocument(left.data, { backgroundColor: left.backgroundColor, _width: left.width, _height: left.height }));
-        const docRight = DocCast(Docs.Create.TextDocument(right.data, { backgroundColor: right.backgroundColor, _width: right.width, _height: right.height }));
-        comp[DocData].data_back = docLeft;
-        comp[DocData].data_front = docRight;
-        return comp;
-    };
+    createComparison = (doc: { left: any; right: any }, options: any) =>
+        Docs.Create.ComparisonDocument(options.title, {
+            data_back: Docs.Create.TextDocument(doc.left.data, { backgroundColor: doc.left.backgroundColor, _width: doc.left.width, _height: doc.left.height }),
+            data_front: Docs.Create.TextDocument(doc.right.data, { backgroundColor: doc.right.backgroundColor, _width: doc.right.width, _height: doc.right.height }),
+            _width: options.width,
+            _height: options.height | 300,
+            backgroundColor: options.backgroundColor,
+        });
 
     /**
      * Event handler to manage citations click in the message components.
@@ -692,7 +615,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @action
     handleCitationClick = (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
@@ -727,8 +649,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                             }
                             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
+                            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;
@@ -796,7 +718,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             try {
                 const storedHistory = JSON.parse(StrCast(this.dataDoc.data));
                 runInAction(() => {
-                    this.history.push(
+                    this._history.push(
                         ...storedHistory.map((msg: AssistantMessage) => ({
                             role: msg.role,
                             content: msg.content,
@@ -811,7 +733,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         } else {
             // Default welcome message
             runInAction(() => {
-                this.history.push({
+                this._history.push({
                     role: ASSISTANT_ROLE.ASSISTANT,
                     content: [
                         {
@@ -835,11 +757,11 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     .filter(d => d);
                 return linkedDocs;
             },
-            linked => linked.forEach(doc => this.linked_docs_to_add.add(doc))
+            linked => linked.forEach(doc => this._linked_docs_to_add.add(doc))
         );
 
         // Observe changes to linked documents and handle document addition
-        observe(this.linked_docs_to_add, change => {
+        observe(this._linked_docs_to_add, change => {
             if (change.type === 'add') {
                 if (PDFCast(change.newValue.data)) {
                     this.addDocToVectorstore(change.newValue);
@@ -913,7 +835,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      * Getter that retrieves all linked CSV files for analysis.
      */
     @computed get linkedCSVs(): { filename: string; id: string; text: string }[] {
-        return this.linked_csv_files;
+        return this._linked_csv_files;
     }
 
     /**
@@ -921,7 +843,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      */
     @computed get formattedHistory(): string {
         let history = '<chat_history>\n';
-        for (const message of this.history) {
+        for (const message of this._history) {
             history += `<${message.role}>${message.content.map(content => content.text).join(' ')}`;
             if (message.loop_summary) {
                 history += `<loop_summary>${message.loop_summary}</loop_summary>`;
@@ -957,7 +879,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
      */
     @action
     handleFollowUpClick = (question: string) => {
-        this.inputValue = question;
+        this._inputValue = question;
     };
 
     /**
@@ -966,11 +888,11 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     render() {
         return (
             <div className="chat-box">
-                {this.isUploadingDocs && (
+                {this._isUploadingDocs && (
                     <div className="uploading-overlay">
                         <div className="progress-container">
                             <ProgressBar />
-                            <div className="step-name">{this.currentStep}</div>
+                            <div className="step-name">{this._currentStep}</div>
                         </div>
                     </div>
                 )}
@@ -978,18 +900,18 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     <h2>{this.userName()}&apos;s AI Assistant</h2>
                 </div>
                 <div className="chat-messages" ref={this.messagesRef}>
-                    {this.history.map((message, index) => (
+                    {this._history.map((message, index) => (
                         <MessageComponentBox key={index} message={message} onFollowUpClick={this.handleFollowUpClick} onCitationClick={this.handleCitationClick} updateMessageCitations={this.updateMessageCitations} />
                     ))}
-                    {this.current_message && (
-                        <MessageComponentBox key={this.history.length} message={this.current_message} onFollowUpClick={this.handleFollowUpClick} onCitationClick={this.handleCitationClick} updateMessageCitations={this.updateMessageCitations} />
+                    {this._current_message && (
+                        <MessageComponentBox key={this._history.length} message={this._current_message} onFollowUpClick={this.handleFollowUpClick} onCitationClick={this.handleCitationClick} updateMessageCitations={this.updateMessageCitations} />
                     )}
                 </div>
 
                 <form onSubmit={this.askGPT} className="chat-input">
-                    <input type="text" name="messageInput" autoComplete="off" placeholder="Type your message here..." value={this.inputValue} onChange={e => (this.inputValue = e.target.value)} disabled={this.isLoading} />
-                    <button className="submit-button" type="submit" disabled={this.isLoading || !this.inputValue.trim()}>
-                        {this.isLoading ? (
+                    <input type="text" name="messageInput" autoComplete="off" placeholder="Type your message here..." value={this._inputValue} onChange={e => (this._inputValue = e.target.value)} disabled={this._isLoading} />
+                    <button className="submit-button" type="submit" disabled={this._isLoading || !this._inputValue.trim()}>
+                        {this._isLoading ? (
                             <div className="spinner"></div>
                         ) : (
                             <svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
@@ -1000,10 +922,10 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     </button>
                 </form>
                 {/* Popup for citation */}
-                {this.citationPopup.visible && (
+                {this._citationPopup.visible && (
                     <div className="citation-popup">
                         <p>
-                            <strong>Text from your document: </strong> {this.citationPopup.text}
+                            <strong>Text from your document: </strong> {this._citationPopup.text}
                         </p>
                     </div>
                 )}
-- 
cgit v1.2.3-70-g09d2

