From 45a9f5789fa6eaacca9a39cb96cc2a8e3ebe649c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 14 Mar 2025 12:20:41 -0400
Subject: simplified anchorMenu to just have an AskAI button .. then updated
 GPtPopup's summaryBox to have a flashcard option.

---
 src/client/views/pdf/GPTPopup/GPTPopup.tsx | 160 +++++++++++++++++++++--------
 1 file changed, 119 insertions(+), 41 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 4dc45e6a0..67213382d 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -31,6 +31,7 @@ import { OpenWhere } from '../../nodes/OpenWhere';
 import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
 import { ImageField } from '../../../../fields/URLField';
 import { List } from '../../../../fields/List';
+import { ComparisonBox } from '../../nodes/ComparisonBox';
 
 export enum GPTPopupMode {
     SUMMARY, // summary of seleted document text
@@ -56,7 +57,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
     private _dataJson: string = '';
     private _documentDescriptions: Promise<string> | undefined; // a cache of the descriptions of all docs in the selected collection. makes it more efficient when asking GPT multiple questions about the collection.
     private _sidebarFieldKey: string = '';
-    private _textToSummarize: string = '';
+    private _aiReferenceText: string = '';
     private _imageDescription: string = '';
     private _textToDocMap = new Map<string, Doc>(); // when GPT answers with a doc's content, this helps us find the Doc
     private _addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -79,7 +80,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
     };
 
     componentDidUpdate() {
-        this._gptProcessing && this.setStopAnimatingResponse(false);
+        //this._gptProcessing && this.setStopAnimatingResponse(false);
     }
     componentDidMount(): void {
         reaction(
@@ -100,14 +101,14 @@ export class GPTPopup extends ObservableReactComponent<object> {
         );
     }
 
+    @observable private _showOriginal = true;
+    @observable private _responseText: string = '';
     @observable private _conversationArray: string[] = ['Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. '];
     @observable private _fireflyArray: string[] = ['Hi! In this pop up, you can ask Firefly to create images. '];
     @observable private _chatEnabled: boolean = false;
     @action private setChatEnabled = (start: boolean) => (this._chatEnabled = start);
     @observable private _gptProcessing: boolean = false;
     @action private setGptProcessing = (loading: boolean) => (this._gptProcessing = loading);
-    @observable private _responseText: string = '';
-    @action private setResponseText = (text: string) => (this._responseText = text);
     @observable private _imgUrls: string[][] = [];
     @action private setImgUrls = (imgs: string[][]) => (this._imgUrls = imgs);
     @observable private _collectionContext: Doc | undefined = undefined;
@@ -286,18 +287,30 @@ export class GPTPopup extends ObservableReactComponent<object> {
     /**
      * Completes an API call to generate a summary of the specified text
      *
-     * @param text the text to summarizz
+     * @param text the text to summarize
      */
-    generateSummary = (text: string) => {
+    private generateSummary = action((text: string) => {
         SnappingManager.SetChatVisible(true);
-        this._textToSummarize = text;
-        this.setMode(GPTPopupMode.SUMMARY);
+        this._showOriginal = false;
         this.setGptProcessing(true);
         return gptAPICall(text, GPTCallType.SUMMARY)
-            .then(res => this.setResponseText(res || 'Something went wrong.'))
+            .then(action(res => (this._responseText = res || 'Something went wrong.')))
             .catch(err => console.error(err))
             .finally(() => this.setGptProcessing(false));
-    };
+    });
+
+    /**
+     * Completes an API call to generate a summary of the specified text
+     *
+     * @param text the text to summarizz
+     */
+    askAIAboutSelection = action((text: string) => {
+        SnappingManager.SetChatVisible(true);
+        this._aiReferenceText = text;
+        this._responseText = '';
+        this._showOriginal = true;
+        this.setMode(GPTPopupMode.SUMMARY);
+    });
 
     /**
      * Completes an API call to generate an analysis of
@@ -306,14 +319,16 @@ export class GPTPopup extends ObservableReactComponent<object> {
     generateDataAnalysis = () => {
         this.setGptProcessing(true);
         return gptAPICall(this._dataJson, GPTCallType.DATA, this._dataChatPrompt)
-            .then(res => {
-                const json = JSON.parse(res! as string);
-                const keys = Object.keys(json);
-                this._correlatedColumns = [];
-                this._correlatedColumns.push(json[keys[0]]);
-                this._correlatedColumns.push(json[keys[1]]);
-                this.setResponseText(json[keys[2]] || 'Something went wrong.');
-            })
+            .then(
+                action(res => {
+                    const json = JSON.parse(res! as string);
+                    const keys = Object.keys(json);
+                    this._correlatedColumns = [];
+                    this._correlatedColumns.push(json[keys[0]]);
+                    this._correlatedColumns.push(json[keys[1]]);
+                    this._responseText = json[keys[2]] || 'Something went wrong.';
+                })
+            )
             .catch(err => console.error(err))
             .finally(() => this.setGptProcessing(false));
     };
@@ -336,6 +351,24 @@ export class GPTPopup extends ObservableReactComponent<object> {
             });
         }
     };
+    /**
+     * Create Flashcards for the selected text
+     */
+    private createFlashcards = action(
+        () =>
+            this.setGptProcessing(true) &&
+            gptAPICall(this._aiReferenceText, GPTCallType.FLASHCARD, undefined, true)
+                .then(res =>
+                    ComparisonBox.createFlashcardDeck(res, 250, 200, 'data_front', 'data_back').then(
+                        action(newCol => {
+                            newCol.zIndex = 1000;
+                            DocumentViewInternal.addDocTabFunc(newCol, OpenWhere.addRight);
+                        })
+                    )
+                )
+                .catch(console.error)
+                .finally(action(() => (this._gptProcessing = false)))
+    );
 
     /**
      * Creates a histogram to show the correlation relationship that was found
@@ -536,35 +569,80 @@ export class GPTPopup extends ObservableReactComponent<object> {
 
     summaryBox = () => (
         <>
-            <div style={{ height: 'calc(100% - 60px)', overflow: 'auto' }}>
-                {this.heading('SUMMARY')}
+            <div className="gptPopup-summaryBox-content">
+                <div onClick={action(() => (this._showOriginal = !this._showOriginal))}>{this.heading(this._showOriginal ? 'SELECTION' : 'SUMMARY')}</div>
                 <div className="gptPopup-content-wrapper">
-                    {!this._gptProcessing &&
-                        (!this._stopAnimatingResponse ? (
-                            <TypeAnimation
-                                speed={50}
-                                sequence={[
-                                    this._responseText,
-                                    () => {
-                                        setTimeout(() => this.setStopAnimatingResponse(true), 500);
-                                    },
-                                ]}
-                            />
+                    {!this._gptProcessing && !this._stopAnimatingResponse && this._responseText ? (
+                        <TypeAnimation
+                            speed={50}
+                            sequence={[
+                                this._responseText,
+                                () => {
+                                    setTimeout(() => this.setStopAnimatingResponse(true), 500);
+                                },
+                            ]}
+                        />
+                    ) : this._showOriginal ? (
+                        this._gptProcessing ? (
+                            '...generating cards...'
                         ) : (
-                            this._responseText
-                        ))}
+                            this._aiReferenceText
+                        )
+                    ) : (
+                        this._responseText || (this._gptProcessing ? '...generating summary...' : '-no ai summary-')
+                    )}
                 </div>
             </div>
-            {!this._gptProcessing && (
-                <div className="btns-wrapper" style={{ position: 'absolute', bottom: 0, width: 'calc(100% - 32px)' }}>
-                    {this._stopAnimatingResponse ? (
-                        <>
-                            <IconButton tooltip="Generate Again" onClick={() => this.generateSummary(this._textToSummarize + ' ')} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(SettingsManager.userVariantColor)} />
-                            <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(SettingsManager.userVariantColor)} type={Type.TERT} />
-                        </>
+            {this._gptProcessing ? null : (
+                <div className="btns-wrapper" style={{ position: 'relative', width: 'calc(100% - 32px)' }}>
+                    {this._stopAnimatingResponse || !this._responseText ? (
+                        <div style={{ display: 'flex' }}>
+                            {!this._showOriginal ? (
+                                <>
+                                    <Button
+                                        tooltip="Show originally selected text" //
+                                        text="Selection"
+                                        onClick={action(() => (this._showOriginal = true))}
+                                        color={StrCast(SettingsManager.userVariantColor)}
+                                        type={Type.TERT}
+                                    />
+                                    <Button
+                                        tooltip="Create a text Doc with this text and link to the text selection" //
+                                        text="Transfer To Text"
+                                        onClick={this.transferToText}
+                                        color={StrCast(SettingsManager.userVariantColor)}
+                                        type={Type.TERT}
+                                    />
+                                </>
+                            ) : (
+                                <>
+                                    <Button
+                                        tooltip="Show AI summary of original selection text (Shift+Click to regenerate)"
+                                        text="Summary"
+                                        onClick={action(e => {
+                                            if (e.shiftKey) {
+                                                this.setStopAnimatingResponse(false);
+                                                this._aiReferenceText += ' ';
+                                                this._responseText = '';
+                                            }
+                                            this.generateSummary(this._aiReferenceText);
+                                        })}
+                                        color={StrCast(SettingsManager.userVariantColor)}
+                                        type={Type.TERT}
+                                    />
+                                    <Button
+                                        tooltip="Create Flashcards" //
+                                        text="Create Flashcards"
+                                        onClick={this.createFlashcards}
+                                        color={StrCast(SettingsManager.userVariantColor)}
+                                        type={Type.TERT}
+                                    />
+                                </>
+                            )}
+                        </div>
                     ) : (
                         <div className="summarizing">
-                            <span>Summarizing</span>
+                            <span>{this._showOriginal ? 'Creating Cards...' : 'Summarizing'}</span>
                             <ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
                             <Button text="Stop Animation" onClick={() => this.setStopAnimatingResponse(true)} color={StrCast(SettingsManager.userVariantColor)} type={Type.TERT} />
                         </div>
-- 
cgit v1.2.3-70-g09d2

