diff options
| author | bobzel <zzzman@gmail.com> | 2025-03-14 12:20:41 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2025-03-14 12:20:41 -0400 |
| commit | 45a9f5789fa6eaacca9a39cb96cc2a8e3ebe649c (patch) | |
| tree | f4d098425f93dc63b9dcb3655bd6034930670e2a /src/client/views/pdf/GPTPopup | |
| parent | 4433afe837412c10a278e54ee8069cad992900a0 (diff) | |
simplified anchorMenu to just have an AskAI button .. then updated GPtPopup's summaryBox to have a flashcard option.
Diffstat (limited to 'src/client/views/pdf/GPTPopup')
| -rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.scss | 16 | ||||
| -rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 160 |
2 files changed, 127 insertions, 49 deletions
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> |
