aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/apis/gpt/GPT.ts20
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx20
-rw-r--r--src/client/views/global/globalScripts.ts5
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx4
-rw-r--r--src/client/views/nodes/WebBox.tsx4
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx6
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx17
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss45
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx629
-rw-r--r--src/client/views/pdf/PDFViewer.tsx2
10 files changed, 285 insertions, 467 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index dc4607b94..066510a6a 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -1,6 +1,14 @@
import { ChatCompletionMessageParam, Image } from 'openai/resources';
import { openai } from './setup';
+export enum GPTTypeStyle {
+ AssignTags = 1,
+ Filter = 2,
+ DocInfo = 3,
+ ChooseDoc = 4,
+ GeneralInfo = 5,
+ SortDocs = 6,
+}
enum GPTCallType {
SUMMARY = 'summary',
COMPLETION = 'completion',
@@ -127,12 +135,12 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
temp: 0,
prompt: `I'm going to provide you with a question.
Based on the question, is the user asking you to
- 1. Assigns docs with tags(like star / heart etc)/labels,
- 2. Filter docs,
- 3. Provide information about a specific doc
- 4. Provide a specific doc based on a question/information
- 5. Provide general information
- 6. Put cards in a specific order.
+ ${GPTTypeStyle.AssignTags}. Assigns docs with tags(like star / heart etc)/labels,
+ ${GPTTypeStyle.ChooseDoc}. Filter docs,
+ ${GPTTypeStyle.DocInfo}. Provide information about a specific doc
+ ${GPTTypeStyle.Filter}. Provide a specific doc based on a question/information
+ ${GPTTypeStyle.GeneralInfo}. Provide general information
+ ${GPTTypeStyle.SortDocs}. Put cards in a specific order.
Answer with only the number for 2-6. For number one, provide the number (1) and the appropriate tag`,
},
subset: {
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 43464e50c..a3ec3884b 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -12,7 +12,7 @@ import { List } from '../../../fields/List';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
import { URLField } from '../../../fields/URLField';
-import { gptImageLabel } from '../../apis/gpt/GPT';
+import { gptImageLabel, GPTTypeStyle } from '../../apis/gpt/GPT';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
@@ -80,7 +80,7 @@ export class CollectionCardView extends CollectionSubView() {
childPairStringListAndUpdateSortDesc = () =>
this.childPairStringList().then(sortDesc => {
GPTPopup.Instance.setSortDesc(sortDesc.join());
- GPTPopup.Instance.onSortComplete = this.processGptOutput;
+ GPTPopup.Instance.onGptResponse = this.processGptResponse;
GPTPopup.Instance.onQuizRandom = this.quizMode;
});
@@ -113,7 +113,7 @@ export class CollectionCardView extends CollectionSubView() {
onGptHide = () => Doc.setDocFilter(this.Document, 'tags', '#chat', 'remove');
componentWillUnmount() {
GPTPopup.Instance.setSortDesc('');
- GPTPopup.Instance.onSortComplete = undefined;
+ GPTPopup.Instance.onGptResponse = undefined;
GPTPopup.Instance.onQuizRandom = undefined;
GPTPopup.Instance.setRegenerateCallback(undefined, null);
Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
@@ -443,18 +443,18 @@ export class CollectionCardView extends CollectionSubView() {
* @param questionType
* @param tag
*/
- processGptOutput = (gptOutput: string, questionType: string, tag?: string) =>
+ processGptResponse = (gptOutput: string, questionType: GPTTypeStyle, tag?: string) =>
undoable(() => {
// Split the string into individual list items
const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
- if (questionType === '2' || questionType === '4') {
+ if (questionType === GPTTypeStyle.Filter || questionType === GPTTypeStyle.ChooseDoc) {
this.childDocs.forEach(d => {
TagItem.removeTagFromDoc(d, '#chat');
});
}
- if (questionType === '6') {
+ if (questionType === GPTTypeStyle.SortDocs) {
this.Document[this._props.fieldKey + '_sort'] = docSortings.Chat;
}
@@ -464,18 +464,18 @@ export class CollectionCardView extends CollectionSubView() {
const doc = this._textToDoc.get(normalizedItem);
if (doc) {
switch (questionType) {
- case '6':
+ case GPTTypeStyle.SortDocs:
doc.chatIndex = index;
break;
- case '1':
+ case GPTTypeStyle.AssignTags:
if (tag) {
const hashTag = tag.startsWith('#') ? tag : '#' + tag[0].toLowerCase() + tag.slice(1);
const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(tag)) ?? hashTag;
TagItem.addTagToDoc(doc, filterTag);
}
break;
- case '2':
- case '4':
+ case GPTTypeStyle.Filter:
+ case GPTTypeStyle.ChooseDoc:
TagItem.addTagToDoc(doc, '#chat');
Doc.setDocFilter(this.Document, 'tags', '#chat', 'check');
break;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index b44292164..029c4dbc7 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -228,15 +228,10 @@ ScriptingGlobals.add(function showFreeform(
if (GPTPopup.Instance.Visible){
doc[Doc.LayoutFieldKey(doc)+"_sort"] = '';
GPTPopup.Instance.setVisible(false);
-
} else {
GPTPopup.Instance.setVisible(true);
GPTPopup.Instance.setMode(GPTPopupMode.CARD);
- GPTPopup.Instance.setCardsDoneLoading(true);
-
}
-
-
},
}],
['toggle-tags', {
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index b874d077b..fa3ab73a7 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -489,7 +489,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
// Changing which document to add the annotation to (the currently selected PDF)
- GPTPopup.Instance.setSidebarId('data_sidebar');
+ GPTPopup.Instance.setSidebarFieldKey('data_sidebar');
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
};
@@ -523,7 +523,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
askGPT = action(async () => {
- GPTPopup.Instance.setSidebarId('data_sidebar');
+ GPTPopup.Instance.setSidebarFieldKey('data_sidebar');
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
GPTPopup.Instance.createFilteredDoc = this.createFilteredDoc;
GPTPopup.Instance.setDataJson('');
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 6026d9ca7..e7a10cc29 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -383,7 +383,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
// Changing which document to add the annotation to (the currently selected WebBox)
- GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+ GPTPopup.Instance.setSidebarFieldKey(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
}
} else {
@@ -446,7 +446,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
(!sel.isCollapsed || this.marqueeing) && AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
// Changing which document to add the annotation to (the currently selected WebBox)
- GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+ GPTPopup.Instance.setSidebarFieldKey(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
}
};
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index eb1f9d07b..17b9bce47 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -135,7 +135,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
/**
* ApplyingChange - Marks whether an interactive text edit is currently in the process of being written to the database.
- * This is needed to distinguish changes to text fields caused by editing vs those caused by changes to
+ * This is needed to distinguish changes to text fields caused by editing vs those caused by changes to
* the prototype or other external edits
*/
public ApplyingChange: string = '';
@@ -1043,7 +1043,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
askGPT = action(async () => {
try {
- GPTPopup.Instance.setSidebarId(this.sidebarKey);
+ GPTPopup.Instance.setSidebarFieldKey(this.sidebarKey);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
const res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
if (!res) {
@@ -1660,7 +1660,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
};
onSelectEnd = () => {
- GPTPopup.Instance.setSidebarId(this.sidebarKey);
+ GPTPopup.Instance.setSidebarFieldKey(this.sidebarKey);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
document.removeEventListener('pointerup', this.onSelectEnd);
};
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 18da01890..f7070c780 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -1,5 +1,5 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from '@dash/components';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -9,16 +9,15 @@ import { ClientUtils, returnFalse, setupMoveUpEvents } from '../../../ClientUtil
import { emptyFunction, unimplementedFunction } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
-import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
import { SettingsManager } from '../../util/SettingsManager';
import { undoBatch } from '../../util/UndoManager';
import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
import { LinkPopup } from '../linking/LinkPopup';
+import { ComparisonBox } from '../nodes/ComparisonBox';
import { DocumentView } from '../nodes/DocumentView';
import { DrawingOptions, SmartDrawHandler } from '../smartdraw/SmartDrawHandler';
import './AnchorMenu.scss';
-import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup';
-import { ComparisonBox } from '../nodes/ComparisonBox';
+import { GPTPopup } from './GPTPopup/GPTPopup';
@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -98,15 +97,7 @@ 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.setVisible(true);
- GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
- GPTPopup.Instance.setLoading(true);
- gptAPICall(this._selectedText, GPTCallType.SUMMARY)
- .then(res => GPTPopup.Instance.setText(res || 'Something went wrong.'))
- .catch(err => console.error(err))
- .finally(() => GPTPopup.Instance.setLoading(false));
- };
+ gptSummarize = () => GPTPopup.Instance.generateSummary(this._selectedText);
/*
* Transfers the flashcard text generated by GPT on flashcards and creates a collection out them.
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 0247dc10c..9cf318dc0 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -5,7 +5,7 @@ $lightgrey: #ececec;
$button: #5b97ff;
$highlightedText: #82e0ff;
-.summary-box {
+.gptPopup-summary-box {
position: fixed;
top: 115px;
left: 75px;
@@ -35,7 +35,7 @@ $highlightedText: #82e0ff;
right: 0;
bottom: 0;
cursor: se-resize;
- }
+ }
.summary-heading {
display: flex;
@@ -63,12 +63,12 @@ $highlightedText: #82e0ff;
cursor: pointer;
}
- .content-wrapper {
+ .gptPopup-content-wrapper {
padding-top: 10px;
min-height: 50px;
// max-height: 150px;
overflow-y: auto;
- height: 100%
+ height: 100%;
}
.btns-wrapper-gpt {
@@ -78,7 +78,7 @@ $highlightedText: #82e0ff;
align-items: center;
flex-direction: column;
- .inputWrapper{
+ .inputWrapper {
display: flex;
justify-content: center;
align-items: center;
@@ -87,17 +87,15 @@ $highlightedText: #82e0ff;
bottom: 0;
width: 100%;
background-color: white;
-
-
}
- .searchBox-input{
+ .searchBox-input {
height: 40px;
border-radius: 10px;
position: absolute;
bottom: 10px;
border-color: #5b97ff;
- width: 90%
+ width: 90%;
}
.chat-wrapper {
@@ -106,52 +104,41 @@ $highlightedText: #82e0ff;
width: 100%;
max-height: calc(100vh - 80px);
overflow-y: auto;
- padding-bottom: 60px;
+ padding-bottom: 60px;
}
-
+
.chat-bubbles {
margin-top: 20px;
display: flex;
flex-direction: column;
flex-grow: 1;
}
-
+
.chat-bubble {
padding: 10px;
margin-bottom: 10px;
border-radius: 10px;
max-width: 60%;
}
-
+
.user-message {
background-color: #283d53;
align-self: flex-end;
color: whitesmoke;
}
-
+
.chat-message {
background-color: #367ae7;
align-self: flex-start;
- color:whitesmoke;
+ color: whitesmoke;
}
-
-
-
.summarizing {
display: flex;
align-items: center;
}
-
-
-
-
-
-
}
-
-
.text-btn {
&:hover {
background-color: $button;
@@ -198,18 +185,12 @@ $highlightedText: #82e0ff;
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 2d95ac2eb..691c24792 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,6 +1,6 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, IconButton, Toggle, ToggleType, Type } from '@dash/components';
-import { action, makeObservable, observable, runInAction } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { CgClose, CgCornerUpLeft } from 'react-icons/cg';
@@ -10,7 +10,7 @@ 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 { GPTCallType, GPTTypeStyle, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
import { DocUtils } from '../../../documents/DocUtils';
import { Docs } from '../../../documents/Documents';
import { SettingsManager } from '../../../util/SettingsManager';
@@ -19,7 +19,6 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocumentView } from '../../nodes/DocumentView';
import { AnchorMenu } from '../AnchorMenu';
import './GPTPopup.scss';
-import { isJSDocProtectedTag } from 'typescript';
export enum GPTPopupMode {
SUMMARY,
@@ -42,133 +41,79 @@ export enum GPTQuizType {
export class GPTPopup extends ObservableReactComponent<object> {
// eslint-disable-next-line no-use-before-define
static Instance: GPTPopup;
- private messagesEndRef: React.RefObject<HTMLDivElement>;
-
- @observable private chatMode: boolean = false;
- private correlatedColumns: string[] = [];
-
- @observable public Visible: boolean = false;
- @action public setVisible = (vis: boolean) => {
- this.Visible = vis;
- };
- @observable public loading: boolean = false;
- @action public setLoading = (loading: boolean) => {
- this.loading = loading;
- };
- @observable public text: string = '';
- @action public setText = (text: string) => {
- this.text = text;
- };
- @observable public selectedText: string = '';
- @action public setSelectedText = (text: string) => {
- this.selectedText = text;
- };
- @observable public dataJson: string = '';
- public dataChatPrompt: string | undefined = undefined;
- @action public setDataJson = (text: string) => {
- if (text === '') this.dataChatPrompt = '';
- this.dataJson = text;
- };
-
- @observable public imgDesc: string = '';
- @action public setImgDesc = (text: string) => {
- this.imgDesc = text;
- };
-
- @observable public imgUrls: string[][] = [];
- @action public setImgUrls = (imgs: string[][]) => {
- this.imgUrls = imgs;
- };
-
- @observable public mode: GPTPopupMode = GPTPopupMode.SUMMARY;
- @action public setMode = (mode: GPTPopupMode) => {
- this.mode = mode;
- };
-
- @observable public highlightRange: number[] = [];
- @action callSummaryApi = () => {};
-
- @observable private done: boolean = false;
- @action public setDone = (done: boolean) => {
- this.done = done;
- this.chatMode = false;
- };
-
- // change what can be a ref into a ref
- @observable private sidebarId: string = '';
- @action public setSidebarId = (id: string) => {
- this.sidebarId = id;
- };
-
- @observable private imgTargetDoc: Doc | undefined;
- @action public setImgTargetDoc = (anchor: Doc) => {
- this.imgTargetDoc = anchor;
- };
-
- @observable private textAnchor: Doc | undefined;
- @action public setTextAnchor = (anchor: Doc) => {
- this.textAnchor = anchor;
+ private _regenerateCallback: (() => Promise<void>) | null = null;
+ private _messagesEndRef: React.RefObject<HTMLDivElement>;
+ private _correlatedColumns: string[] = [];
+ private _dataChatPrompt: string | undefined = undefined;
+ private _imgTargetDoc: Doc | undefined;
+ private _textAnchor: Doc | undefined;
+ private _dataJson: string = '';
+ private _sortDesc: string = '';
+ private _sidebarFieldKey: string = '';
+ private _imgDesc: string = '';
+ private _selectedText: string = '';
+
+ public setImgDesc = (text: string) => (this._imgDesc = text);
+ public setSidebarFieldKey = (id: string) => (this._sidebarFieldKey = id);
+ public setSortDesc = (t: string) => (this._sortDesc = t);
+ public setImgTargetDoc = (anchor: Doc) => (this._imgTargetDoc = anchor);
+ public setTextAnchor = (anchor: Doc) => (this._textAnchor = anchor);
+ public onGptResponse?: (sortResult: string, questionType: GPTTypeStyle, tag?: string) => void;
+ public onQuizRandom?: () => void;
+ public setDataJson = (text: string) => {
+ if (text === '') this._dataChatPrompt = '';
+ this._dataJson = text;
};
- @observable public sortDesc: string = '';
- @action public setSortDesc = (t: string) => {
- this.sortDesc = t;
- };
-
- onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void;
- onQuizRandom?: () => void;
-
- @observable collectionDoc: Doc | undefined = undefined;
- @action setCollectionDoc(doc: Doc | undefined) {
- this.collectionDoc = doc;
- }
-
- @observable sortRespText: string = '';
-
- @action setSortRespText(resp: string) {
- this.sortRespText = resp;
+ constructor(props: object) {
+ super(props);
+ makeObservable(this);
+ GPTPopup.Instance = this;
+ this._messagesEndRef = React.createRef();
}
- @observable chatSortPrompt: string = '';
-
- sortPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
- this.chatSortPrompt = e.target.value;
- });
+ componentDidUpdate = () => this._gptProcessing && this.setStopAnimatingResponse(false);
- @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 private _conversationArray: string[] = ['Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. '];
+ @observable private _chatEnabled: boolean = false;
+ @action private setChatEnabled = (start: boolean) => (this._chatEnabled = start);
+ @observable public Visible: boolean = false;
+ @action public setVisible = (vis: boolean) => (this.Visible = vis);
+ @observable private _gptProcessing: boolean = false;
+ @action public setGptProcessing = (loading: boolean) => (this._gptProcessing = loading);
+ @observable private _responseText: string = '';
+ @action public setResponseText = (text: string) => (this._responseText = text);
+ @observable private _imgUrls: string[][] = [];
+ @action public setImgUrls = (imgs: string[][]) => (this._imgUrls = imgs);
+ @observable private _mode: GPTPopupMode = GPTPopupMode.SUMMARY;
+ @action public setMode = (mode: GPTPopupMode) => (this._mode = mode);
+ @observable private _collectionContext: Doc | undefined = undefined;
+ @action setCollectionContext = (doc: Doc | undefined) => (this._collectionContext = doc);
+ @observable private _sortPrompt: string = '';
+ @action setSortPrompt = (e: React.ChangeEvent<HTMLInputElement>) => (this._sortPrompt = e.target.value);
+ @observable private _quizAnswer: string = '';
+ @action setQuizAnswer = (e: React.ChangeEvent<HTMLInputElement>) => (this._quizAnswer = e.target.value);
+ @observable private _stopAnimatingResponse: boolean = false;
+ @action public setStopAnimatingResponse = (done: boolean) => (this._stopAnimatingResponse = done);
/**
- * When the cards are in quiz mode in the card view, allows gpt to determine whether the user's answer was correct
- * @returns
+ * Callback function that causes the card view to update the childpair string list
+ * @param callback
*/
- generateQuiz = (selected: Doc) =>
- (this.regenerateCallback?.() ?? Promise.resolve()).then(() =>
- this.generateRubric(selected).then(() =>
- gptAPICall('Question: ' + StrCast(selected.gptInputText) + ' UserAnswer: ' + this.quizAnswer + '. Rubric: ' + StrCast(selected.gptRubric), GPTCallType.QUIZ)
- .then(res => {
- if (res) {
- this.setQuizResp(res);
- this.conversationArray.push(res);
- this.onQuizRandom?.();
- } else {
- console.error('GPT provided no response');
- }
- })
- .catch(err => console.error('GPT call failed', err))
- )
- );
+ public setRegenerateCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) {
+ this.setCollectionContext(collectionDoc);
+ 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;
+ public questionTypeNumberToStyle = (questionType: string) => +questionType.split(' ')[0][0];
/**
- * Generates a rubric by which to compare the user's answer to
- * @param inputText user's answer
+ * Generates a rubric for evaluating the user's description of the document's text
* @param doc the doc the user is providing info about
- * @returns gpt's response
+ * @returns gpt's response rubric
*/
generateRubric = (doc: Doc) =>
StrCast(doc.gptRubric)
@@ -177,148 +122,129 @@ export class GPTPopup extends ObservableReactComponent<object> {
.then(res => (doc.gptRubric = res))
.catch(err => console.error('GPT call failed', err));
- @observable private regenerateCallback: (() => Promise<void>) | null = null;
-
/**
- * Callback function that causes the card view to update the childpair string list
- * @param callback
+ * When the cards are in quiz mode in the card view, allows gpt to determine whether the user's answer was correct
+ * @param doc the doc the user is providing info about
+ * @param quizAnswer the user's answer/description for the document
+ * @returns
*/
- @action public setRegenerateCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) {
- this.setCollectionDoc(collectionDoc);
- 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;
- }
+ generateQuizAnswerAnalysis = (doc: Doc, quizAnswer: string) =>
+ (this._regenerateCallback?.() ?? Promise.resolve()).then(
+ () =>
+ this.generateRubric(doc).then(() =>
+ gptAPICall(
+ `Question: ${StrCast(doc.gptInputText)};
+ UserAnswer: ${quizAnswer};
+ Rubric: ${StrCast(doc.gptRubric)}`,
+ GPTCallType.QUIZ
+ ).then(res => {
+ this._conversationArray.push(res || 'GPT provided no answer');
+ this.onQuizRandom?.();
+ })
+ .catch(err => console.error('GPT call failed', err))
+ ) // prettier-ignore
+ );
/**
* Generates a response to the user's question depending on the type of their question
+ * @param userPrompt the user's input that chat will respond to
*/
- generateCard = async () => {
- this.setLoading(true);
-
- await this.regenerateCallback?.();
-
- try {
- const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE);
- const questionNumber = questionType.split(' ')[0][0];
- const res = await (() => {
- switch (questionNumber) {
- case '1':
- case '2':
- case '4': return gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt);
- case '6': return gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
- default: return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, this.chatSortPrompt);
- }})(); // prettier-ignore
-
- // Trigger the callback with the result
- if (this.onSortComplete) {
- this.onSortComplete(res || 'Something went wrong :(', questionNumber, questionType.split(' ').slice(1).join(' '));
-
- 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';
- }
-
- // Set the extracted explanation to sortRespText
- this.setSortRespText(explanation);
- runInAction(() => this.conversationArray.push(this.sortRespText));
- this.scrollToBottom();
-
- console.log(res);
- }
- } catch (err) {
- console.error(err);
- }
-
- this.setLoading(false);
- };
+ generateQueryResponse = (userPrompt: string) =>
+ (this._regenerateCallback ?? Promise.resolve)().then(() =>
+ gptAPICall(userPrompt, GPTCallType.TYPE).then(questionType =>
+ (() => {
+ switch (this.questionTypeNumberToStyle(questionType)) {
+ case GPTTypeStyle.AssignTags:
+ case GPTTypeStyle.Filter:
+ case GPTTypeStyle.ChooseDoc: return gptAPICall(this._sortDesc, GPTCallType.SUBSET, userPrompt);
+ case GPTTypeStyle.SortDocs: return gptAPICall(this._sortDesc, GPTCallType.SORT, userPrompt);
+ default: return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, userPrompt);
+ } // prettier-ignore
+ })().then(
+ action(res => {
+ // Trigger the callback with the result
+ this.onGptResponse?.(res || 'Something went wrong :(', this.questionTypeNumberToStyle(questionType), questionType.split(' ').slice(1).join(' '));
+ this._conversationArray.push(
+ ![GPTTypeStyle.GeneralInfo, GPTTypeStyle.DocInfo].includes(this.questionTypeNumberToStyle(questionType))?
+ // Extract explanation surrounded by ------ at the top or both at the top and bottom
+ (res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) ?? [])[1]?.trim() ?? 'No explanation found' : res);
+ })
+ ).catch(err => console.log(err))
+ ).catch(err => console.log(err))
+ ); // prettier-ignore
/**
* Generates a Dalle image and uploads it to the server.
*/
- generateImage = async () => {
- if (this.imgDesc === '') return undefined;
- this.setImgUrls([]);
- this.setMode(GPTPopupMode.IMAGE);
- this.setVisible(true);
- this.setLoading(true);
-
- try {
- 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);
+ generateImage = () => {
+ if (this._imgDesc !== '') {
+ this.setImgUrls([]);
+ this.setMode(GPTPopupMode.IMAGE);
+ this.setVisible(true);
+ this.setGptProcessing(true);
+
+ return gptImageCall(this._imgDesc)
+ .then(imageUrls =>
+ imageUrls?.[0]
+ ? Networking.PostToServer('/uploadRemoteImage', { sources: [imageUrls[0]] }).then(res => {
+ const source = ClientUtils.prepend(res[0].accessPaths.agnostic.client);
+ return this.setImgUrls([[imageUrls[0]!, source]]);
+ })
+ : undefined
+ )
+ .catch(err => console.error(err))
+ .finally(() => this.setGptProcessing(false));
}
- this.setLoading(false);
return undefined;
};
/**
- * Completes an API call to generate a summary of
- * this.selectedText in the popup.
+ * Completes an API call to generate a summary of the specified text
+ *
+ * @param text the text to summarizz
*/
- generateSummary = async () => {
- GPTPopup.Instance.setVisible(true);
- GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
- GPTPopup.Instance.setLoading(true);
-
- try {
- const res = await gptAPICall(this.selectedText, GPTCallType.SUMMARY);
- GPTPopup.Instance.setText(res || 'Something went wrong.');
- } catch (err) {
- console.error(err);
- }
- GPTPopup.Instance.setLoading(false);
+ generateSummary = (text?: string) => {
+ this._selectedText = text ?? this._selectedText;
+ this.setVisible(true);
+ this.setMode(GPTPopupMode.SUMMARY);
+ this.setGptProcessing(true);
+ return gptAPICall(this._selectedText, GPTCallType.SUMMARY)
+ .then(res => this.setResponseText(res || 'Something went wrong.'))
+ .catch(err => console.error(err))
+ .finally(() => this.setGptProcessing(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 {
- 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);
+ generateDataAnalysis = () => {
+ this.setVisible(true);
+ 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.');
+ })
+ .catch(err => console.error(err))
+ .finally(() => this.setGptProcessing(false));
};
/**
* Transfers the summarization text to a sidebar annotation text document.
*/
private transferToText = () => {
- const newDoc = Docs.Create.TextDocument(this.text.trim(), {
+ const newDoc = Docs.Create.TextDocument(this._responseText.trim(), {
_width: 200,
_height: 50,
_layout_fitWidth: true,
_layout_autoHeight: true,
});
- this.addDoc(newDoc, this.sidebarId);
- // newDoc.data = 'Hello world';
+ this.addDoc(newDoc, this._sidebarFieldKey);
const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
if (anchor) {
DocUtils.MakeLink(newDoc, anchor, {
@@ -330,73 +256,35 @@ export class GPTPopup extends ObservableReactComponent<object> {
/**
* Creates a histogram to show the correlation relationship that was found
*/
- private createVisualization = () => {
- this.createFilteredDoc(this.correlatedColumns);
- };
+ private createVisualization = () => this.createFilteredDoc(this._correlatedColumns);
/**
* Transfers the image urls to actual image docs
*/
private transferToImage = (source: string) => {
- const textAnchor = this.textAnchor ?? this.imgTargetDoc;
- if (!textAnchor) return;
- const newDoc = Docs.Create.ImageDocument(source, {
- x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
- y: NumCast(textAnchor.y),
- _height: 200,
- _width: 200,
- data_nativeWidth: 1024,
- data_nativeHeight: 1024,
- });
- if (Doc.IsInMyOverlay(textAnchor)) {
- newDoc.overlayX = textAnchor.x;
- newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
- Doc.AddToMyOverlay(newDoc);
- } else {
- this.addToCollection?.(newDoc);
- }
- // Create link between prompt and image
- DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
- };
-
- /**
- * Creates a chatbox for analyzing data so that users can ask specific questions.
- */
- private chatWithAI = () => {
- this.chatMode = true;
- };
- dataPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
- this.dataChatPrompt = e.target.value;
- });
-
- private getPreviewUrl = (source: string) => source.split('.').join('_m.');
-
- constructor(props: object) {
- super(props);
- makeObservable(this);
- GPTPopup.Instance = this;
- this.messagesEndRef = React.createRef();
- }
-
- scrollToBottom = () => {
- setTimeout(() => {
- // Code to execute after 1 second (1000 ms)
- if (this.messagesEndRef.current) {
- this.messagesEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });
+ const textAnchor = this._textAnchor ?? this._imgTargetDoc;
+ if (textAnchor) {
+ const newDoc = Docs.Create.ImageDocument(source, {
+ x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
+ y: NumCast(textAnchor.y),
+ _height: 200,
+ _width: 200,
+ data_nativeWidth: 1024,
+ data_nativeHeight: 1024,
+ });
+ if (Doc.IsInMyOverlay(textAnchor)) {
+ newDoc.overlayX = textAnchor.x;
+ newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
+ Doc.AddToMyOverlay(newDoc);
+ } else {
+ this.addToCollection?.(newDoc);
}
- }, 50);
- };
-
- componentDidUpdate = () => {
- if (this.loading) {
- this.setDone(false);
+ // Create link between prompt and image
+ DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
}
};
- @observable quizMode: GPTQuizType = GPTQuizType.CURRENT;
- @action setQuizMode(g: GPTQuizType) {
- this.quizMode = g;
- }
+ scrollToBottom = () => setTimeout(() => this._messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' }), 50);
cardMenu = () => (
<div className="btns-wrapper-gpt">
@@ -419,7 +307,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
tooltip="Test your knowledge with ChatGPT!"
text="Quiz Cards!"
onClick={() => {
- this.conversationArray = ['Define the selected card!'];
+ this._conversationArray = ['Define the selected card!'];
this.setMode(GPTPopupMode.QUIZ);
this.onQuizRandom?.();
}}
@@ -437,27 +325,19 @@ export class GPTPopup extends ObservableReactComponent<object> {
);
@action
- handleKeyPress = (e: React.KeyboardEvent, isSort: boolean) => {
+ handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => {
if (e.key === 'Enter') {
e.stopPropagation();
+ this.setGptProcessing(true);
if (isSort) {
- this.conversationArray.push(this.chatSortPrompt);
- this.generateCard().then(
- action(() => {
- this.chatSortPrompt = '';
- })
- );
+ this._conversationArray.push(this._sortPrompt);
+ await this.generateQueryResponse(this._sortPrompt).then(action(() => (this._sortPrompt = '')));
} else {
- this.conversationArray.push(this.quizAnswer);
- this.setLoading(true);
- this.generateQuiz(DocumentView.SelectedDocs().lastElement()).then(
- action(() => {
- this.quizAnswer = '';
- this.setLoading(false);
- })
- );
+ this._conversationArray.push(this._quizAnswer);
+ await this.generateQuizAnswerAnalysis(DocumentView.SelectedDocs().lastElement(), this._quizAnswer).then(action(() => (this._quizAnswer = '')));
}
+ this.setGptProcessing(false);
this.scrollToBottom();
}
@@ -467,26 +347,24 @@ export class GPTPopup extends ObservableReactComponent<object> {
<div className="btns-wrapper-gpt">
<div className="chat-wrapper">
<div className="chat-bubbles">
- {this.conversationArray.map((message, index) => (
+ {this._conversationArray.map((message, index) => (
<div key={index} className={`chat-bubble ${index % 2 === 1 ? 'user-message' : 'chat-message'}`}>
{message}
</div>
))}
- {this.loading && <div className={`chat-bubble chat-message`}>...</div>}
+ {this._gptProcessing && <div className={`chat-bubble chat-message`}>...</div>}
</div>
- <div ref={this.messagesEndRef} style={{ height: '100px' }} />
+ <div ref={this._messagesEndRef} style={{ height: '100px' }} />
</div>
<div className="inputWrapper">
<input
className="searchBox-input"
defaultValue=""
- value={isSort ? this.chatSortPrompt : this.quizAnswer} // Controlled input
+ value={isSort ? this._sortPrompt : this._quizAnswer} // Controlled input
autoComplete="off"
- onChange={isSort ? this.sortPromptChanged : this.quizAnswerChanged}
- onKeyDown={e => {
- this.handleKeyPress(e, isSort);
- }}
+ onChange={isSort ? this.setSortPrompt : this.setQuizAnswer}
+ 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!'}`}
/>
@@ -497,7 +375,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
sortBox = (isSort: boolean) => (
<div className="gptPopup-sortBox" style={{ height: '80%' }}>
{this.heading(isSort ? 'SORTING' : 'QUIZ')}
- {this.mode === GPTPopupMode.CARD ? this.cardMenu() : this.cardActual(isSort)}
+ {this._mode === GPTPopupMode.CARD ? this.cardMenu() : this.cardActual(isSort)}
</div>
);
@@ -505,7 +383,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
{this.heading('GENERATED IMAGE')}
<div className="image-content-wrapper">
- {this.imgUrls.map((rawSrc, i) => (
+ {this._imgUrls.map((rawSrc, i) => (
<div key={rawSrc[0] + i} className="img-wrapper">
<div className="img-container">
<img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
@@ -516,7 +394,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
</div>
))}
</div>
- {!this.loading && <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />}
+ {this._gptProcessing ? null : <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />}
</div>
);
@@ -524,44 +402,35 @@ export class GPTPopup extends ObservableReactComponent<object> {
<>
<div>
{this.heading('SUMMARY')}
- <div className="content-wrapper">
- {!this.loading &&
- (!this.done ? (
+ <div className="gptPopup-content-wrapper">
+ {!this._gptProcessing &&
+ (!this._stopAnimatingResponse ? (
<TypeAnimation
speed={50}
sequence={[
- this.text,
+ this._responseText,
() => {
- setTimeout(() => {
- this.setDone(true);
- }, 500);
+ setTimeout(() => this.setStopAnimatingResponse(true), 500);
},
]}
/>
) : (
- this.text
+ this._responseText
))}
</div>
</div>
- {!this.loading && (
+ {!this._gptProcessing && (
<div className="btns-wrapper">
- {this.done ? (
+ {this._stopAnimatingResponse ? (
<>
- <IconButton tooltip="Generate Again" onClick={this.generateSummary /* this.callSummaryApi */} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(SettingsManager.userVariantColor)} />
+ <IconButton tooltip="Generate Again" onClick={() => this.generateSummary(this._selectedText + ' ')} 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">
<span>Summarizing</span>
<ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
- <Button
- text="Stop Animation"
- onClick={() => {
- this.setDone(true);
- }}
- color={StrCast(SettingsManager.userVariantColor)}
- type={Type.TERT}
- />
+ <Button text="Stop Animation" onClick={() => this.setStopAnimatingResponse(true)} color={StrCast(SettingsManager.userVariantColor)} type={Type.TERT} />
</div>
)}
</div>
@@ -573,33 +442,31 @@ export class GPTPopup extends ObservableReactComponent<object> {
<>
<div>
{this.heading('ANALYSIS')}
- <div className="content-wrapper">
- {!this.loading &&
- (!this.done ? (
+ <div className="gptPopup-content-wrapper">
+ {!this._gptProcessing &&
+ (!this._stopAnimatingResponse ? (
<TypeAnimation
speed={50}
sequence={[
- this.text,
+ this._responseText,
() => {
- setTimeout(() => {
- this.setDone(true);
- }, 500);
+ setTimeout(() => this.setStopAnimatingResponse(true), 500);
},
]}
/>
) : (
- this.text
+ this._responseText
))}
</div>
</div>
- {!this.loading && (
+ {!this._gptProcessing && (
<div className="btns-wrapper">
- {this.done ? (
- this.chatMode ? (
+ {this._stopAnimatingResponse ? (
+ this._chatEnabled ? (
<input
defaultValue=""
autoComplete="off"
- onChange={this.dataPromptChanged}
+ onChange={e => (this._dataChatPrompt = e.target.value)}
onKeyDown={e => {
e.key === 'Enter' ? this.generateDataAnalysis() : null;
e.stopPropagation();
@@ -613,21 +480,14 @@ export class GPTPopup extends ObservableReactComponent<object> {
) : (
<>
<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} />
+ <Button tooltip="Chat with AI" text="Chat with AI" onClick={() => this.setChatEnabled(true)} 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(SnappingManager.userVariantColor)}
- type={Type.TERT}
- />
+ <Button text="Stop Animation" onClick={() => this.setStopAnimatingResponse(true)} color={StrCast(SnappingManager.userVariantColor)} type={Type.TERT} />
</div>
)}
</div>
@@ -636,70 +496,53 @@ export class GPTPopup extends ObservableReactComponent<object> {
);
aiWarning = () =>
- this.done ? (
+ !this._stopAnimatingResponse ? null : (
<div className="ai-warning">
<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 ? (
+ {this._gptProcessing ? (
<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' }} />
)}
<Toggle
tooltip="Clear Chat filter"
toggleType={ToggleType.BUTTON}
type={Type.PRIM}
- toggleStatus={Doc.hasDocFilter(this.collectionDoc, 'tags', '#chat')}
- text={Doc.hasDocFilter(this.collectionDoc, 'tags', '#chat') ? 'filtered' : ''}
+ toggleStatus={Doc.hasDocFilter(this._collectionContext, 'tags', '#chat')}
+ text={Doc.hasDocFilter(this._collectionContext, 'tags', '#chat') ? 'filtered' : ''}
color="red"
- onClick={() => this.collectionDoc && Doc.setDocFilter(this.collectionDoc, 'tags', '#chat', 'remove')}
- />
- <IconButton
- color={StrCast(SettingsManager.userVariantColor)}
- tooltip="close"
- icon={<CgClose size="16px" />}
- onClick={() => {
- this.setVisible(false);
- }}
+ onClick={() => this._collectionContext && Doc.setDocFilter(this._collectionContext, 'tags', '#chat', 'remove')}
/>
+ <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />
</>
)}
</div>
);
render() {
- let content;
-
- switch (this.mode) {
- case GPTPopupMode.SUMMARY:
- content = this.summaryBox();
- break;
- case GPTPopupMode.DATA:
- content = this.dataAnalysisBox();
- break;
- case GPTPopupMode.IMAGE:
- content = this.imageBox();
- break;
- case GPTPopupMode.SORT:
- case GPTPopupMode.CARD:
- case GPTPopupMode.QUIZ:
- content = this.sortBox(this.mode === GPTPopupMode.SORT);
- break;
- default:
- content = null;
- }
-
return (
- <div className="summary-box" style={{ display: this.Visible ? 'flex' : 'none' }}>
- {content}
+ <div className="gptPopup-summary-box" style={{ display: this.Visible ? 'flex' : 'none' }}>
+ {(() => {
+ //prettier-ignore
+ switch (this._mode) {
+ case GPTPopupMode.SORT:
+ case GPTPopupMode.CARD:
+ case GPTPopupMode.QUIZ: return this.sortBox(this._mode === GPTPopupMode.SORT);
+ case GPTPopupMode.SUMMARY: return this.summaryBox();
+ case GPTPopupMode.DATA: return this.dataAnalysisBox();
+ case GPTPopupMode.IMAGE: return this.imageBox();
+ default: return null;
+ }
+ })()}
<div className="resize-handle" />
</div>
);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 8728ce99c..167421a4a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -437,7 +437,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
}
- GPTPopup.Instance.setSidebarId('data_sidebar');
+ GPTPopup.Instance.setSidebarFieldKey('data_sidebar');
GPTPopup.Instance.addDoc = this._props.sidebarAddDoc;
// allows for creating collection
AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument;