From 4433afe837412c10a278e54ee8069cad992900a0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 13 Mar 2025 19:23:31 -0400
Subject: cleaned up how Presboxelement finds PresBox

---
 src/client/views/nodes/trails/PresElementBox.tsx | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index a76805960..957df894c 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -52,7 +52,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // the presentation view that renders this slide
     @computed get presBoxView() {
-        return this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as PresBox;
+        return this.DocumentView?.()
+            .containerViewPath?.()
+            .slice()
+            .reverse()
+            .find(dv => dv?.ComponentView instanceof PresBox)?.ComponentView as PresBox;
     }
 
     // the presentation view document that renders this slide
-- 
cgit v1.2.3-70-g09d2


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/StyleProviderQuiz.tsx             |  31 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  41 +-----
 src/client/views/nodes/trails/PresElementBox.tsx   |   5 +-
 src/client/views/pdf/AnchorMenu.tsx                |  26 +---
 src/client/views/pdf/GPTPopup/GPTPopup.scss        |  16 +--
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         | 160 +++++++++++++++------
 src/client/views/pdf/PDFViewer.tsx                 |  21 +--
 7 files changed, 152 insertions(+), 148 deletions(-)

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/views/StyleProviderQuiz.tsx b/src/client/views/StyleProviderQuiz.tsx
index acda38dd7..f9ee8d3db 100644
--- a/src/client/views/StyleProviderQuiz.tsx
+++ b/src/client/views/StyleProviderQuiz.tsx
@@ -9,7 +9,7 @@ import { DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
 import { NumCast, StrCast } from '../../fields/Types';
 import { Networking } from '../Network';
-import { GPTCallType, gptAPICall, gptImageLabel } from '../apis/gpt/GPT';
+import { GPTCallType, gptAPICall } from '../apis/gpt/GPT';
 import { Docs } from '../documents/Documents';
 import { ContextMenu } from './ContextMenu';
 import { ContextMenuProps } from './ContextMenuItem';
@@ -110,20 +110,21 @@ export namespace styleProviderQuiz {
         const blob = await ImageUtility.canvasToBlob(canvas);
         return selectUrlToBase64(blob);
     }
-    /**
-     * Create flashcards from an image.
-     */
-    async function makeFlashcardsForImage(img: ImageBox) {
-        img.Loading = true;
-        try {
-            const hrefBase64 = await createCanvas(img);
-            const response = await gptImageLabel(hrefBase64, 'Make flashcards out of this image with each question and answer labeled as "question" and "answer". Do not label each flashcard and do not include asterisks: ');
-            AnchorMenu.Instance.transferToFlashcard(response, NumCast(img.layoutDoc.x), NumCast(img.layoutDoc.y));
-        } catch (error) {
-            console.log('Error', error);
-        }
-        img.Loading = false;
-    }
+
+    // /**
+    //  * Create flashcards from an image.
+    //  */
+    // async function makeFlashcardsForImage(img: ImageBox) {
+    //     img.Loading = true;
+    //     try {
+    //         const hrefBase64 = await createCanvas(img);
+    //         const response = await gptImageLabel(hrefBase64, 'Make flashcards out of this image with each question and answer labeled as "question" and "answer". Do not label each flashcard and do not include asterisks: ');
+    //         AnchorMenu.Instance.transferToFlashcard(response, NumCast(img.layoutDoc.x), NumCast(img.layoutDoc.y));
+    //     } catch (error) {
+    //         console.log('Error', error);
+    //     }
+    //     img.Loading = false;
+    // }
 
     /**
      * Calls the createCanvas and pushInfo methods to convert the
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index b86536f86..7b24125e7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transacti
 import { EditorView, NodeViewConstructor } from 'prosemirror-view';
 import * as React from 'react';
 import { BsMarkdownFill } from 'react-icons/bs';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, imageUrlToBase64, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
 import { DateField } from '../../../../fields/DateField';
 import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
@@ -26,7 +26,7 @@ import { ComputedField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, RTFCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
 import { emptyFunction, numberRange, unimplementedFunction, Utils } from '../../../../Utils';
-import { gptAPICall, GPTCallType, gptImageLabel } from '../../../apis/gpt/GPT';
+import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
 import { DocServer } from '../../../DocServer';
 import { Docs } from '../../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -226,18 +226,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         return anchor;
     };
 
-    selectionToFlashcards = async () => {
-        const queryText = window.getSelection()?.toString() ?? '';
-        try {
-            if (queryText) {
-                const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
-                AnchorMenu.Instance.transferToFlashcard(res || 'Something went wrong', NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y));
-            }
-        } catch (err) {
-            console.error(err);
-        }
-    };
-
     @action
     setupAnchorMenu = () => {
         AnchorMenu.Instance.Status = 'marquee';
@@ -1012,31 +1000,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' });
     };
 
-    findImageTags = async () => {
-        const c = this.ProseRef?.getElementsByTagName('img');
-        if (c) {
-            for (const i of c) {
-                // console.log(canvas.toDataURL());
-                // canvas.style.zIndex = '2000000';
-                // document.body.appendChild(canvas);
-                if (i.className !== 'ProseMirror-separator') this.getImageDesc(i.src);
-            }
-        }
-    };
-
-    getImageDesc = async (u: string) => {
-        try {
-            const hrefBase64 = await imageUrlToBase64(u);
-            const response = await gptImageLabel(
-                hrefBase64,
-                'Make flashcards out of this text and image with each question and answer labeled as question and answer. Do not label each flashcard and do not include asterisks: ' + (this.dataDoc.text as RichTextField)?.Text
-            );
-            AnchorMenu.Instance.transferToFlashcard(response || 'Something went wrong', NumCast(this.dataDoc['x']), NumCast(this.dataDoc['y']));
-        } catch (error) {
-            console.log('Error', error);
-        }
-    };
-
     animateRes = (resIndex: number, newText: string) => {
         if (resIndex < newText.length) {
             const marks = this.EditorView?.state.storedMarks ?? [];
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 957df894c..31cd1603f 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -239,10 +239,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 e.clientY,
                 undefined,
                 action(() => {
-                    Array.from(classesToRestore).forEach(pair => {
-                        // eslint-disable-next-line prefer-destructuring
-                        pair[0].className = pair[1];
-                    });
+                    Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1]));
                     this._dragging = false;
                 })
             );
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 6ae659ac1..eb6516403 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -10,7 +10,6 @@ import { Doc, Opt } from '../../../fields/Doc';
 import { SettingsManager } from '../../util/SettingsManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
 import { LinkPopup } from '../linking/LinkPopup';
-import { ComparisonBox } from '../nodes/ComparisonBox';
 import { DocumentView } from '../nodes/DocumentView';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
 import './AnchorMenu.scss';
@@ -69,7 +68,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     public MakeTargetToggle: () => void = unimplementedFunction;
     public ShowTargetTrail: () => void = unimplementedFunction;
     public IsTargetToggler: () => boolean = returnFalse;
-    public gptFlashcards: () => void = unimplementedFunction;
     public makeLabels: () => void = unimplementedFunction;
     public marqueeWidth = 0;
     public marqueeHeight = 0;
@@ -94,22 +92,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
      * Invokes the API with the selected text and stores it in the summarized text.
      * @param e pointer down event
      */
-    gptSummarize = () => GPTPopup.Instance.generateSummary(this._selectedText);
-
-    /*
-     * Transfers the flashcard text generated by GPT on flashcards and creates a collection out them.
-     */
-
-    transferToFlashcard = (text: string, x: number, y: number) => {
-        ComparisonBox.createFlashcardDeck(text, 250, 200, 'data_front', 'data_back').then(
-            action(newCol => {
-                newCol.x = x;
-                newCol.y = y;
-                newCol.zIndex = 1000;
-                this.addToCollection?.(newCol);
-                this._loading = false;
-            })
-        );
+    gptAskAboutSelection = () => {
+        GPTPopup.Instance.askAIAboutSelection(this._selectedText);
+        AnchorMenu.Instance.fadeOut(true);
     };
 
     pointerDown = (e: React.PointerEvent) => {
@@ -188,14 +173,13 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                     {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection */}
                     {this._selectedText && (
                         <IconButton
-                            tooltip="Summarize with AI" //
-                            onPointerDown={this.gptSummarize}
+                            tooltip="Ask AI..." //
+                            onPointerDown={this.gptAskAboutSelection}
                             icon={<FontAwesomeIcon icon="comment-dots" size="lg" />}
                             color={SettingsManager.userColor}
                         />
                     )}
                     {/* Adds a create flashcards option to the anchor menu, which calls the gptFlashcard method. */}
-                    {this.gptFlashcards === unimplementedFunction ? null : <IconButton tooltip="Create flashcards" onPointerDown={this.gptFlashcards} icon={<FontAwesomeIcon icon="layer-group" size="lg" />} color={SettingsManager.userColor} />}
                     {this.makeLabels === unimplementedFunction ? null : <IconButton tooltip="Create labels" onPointerDown={this.makeLabels} icon={<FontAwesomeIcon icon="tag" size="lg" />} color={SettingsManager.userColor} />}
                     {this._selectedText && RichTextMenu.Instance?.createLinkButton()}
                     {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index c8903e09f..18441f76e 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -15,12 +15,8 @@ $headingHeight: 32px;
     height: 100%;
     top: 0;
     left: 0;
-    pointer-events: none;
     border-top: solid gray 20px;
     border-radius: 16px;
-    padding: 16px;
-    padding-bottom: 0;
-    padding-top: 0px;
     z-index: 999;
     display: flex;
     flex-direction: column;
@@ -29,11 +25,13 @@ $headingHeight: 32px;
     box-shadow: 0 2px 5px #7474748d;
     color: $textgrey;
 
-    .gptPopup-sortBox {
+    .gptPopup-summaryBox-content {
+        padding-right: 16px;
+        padding-left: 16px;
+        position: relative;
+        overflow: hidden;
         display: flex;
         flex-direction: column;
-        height: calc(100% - $inputHeight - $headingHeight);
-        pointer-events: all;
     }
 
     .summary-heading {
@@ -65,7 +63,9 @@ $headingHeight: 32px;
     .gptPopup-content-wrapper {
         padding-top: 10px;
         min-height: 50px;
-        height: calc(100% - 32px);
+        white-space: pre-line;
+        overflow: auto;
+        margin-bottom: 10px;
     }
 
     .inputWrapper {
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>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a50526223..83b3dffe5 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -28,7 +28,6 @@ import { AnchorMenu } from './AnchorMenu';
 import { Annotation } from './Annotation';
 import { GPTPopup } from './GPTPopup/GPTPopup';
 import './PDFViewer.scss';
-import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
 import ReactLoading from 'react-loading';
 import { Transform } from '../../util/Transform';
 
@@ -392,23 +391,6 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
         }
     };
 
-    /**
-     * Create a flashcard pile based on the selected text of a pdf.
-     */
-    gptPDFFlashcards = async () => {
-        const queryText = this._selectionText;
-        runInAction(() => (this._loading = true));
-        try {
-            const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
-
-            AnchorMenu.Instance.transferToFlashcard(res || 'Something went wrong', NumCast(this._props.layoutDoc['x']), NumCast(this._props.layoutDoc['y']));
-            this._selectionText = '';
-        } catch (err) {
-            console.error(err);
-        }
-        runInAction(() => (this._loading = false));
-    };
-
     @action
     finishMarquee = (/* x?: number, y?: number */) => {
         AnchorMenu.Instance.makeLabels = unimplementedFunction;
@@ -430,7 +412,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
 
         if (sel) {
             AnchorMenu.Instance.setSelectedText(sel.toString());
-            AnchorMenu.Instance.setLocation(NumCast(this._props.layoutDoc['x']), NumCast(this._props.layoutDoc['y']));
+            AnchorMenu.Instance.setLocation(NumCast(this._props.layoutDoc.x), NumCast(this._props.layoutDoc.y));
         }
 
         if (sel?.type === 'Range') {
@@ -442,7 +424,6 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
         GPTPopup.Instance.addDoc = this._props.sidebarAddDoc;
         // allows for creating collection
         AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument;
-        AnchorMenu.Instance.gptFlashcards = this.gptPDFFlashcards;
         AnchorMenu.Instance.makeLabels = unimplementedFunction;
         AnchorMenu.Instance.AddDrawingAnnotation = this.addDrawingAnnotation;
     };
-- 
cgit v1.2.3-70-g09d2

