aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/GPTPopup
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-05-19 02:08:43 -0400
committerbobzel <zzzman@gmail.com>2024-05-19 02:08:43 -0400
commit6841dc0fd2aecf31eda2102e660c58905d1e6f44 (patch)
tree821b79178aa07001274759c716badb2a8a170056 /src/client/views/pdf/GPTPopup
parent2fc1fb7d322ab0950afb0d334c17aa93bd16f6c0 (diff)
parent13dc6de0e0099f699ad0d2bb54401e6a0aa25018 (diff)
merged with soon-to-be-master
Diffstat (limited to 'src/client/views/pdf/GPTPopup')
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss43
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx238
2 files changed, 219 insertions, 62 deletions
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 5d966395c..6d8793f82 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -11,8 +11,8 @@ $highlightedText: #82e0ff;
right: 10px;
width: 250px;
min-height: 200px;
- border-radius: 15px;
- padding: 15px;
+ border-radius: 16px;
+ padding: 16px;
padding-bottom: 0;
z-index: 999;
display: flex;
@@ -55,16 +55,29 @@ $highlightedText: #82e0ff;
overflow-y: auto;
}
- .btns-wrapper {
+ .btns-wrapper-gpt {
height: 50px;
display: flex;
- justify-content: space-between;
+ justify-content: center;
align-items: center;
+ transform: translateY(30px);
+
+
+ .searchBox-input{
+ transform: translateY(-15px);
+ height: 50px;
+ border-radius: 10px;
+ border-color: #5b97ff;
+ }
+
+
.summarizing {
display: flex;
align-items: center;
}
+
+
}
button {
@@ -111,6 +124,28 @@ $highlightedText: #82e0ff;
}
}
+.loading-spinner {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100px;
+ font-size: 20px;
+ font-weight: bold;
+ color: #666;
+}
+
+
+
+
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+
+
.image-content-wrapper {
display: flex;
flex-direction: column;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 29b1ca365..8bb2e2844 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,34 +1,41 @@
+/* eslint-disable jsx-a11y/label-has-associated-control */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, EditableText, IconButton, Size, Type } from 'browndash-components';
+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 } from 'react-icons/cg';
import ReactLoading from 'react-loading';
import { TypeAnimation } from 'react-type-animation';
-import { Utils } from '../../../../Utils';
+import { ClientUtils } from '../../../../ClientUtils';
import { Doc } from '../../../../fields/Doc';
import { NumCast, StrCast } from '../../../../fields/Types';
import { Networking } from '../../../Network';
import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
-import { DocUtils, Docs } from '../../../documents/Documents';
+import { Docs } from '../../../documents/Documents';
+import { DocUtils } from '../../../documents/DocUtils';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { AnchorMenu } from '../AnchorMenu';
import './GPTPopup.scss';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { SnappingManager } from '../../../util/SnappingManager';
export enum GPTPopupMode {
SUMMARY,
EDIT,
IMAGE,
DATA,
+ SORT,
}
interface GPTPopupProps {}
@observer
export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
+ // eslint-disable-next-line no-use-before-define
static Instance: GPTPopup;
@observable private chatMode: boolean = false;
+ private correlatedColumns: string[] = [];
@observable
public visible: boolean = false;
@@ -59,7 +66,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
public dataChatPrompt: string | null = null;
@action
public setDataJson = (text: string) => {
- if (text=="") this.dataChatPrompt = "";
+ if (text === '') this.dataChatPrompt = '';
this.dataJson = text;
};
@@ -87,8 +94,6 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
@observable
public highlightRange: number[] = [];
@action callSummaryApi = () => {};
- @action callEditApi = () => {};
- @action replaceText = (replacement: string) => {};
@observable
private done: boolean = false;
@@ -98,6 +103,14 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
this.chatMode = false;
};
+ @observable
+ private sortDone: boolean = false; // this is so redundant but the og done variable was causing weird unknown problems and im just a girl
+
+ @action
+ public setSortDone = (done: boolean) => {
+ this.sortDone = done;
+ };
+
// change what can be a ref into a ref
@observable
private sidebarId: string = '';
@@ -120,35 +133,75 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
this.textAnchor = anchor;
};
+ @observable
+ public sortDesc: string = '';
+
+ @action public setSortDesc = (t: string) => {
+ this.sortDesc = t;
+ };
+
+ @observable onSortComplete?: (sortResult: string) => void;
+ @observable cardsDoneLoading = false;
+
+ @action setCardsDoneLoading(done: boolean) {
+ console.log(done + 'HI HIHI');
+ this.cardsDoneLoading = done;
+ }
+
public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
+ public createFilteredDoc: (axes?: any) => boolean = () => false;
public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
/**
+ * Sorts cards in the CollectionCardDeckView
+ */
+ generateSort = async () => {
+ this.setLoading(true);
+ this.setSortDone(false);
+
+ try {
+ const res = await gptAPICall(this.sortDesc, GPTCallType.SORT);
+ // Trigger the callback with the result
+ if (this.onSortComplete) {
+ this.onSortComplete(res || 'Something went wrong :(');
+ console.log(res);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+
+ this.setLoading(false);
+ this.setSortDone(true);
+ };
+
+ /**
* Generates a Dalle image and uploads it to the server.
*/
generateImage = async () => {
- if (this.imgDesc === '') return;
+ if (this.imgDesc === '') return undefined;
this.setImgUrls([]);
this.setMode(GPTPopupMode.IMAGE);
this.setVisible(true);
this.setLoading(true);
try {
- let image_urls = await gptImageCall(this.imgDesc);
- console.log('Image urls: ', image_urls);
- if (image_urls && image_urls[0]) {
- const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
- console.log('Upload result: ', result);
- const source = Utils.prepend(result.accessPaths.agnostic.client);
- console.log('Upload source: ', source);
- this.setImgUrls([[image_urls[0], source]]);
+ const imageUrls = await gptImageCall(this.imgDesc);
+ if (imageUrls && imageUrls[0]) {
+ const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [imageUrls[0]] });
+ const source = ClientUtils.prepend(result.accessPaths.agnostic.client);
+ this.setImgUrls([[imageUrls[0], source]]);
}
} catch (err) {
console.error(err);
}
this.setLoading(false);
+ return undefined;
};
+ /**
+ * Completes an API call to generate a summary of
+ * this.selectedText in the popup.
+ */
generateSummary = async () => {
GPTPopup.Instance.setVisible(true);
GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
@@ -161,19 +214,28 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
console.error(err);
}
GPTPopup.Instance.setLoading(false);
- }
+ };
+ /**
+ * Completes an API call to generate an analysis of
+ * this.dataJson in the popup.
+ */
generateDataAnalysis = async () => {
GPTPopup.Instance.setVisible(true);
GPTPopup.Instance.setLoading(true);
try {
- let res = await gptAPICall(this.dataJson, GPTCallType.DATA, this.dataChatPrompt);
- GPTPopup.Instance.setText(res || 'Something went wrong.');
+ const res = await gptAPICall(this.dataJson, GPTCallType.DATA, this.dataChatPrompt);
+ 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]]);
+ GPTPopup.Instance.setText(json[keys[2]] || 'Something went wrong.');
} catch (err) {
console.error(err);
}
GPTPopup.Instance.setLoading(false);
- }
+ };
/**
* Transfers the summarization text to a sidebar annotation text document.
@@ -186,6 +248,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
_layout_autoHeight: true,
});
this.addDoc(newDoc, this.sidebarId);
+ // newDoc.data = 'Hello world';
const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
if (anchor) {
DocUtils.MakeLink(newDoc, anchor, {
@@ -195,6 +258,13 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
};
/**
+ * Creates a histogram to show the correlation relationship that was found
+ */
+ private createVisualization = () => {
+ this.createFilteredDoc(this.correlatedColumns);
+ };
+
+ /**
* Transfers the image urls to actual image docs
*/
private transferToImage = (source: string) => {
@@ -224,7 +294,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
*/
private chatWithAI = () => {
this.chatMode = true;
- }
+ };
dataPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
this.dataChatPrompt = e.target.value;
});
@@ -243,30 +313,77 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
}
};
- imageBox = () => {
- return (
- <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
- {this.heading('GENERATED IMAGE')}
- <div className="image-content-wrapper">
- {this.imgUrls.map(rawSrc => (
- <div className="img-wrapper">
- <div className="img-container">
- <img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
- </div>
- <div className="btn-container">
- <Button text="Save Image" onClick={() => this.transferToImage(rawSrc[1])} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
- </div>
+ sortBox = () => (
+ <>
+ <div>
+ {this.heading('SORTING')}
+ {this.loading ? (
+ <div className="content-wrapper">
+ <div className="loading-spinner">
+ <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
+ <span>Loading...</span>
</div>
- ))}
- </div>
- {!this.loading && (
+ </div>
+ ) : (
<>
- <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+ {!this.cardsDoneLoading ? (
+ <div className="content-wrapper">
+ <div className="loading-spinner">
+ <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
+ <span>Reading Cards...</span>
+ </div>
+ </div>
+ ) : (
+ !this.sortDone && (
+ <div className="btns-wrapper-gpt">
+ <Button
+ tooltip="Have ChatGPT sort your cards for you!"
+ text="Sort!"
+ onClick={this.generateSort}
+ color={StrCast(Doc.UserDoc().userVariantColor)}
+ type={Type.TERT}
+ style={{
+ width: '90%', // Almost as wide as the container
+ textAlign: 'center',
+ color: '#ffffff', // White text
+ fontSize: '16px', // Adjust font size as needed
+ }}
+ />
+ </div>
+ )
+ )}
+
+ {this.sortDone && (
+ <div>
+ <div className="content-wrapper">
+ <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : 'Sorting done! Feel free to move things around / regenerate :) !'}</p>
+ <IconButton tooltip="Generate Again" onClick={() => this.setSortDone(false)} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+ </div>
+ </div>
+ )}
</>
)}
</div>
- );
- };
+ </>
+ );
+ imageBox = () => (
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
+ {this.heading('GENERATED IMAGE')}
+ <div className="image-content-wrapper">
+ {this.imgUrls.map(rawSrc => (
+ <div className="img-wrapper">
+ <div className="img-container">
+ <img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
+ </div>
+ <div className="btn-container">
+ <Button text="Save Image" onClick={() => this.transferToImage(rawSrc[1])} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
+ </div>
+ </div>
+ ))}
+ </div>
+ {!this.loading && <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />}
+ </div>
+ );
summaryBox = () => (
<>
@@ -295,8 +412,8 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
<div className="btns-wrapper">
{this.done ? (
<>
- <IconButton tooltip="Generate Again" onClick={this.generateSummary} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
- <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
+ <IconButton tooltip="Generate Again" onClick={this.generateSummary} 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} />
</>
) : (
<div className="summarizing">
@@ -307,7 +424,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
onClick={() => {
this.setDone(true);
}}
- color={StrCast(Doc.UserDoc().userVariantColor)}
+ color={StrCast(SettingsManager.userVariantColor)}
type={Type.TERT}
/>
</div>
@@ -320,7 +437,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
dataAnalysisBox = () => (
<>
<div>
- {this.heading("ANALYSIS")}
+ {this.heading('ANALYSIS')}
<div className="content-wrapper">
{!this.loading &&
(!this.done ? (
@@ -342,8 +459,8 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
</div>
{!this.loading && (
<div className="btns-wrapper">
- {this.done?
- this.chatMode?(
+ {this.done ? (
+ this.chatMode ? (
<input
defaultValue=""
autoComplete="off"
@@ -356,19 +473,26 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
placeholder="Ask GPT a question about the data..."
id="search-input"
className="searchBox-input"
- style={{width: "100%"}}
+ style={{ width: '100%' }}
/>
- )
- :(
- <>
- <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
- <Button tooltip="Chat with AI" text="Chat with AI" onClick={this.chatWithAI} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
- </>
+ ) : (
+ <>
+ <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(SnappingManager.userVariantColor)} type={Type.TERT} />
+ <Button tooltip="Chat with AI" text="Chat with AI" onClick={this.chatWithAI} color={StrCast(SnappingManager.userVariantColor)} type={Type.TERT} />
+ </>
+ )
) : (
<div className="summarizing">
<span>Summarizing</span>
<ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
- <Button text="Stop Animation" onClick={() => {this.setDone(true);}} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT}/>
+ <Button
+ text="Stop Animation"
+ onClick={() => {
+ this.setDone(true);
+ }}
+ color={StrCast(SnappingManager.userVariantColor)}
+ type={Type.TERT}
+ />
</div>
)}
</div>
@@ -382,21 +506,19 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
<FontAwesomeIcon icon="exclamation-circle" size="sm" style={{ paddingRight: '5px' }} />
AI generated responses can contain inaccurate or misleading content.
</div>
- ) : (
- <></>
- );
+ ) : null;
heading = (headingText: string) => (
<div className="summary-heading">
<label className="summary-text">{headingText}</label>
- {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userVariantColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
+ {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
</div>
);
render() {
return (
<div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
- {this.mode === GPTPopupMode.SUMMARY? this.summaryBox() : this.mode === GPTPopupMode.DATA? this.dataAnalysisBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : <></>}
+ {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.DATA ? this.dataAnalysisBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.mode === GPTPopupMode.SORT ? this.sortBox() : null}
</div>
);
}