From 78f348cd1406cfaf3d0247973b826783ab3532b5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 17 Sep 2024 21:05:17 -0400
Subject: from last

---
 src/client/views/pdf/GPTPopup/GPTPopup.tsx | 282 ++++++++++++-----------------
 1 file changed, 112 insertions(+), 170 deletions(-)

(limited to 'src/client/views/pdf/GPTPopup/GPTPopup.tsx')

diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 53bedbb65..ed3f99377 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -3,7 +3,7 @@ import { Button, IconButton, Type } from 'browndash-components';
 import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { CgClose, CgPathBack, CgArrowLeftO, CgCornerUpLeft } from 'react-icons/cg';
+import { CgClose, CgCornerUpLeft } from 'react-icons/cg';
 import ReactLoading from 'react-loading';
 import { TypeAnimation } from 'react-type-animation';
 import { ClientUtils } from '../../../../ClientUtils';
@@ -11,16 +11,14 @@ import { Doc } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { Networking } from '../../../Network';
 import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
-import { Docs } from '../../../documents/Documents';
 import { DocUtils } from '../../../documents/DocUtils';
-import { ObservableReactComponent } from '../../ObservableReactComponent';
-import { AnchorMenu } from '../AnchorMenu';
-import './GPTPopup.scss';
+import { Docs } from '../../../documents/Documents';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { SnappingManager } from '../../../util/SnappingManager';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { DocumentView } from '../../nodes/DocumentView';
-import { DocCast } from '../../../../fields/Types';
-import { RTFCast } from '../../../../fields/Types';
+import { AnchorMenu } from '../AnchorMenu';
+import './GPTPopup.scss';
 
 export enum GPTPopupMode {
     SUMMARY,
@@ -30,21 +28,15 @@ export enum GPTPopupMode {
     DATA,
     CARD,
     SORT,
-    QUIZ
+    QUIZ,
 }
 
 export enum GPTQuizType {
     CURRENT = 0,
     CHOOSE = 1,
-    MULTIPLE = 2
-
+    MULTIPLE = 2,
 }
 
-
-
-
-
-
 interface GPTPopupProps {}
 
 @observer
@@ -168,30 +160,29 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         this.cardsDoneLoading = done;
     }
 
-    @observable sortRespText: string = ''
+    @observable sortRespText: string = '';
 
     @action setSortRespText(resp: string) {
-        this.sortRespText = resp
+        this.sortRespText = resp;
     }
 
-    @observable chatSortPrompt: string = ""
+    @observable chatSortPrompt: string = '';
 
     sortPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
         this.chatSortPrompt = e.target.value;
     });
 
-    @observable quizAnswer: string = ""
+    @observable quizAnswer: string = '';
 
     quizAnswerChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
         this.quizAnswer = e.target.value;
     });
 
-    @observable conversationArray: string[] = ["Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. "]
-
+    @observable conversationArray: string[] = ['Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. '];
 
     /**
      * When the cards are in quiz mode in the card view, allows gpt to determine whether the user's answer was correct
-     * @returns 
+     * @returns
      */
     generateQuiz = async () => {
         this.setLoading(true);
@@ -201,14 +192,13 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
 
         const selected = DocumentView.SelectedDocs().lastElement();
 
-
         const questionText = 'Question: ' + StrCast(selected['gptInputText']);
 
         if (StrCast(selected['gptRubric']) === '') {
-            const rubricText = 'Rubric: ' + await this.generateRubric(StrCast(selected['gptInputText']), selected)
+            const rubricText = 'Rubric: ' + (await this.generateRubric(StrCast(selected['gptInputText']), selected));
         }
 
-        const rubricText = 'Rubric: ' + (StrCast(selected['gptRubric']))
+        const rubricText = 'Rubric: ' + StrCast(selected['gptRubric']);
         const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText;
 
         try {
@@ -217,23 +207,20 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
                 console.error('GPT call failed');
                 return;
             }
-            console.log(res)
-            this.setQuizResp(res)
-            this.conversationArray.push(res)
+            console.log(res);
+            this.setQuizResp(res);
+            this.conversationArray.push(res);
 
             this.setLoading(false);
             this.setSortDone(true);
-
         } catch (err) {
             console.error('GPT call failed');
         }
 
-
-        if (this.onQuizRandom){
-            this.onQuizRandom()
+        if (this.onQuizRandom) {
+            this.onQuizRandom();
         }
-
-    }
+    };
 
     /**
      * Generates a rubric by which to compare the user's answer to
@@ -241,105 +228,95 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
      * @param doc the doc the user is providing info about
      * @returns gpt's response
      */
-    generateRubric = async (inputText: string, doc:Doc) => {
+    generateRubric = async (inputText: string, doc: Doc) => {
         try {
-            const res = await gptAPICall(inputText, GPTCallType.RUBRIC);            
-            doc['gptRubric']= res;
-            return res
+            const res = await gptAPICall(inputText, GPTCallType.RUBRIC);
+            doc['gptRubric'] = res;
+            return res;
         } catch (err) {
             console.error('GPT call failed');
         }
-
-    }
-
-
+    };
 
     @observable private regenerateCallback: (() => Promise<void>) | null = null;
 
     /**
      * Callback function that causes the card view to update the childpair string list
-     * @param callback 
+     * @param callback
      */
     @action public setRegenerateCallback(callback: () => Promise<void>) {
         this.regenerateCallback = callback;
     }
 
-
-
-
     public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
     public createFilteredDoc: (axes?: string[]) => boolean = () => false;
     public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
 
-    @observable quizRespText: string = ''
-
-    @action setQuizResp (resp: string) {
-        this.quizRespText = resp
+    @observable quizRespText: string = '';
 
+    @action setQuizResp(resp: string) {
+        this.quizRespText = resp;
     }
 
     /**
      * Generates a response to the user's question depending on the type of their question
      */
     generateCard = async () => {
-        console.log(this.chatSortPrompt + "USER PROMPT")
+        console.log(this.chatSortPrompt + 'USER PROMPT');
         this.setLoading(true);
         this.setSortDone(false);
 
         if (this.regenerateCallback) {
             await this.regenerateCallback();
         }
-    
+
         try {
             // const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
             const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE);
-            const questionNumber = questionType.split(' ')[0]
-            console.log(questionType)
-            let res = ''
+            const questionNumber = questionType.split(' ')[0];
+            console.log(questionType);
+            let res = '';
 
             switch (questionNumber) {
                 case '1':
                 case '2':
                 case '4':
                     res = await gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt);
-                    break
+                    break;
                 case '6':
                     res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
-                    break
+                    break;
                 default:
-
                     const selected = DocumentView.SelectedDocs().lastElement();
                     const questionText = StrCast(selected!['gptInputText']);
 
-
                     res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt);
-                    break
+                    break;
             }
 
             // Trigger the callback with the result
             if (this.onSortComplete) {
                 this.onSortComplete(res || 'Something went wrong :(', questionNumber, questionType.split(' ').slice(1).join(' '));
 
-                let explanation = res
+                let explanation = res;
+
+                if (questionType != '5' && questionType != '3') {
+                    // Extract explanation surrounded by ------ at the top or both at the top and bottom
+                    const explanationMatch = res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) || [];
+                    explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found';
+                }
 
-                if (questionType != '5' && questionType != '3'){
-    
-                // Extract explanation surrounded by ------ at the top or both at the top and bottom
-                const explanationMatch = res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) || [];
-                explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found';
-            } 
-    
                 // Set the extracted explanation to sortRespText
                 this.setSortRespText(explanation);
-                this.conversationArray.push(this.sortRespText)
-                this.scrollToBottom()
-    
+                this.conversationArray.push(this.sortRespText);
+                this.scrollToBottom();
+
                 console.log(res);
             }
         } catch (err) {
             console.error(err);
         }
-    
+
         this.setLoading(false);
         this.setSortDone(true);
     };
@@ -476,21 +453,16 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         makeObservable(this);
         GPTPopup.Instance = this;
         this.messagesEndRef = React.createRef();
-
     }
 
-
-
     scrollToBottom = () => {
-            setTimeout(() => {
-                // Code to execute after 1 second (1000 ms)
-                if (this.messagesEndRef.current) {
-
+        setTimeout(() => {
+            // Code to execute after 1 second (1000 ms)
+            if (this.messagesEndRef.current) {
                 this.messagesEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });
-                }
-
-              }, 50);
-      }
+            }
+        }, 50);
+    };
 
     componentDidUpdate = () => {
         if (this.loading) {
@@ -498,9 +470,9 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         }
     };
 
-    @observable quizMode : GPTQuizType = GPTQuizType.CURRENT
-    @action setQuizMode (g: GPTQuizType) {
-        this.quizMode = g
+    @observable quizMode: GPTQuizType = GPTQuizType.CURRENT;
+    @action setQuizMode(g: GPTQuizType) {
+        this.quizMode = g;
     }
 
     cardMenu = () => (
@@ -517,21 +489,18 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
                     textAlign: 'center',
                     color: '#ffffff',
                     fontSize: '16px',
-                    marginBottom: '10px'
+                    marginBottom: '10px',
                 }}
             />
             <Button
                 tooltip="Test your knowledge with ChatGPT!"
                 text="Quiz Cards!"
                 onClick={() => {
-                    this.conversationArray = ['Define the selected card!']
-                    this.setMode(GPTPopupMode.QUIZ)
-                    if (this.onQuizRandom){
-                        this.onQuizRandom()
+                    this.conversationArray = ['Define the selected card!'];
+                    this.setMode(GPTPopupMode.QUIZ);
+                    if (this.onQuizRandom) {
+                        this.onQuizRandom();
                     }
-                    
-
-
                 }}
                 color={StrCast(Doc.UserDoc().userVariantColor)}
                 type={Type.TERT}
@@ -541,13 +510,10 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
                     color: '#ffffff',
                     fontSize: '16px',
                     height: '40%',
-
                 }}
             />
         </div>
-    )
-
-
+    );
 
     handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => {
         if (e.key === 'Enter') {
@@ -556,85 +522,72 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
             if (isSort) {
                 this.conversationArray.push(this.chatSortPrompt);
                 await this.generateCard();
-                this.chatSortPrompt = ''
-
+                this.chatSortPrompt = '';
             } else {
                 this.conversationArray.push(this.quizAnswer);
                 await this.generateQuiz();
-                this.quizAnswer = ''
-
+                this.quizAnswer = '';
             }
 
             this.scrollToBottom();
         }
-    }
-    
+    };
+
     cardActual = (opt: GPTPopupMode) => {
-        const isSort = opt === GPTPopupMode.SORT
-            return (
-
-                    <div className="btns-wrapper-gpt">
-                        <div className="chat-wrapper">
-                            <div className="chat-bubbles">
-                                {this.conversationArray.map((message, index) => (
-                                    <div
-                                        key={index}
-                                        className={`chat-bubble ${index % 2 === 1 ? 'user-message' : 'chat-message'}`}
-                                    >
-                                        {message}
-                                    </div>
-                                ))}
-                                {(!this.cardsDoneLoading || this.loading) && (
-                                    <div className={`chat-bubble chat-message`}>
-                                       ...
-                                    </div>
-                                )}
+        const isSort = opt === GPTPopupMode.SORT;
+        return (
+            <div className="btns-wrapper-gpt">
+                <div className="chat-wrapper">
+                    <div className="chat-bubbles">
+                        {this.conversationArray.map((message, index) => (
+                            <div key={index} className={`chat-bubble ${index % 2 === 1 ? 'user-message' : 'chat-message'}`}>
+                                {message}
                             </div>
+                        ))}
+                        {(!this.cardsDoneLoading || this.loading) && <div className={`chat-bubble chat-message`}>...</div>}
+                    </div>
 
-                            <div ref={this.messagesEndRef} style= {{height: '100px'}} />
-
-                        </div>
-
-                        <div className="inputWrapper">
-                            <input
-                                className="searchBox-input"
-                                defaultValue=""
-                                value={isSort ? this.chatSortPrompt : this.quizAnswer} // Controlled input
-                                autoComplete="off"
-                                onChange={isSort ? this.sortPromptChanged : this.quizAnswerChanged}
-                                onKeyDown={(e) => {
-                                    this.handleKeyPress(e, isSort)
+                    <div ref={this.messagesEndRef} style={{ height: '100px' }} />
+                </div>
 
-                                            
-                                    }
-                                }
-                                type="text"
-                                placeholder={`${isSort ? 'Have ChatGPT sort, tag, define, or filter your cards for you!' : 'Define the selected card!'}`}
-                            />
-                        </div>
-                    </div>
-            );
+                <div className="inputWrapper">
+                    <input
+                        className="searchBox-input"
+                        defaultValue=""
+                        value={isSort ? this.chatSortPrompt : this.quizAnswer} // Controlled input
+                        autoComplete="off"
+                        onChange={isSort ? this.sortPromptChanged : this.quizAnswerChanged}
+                        onKeyDown={e => {
+                            this.handleKeyPress(e, isSort);
+                        }}
+                        type="text"
+                        placeholder={`${isSort ? 'Have ChatGPT sort, tag, define, or filter your cards for you!' : 'Define the selected card!'}`}
+                    />
+                </div>
+            </div>
+        );
     };
-    
+
     sortBox = () => (
-        <div style = {{height: '80%'}}>
+        <div style={{ height: '80%' }}>
             {this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')}
             <>
-                {!this.cardsDoneLoading? (
+                {!this.cardsDoneLoading ? (
                     <div className="content-wrapper">
                         <div className="loading-spinner">
                             <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
                             {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
                         </div>
                     </div>
+                ) : this.mode === GPTPopupMode.CARD ? (
+                    this.cardMenu()
                 ) : (
-                    (this.mode === GPTPopupMode.CARD ? this.cardMenu() : this.cardActual(this.mode)) // Call the functions to render JSX
-                )}
+                    this.cardActual(this.mode)
+                ) // Call the functions to render JSX
+                }
             </>
         </div>
     );
-    
-    
 
     imageBox = () => (
         <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
@@ -785,32 +738,25 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
                 <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />
             ) : (
                 <>
-                {(this.mode === GPTPopupMode.SORT || this.mode === GPTPopupMode.QUIZ) && (
-                        <IconButton
-                            color={StrCast(SettingsManager.userVariantColor)}
-                            tooltip="back"
-                            icon={<CgCornerUpLeft size="16px" />}
-                            onClick={() => this.mode = GPTPopupMode.CARD}
-                            style = {{right: '50px', position: 'absolute'}}
-                        />
+                    {(this.mode === GPTPopupMode.SORT || this.mode === GPTPopupMode.QUIZ) && (
+                        <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="back" icon={<CgCornerUpLeft size="16px" />} onClick={() => (this.mode = GPTPopupMode.CARD)} style={{ right: '50px', position: 'absolute' }} />
                     )}
                     <IconButton
                         color={StrCast(SettingsManager.userVariantColor)}
                         tooltip="close"
                         icon={<CgClose size="16px" />}
                         onClick={() => {
-                            this.setVisible(false)}}
+                            this.setVisible(false);
+                        }}
                     />
-                    
                 </>
             )}
         </div>
     );
-        
 
     render() {
         let content;
-        
+
         switch (this.mode) {
             case GPTPopupMode.SUMMARY:
                 content = this.summaryBox();
@@ -829,16 +775,12 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
             default:
                 content = null;
         }
-    
+
         return (
-            <div
-                className="summary-box"
-                style={{ display: this.visible ? 'flex' : 'none' }}
-            >
+            <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
                 {content}
                 <div className="resize-handle" />
             </div>
         );
     }
-    
 }
-- 
cgit v1.2.3-70-g09d2

