From c5b2912cc4f44c5e12d8b5dc123d99bf9c4d9b85 Mon Sep 17 00:00:00 2001 From: alyssaf16 Date: Wed, 17 Apr 2024 13:17:51 -0400 Subject: ai flashcards --- src/client/apis/gpt/GPT.ts | 4 +- src/client/views/nodes/ComparisonBox.tsx | 71 +++++++++++------------------- src/client/views/pdf/AnchorMenu.tsx | 24 +++++++++- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 42 +++++++++++------- src/client/views/pdf/PDFViewer.tsx | 1 + 5 files changed, 79 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 968b45273..2757fc830 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -17,7 +17,7 @@ type GPTCallOpts = { const callTypeMap: { [type: string]: GPTCallOpts } = { summary: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' }, edit: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' }, - flashcard: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Make flashcards out of this text with questions on the front and answers on the back: ' }, + flashcard: { model: 'gpt-3.5-turbo-instruct', maxTokens: 512, temp: 0.5, prompt: 'Make flashcards out of this text with questions and answers: ' }, completion: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: '' }, }; @@ -28,7 +28,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { * @returns AI Output */ const gptAPICall = async (inputText: string, callType: GPTCallType) => { - if (callType === GPTCallType.SUMMARY) inputText += '.'; + if (callType === GPTCallType.SUMMARY || callType == GPTCallType.FLASHCARD) inputText += '.'; const opts: GPTCallOpts = callTypeMap[callType]; try { const configuration: ClientOptions = { diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index ce862cd22..76631f071 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -3,7 +3,7 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils'; -import { Doc, Opt } from '../../../fields/Doc'; +import { Doc, Opt, DocListCast } from '../../../fields/Doc'; import { DocCast, NumCast, RTFCast, StrCast } from '../../../fields/Types'; import { DocUtils, Docs } from '../../documents/Documents'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -21,6 +21,7 @@ import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { RichTextField } from '../../../fields/RichTextField'; import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT'; import { DocData } from '../../../fields/DocSymbols'; +import { KeyValueBox } from './KeyValueBox'; @observer export class ComparisonBox extends ViewBoxAnnotatableComponent() implements ViewBoxInterface { @@ -200,6 +201,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() console.log(this.layoutDoc[`_${this._props.fieldKey}_revealOp`]); if (!this.layoutDoc[`_${this._props.fieldKey}_revealOp`] || this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'flip') { this.flipFlashcard(); + console.log('Print context of cards: ' + DocCast(this.dataDoc[this.fieldKey + '_1']).text); } }) } @@ -214,6 +216,18 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() ); } + askGPT = async (): Promise => { + try { + let res = await gptAPICall(StrCast(this.dataDoc.data), GPTCallType.COMPLETION); + if (!res) { + console.error('GPT call failed'); + return; + } + } catch (err) { + console.error('GPT call failed'); + } + }; + render() { const clearButton = (which: string) => { return ( @@ -232,24 +246,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() const whichDoc = DocCast(this.dataDoc[which]); const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc); // if there is no Doc in the first comparison slot, but the comparison box's fieldKey slot has a RichTextField, then render a text box to show the contents of the document's field key slot - //const layoutTemplateString = !targetDoc && which.endsWith('1') && this.Document[this.fieldKey] instanceof RichTextField ? FormattedTextBox.LayoutString(this.fieldKey) : undefined; - const subjectText = RTFCast(this.Document[this.fieldKey])?.Text; - const layoutTemplateString = !targetDoc - ? which.endsWith('0') && subjectText !== undefined - ? FormattedTextBox.LayoutString(this.fieldKey) - : which.endsWith('1') && (this.Document[which] instanceof RichTextField || typeof this.Document[which] === 'string') - ? FormattedTextBox.LayoutString(which) - : undefined - : undefined; - - if (which.endsWith('1') && !layoutTemplateString && targetDoc) { - const queryText = RTFCast(this.Document[this.fieldKey + '_1'])?.Text; - console.log('QUER' + queryText); - if (queryText?.includes('--TEXT--') && subjectText) { - this.Document[DocData][this.fieldKey + '_1'] = ''; - gptAPICall(queryText?.replace('--TEXT--', subjectText), GPTCallType.COMPLETION).then(value => (this.Document[DocData][this.fieldKey + '_1'] = value.trim())); - } - } + const layoutTemplateString = !targetDoc && which.endsWith('1') && this.Document[this.fieldKey] instanceof RichTextField ? FormattedTextBox.LayoutString(this.fieldKey) : undefined; return targetDoc || layoutTemplateString ? ( <> @@ -315,15 +312,20 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() if (this.Document._layout_isFlashcard) { const side = this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 1 : 0; - // add text box when first created + if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] == 'empty')) { - console.log('TEXT HERE' + this.dataDoc.data); - const newDoc = Docs.Create.TextDocument(StrCast(this.dataDoc.data)); + const dataSplit = StrCast(this.dataDoc.data).split('Answer'); + const newDoc = Docs.Create.TextDocument(dataSplit[1]); + newDoc.text = dataSplit[1]; this.addDoc(newDoc, this.fieldKey + '_0'); } - // if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] == 'empty')) this.dataDoc[this.fieldKey + '_0'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); - if (!(this.dataDoc[this.fieldKey + '_1'] || this.dataDoc[this.fieldKey + '_1'] == 'empty')) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); + if (!(this.dataDoc[this.fieldKey + '_1'] || this.dataDoc[this.fieldKey + '_1'] == 'empty')) { + const dataSplit = StrCast(this.dataDoc.data).split('Answer'); + const newDoc = Docs.Create.TextDocument(dataSplit[0]); + newDoc.text = 'jhkhkjh'; + this.addDoc(newDoc, this.fieldKey + '_1'); + } if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hide/reveal') { { @@ -333,21 +335,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() {displayBox(`${this.fieldKey}_1`, 1, this._props.PanelHeight() - 3)} ); - // return ( - //
- // {displayBox(`${this.fieldKey}_2`, 1, this._props.PanelHeight() - 3)} - //
- // {displayBox(`${this.fieldKey}_1`, 0, 0)} - //
- //
- // ); - // return ( - //
- // {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelHeight() / 2)} - // {displayBox(`${this.fieldKey}_${side === 0 ? 0 : 1}`, 1, this._props.PanelHeight() / 2)} - - //
- // ); } } else { return ( @@ -362,12 +349,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() this.hoverFlip(undefined); }}> {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)} - {/* {displayBoxReveal(`${this.fieldKey}_${side}`, side, this._props.PanelWidth() - 3)} - {displayBoxReveal(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side === 0 ? 1 : 0, this._props.PanelWidth() - 3)} */} - {/*
{displayBoxReveal(`${this.fieldKey}_${side}`, side, this._props.PanelWidth() - 3)}
*/} - {/*
- {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side === 0 ? 1 : 0, this._props.PanelWidth() - 3)} -
*/} {this.overlayAlternateIcon} diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 844c1e36d..7cb6a20f4 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -15,6 +15,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu'; import { LinkPopup } from '../linking/LinkPopup'; import './AnchorMenu.scss'; import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup'; +import { PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs'; @observer export class AnchorMenu extends AntimodeMenu { @@ -57,6 +58,7 @@ export class AnchorMenu extends AntimodeMenu { public get Active() { return this._left > 0; } + public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined; componentWillUnmount() { this._disposer?.(); @@ -96,14 +98,34 @@ export class AnchorMenu extends AntimodeMenu { try { const res = await gptAPICall(this.selectedText, GPTCallType.FLASHCARD); + GPTPopup.Instance.setText(res || 'Something went wrong.'); - GPTPopup.Instance.transferToFlashcard(); + this.transferToFlashcard(res); } catch (err) { console.error(err); } GPTPopup.Instance.setLoading(false); }; + transferToFlashcard = (text: string) => { + const senArr = text.split('Question'); + const collectionArr: Doc[] = []; + for (var i = 1; i < senArr.length; i++) { + console.log('Arr ' + i + ': ' + senArr[i]); + const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 }); + newDoc.text = senArr[i]; + collectionArr.push(newDoc); + } + const newCol = Docs.Create.CarouselDocument(collectionArr, { + _width: 200, + _height: 200, + _layout_fitWidth: true, + _layout_autoHeight: true, + }); + + this.addToCollection?.(newCol); + }; + pointerDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index dbcdd4e3a..32c1721ec 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -144,6 +144,15 @@ export class GPTPopup extends ObservableReactComponent { _layout_autoHeight: true, }); this.addDoc(newDoc, this.sidebarId); + // const arr = [newDoc]; + // const newCol = Docs.Create.CarouselDocument(arr, { + // _width: 200, + // _height: 200, + // _layout_fitWidth: true, + // _layout_autoHeight: true, + // }); + // this.addDoc(newDoc, this.sidebarId); + // this.addDoc(newCol, this.sidebarId); const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false); if (anchor) { DocUtils.MakeLink(newDoc, anchor, { @@ -152,21 +161,24 @@ export class GPTPopup extends ObservableReactComponent { } }; - transferToFlashcard = () => { - const senArr = this.text.split('.'); - for (var i = 0; i < senArr.length; i += 2) { - console.log('SEN: ' + senArr[i]); - const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 }); - newDoc.text = senArr[i]; - this.addDoc(newDoc, this.sidebarId); - const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false); - if (anchor) { - DocUtils.MakeLink(newDoc, anchor, { - link_relationship: 'GPT Summary', - }); - } - } - }; + // transferToFlashcard = () => { + // const senArr = this.text.split('Question'); + // const collectionArr: Doc[] = []; + // for (var i = 1; i < senArr.length; i++) { + // console.log('Arr ' + i + ': ' + senArr[i]); + // const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 }); + // newDoc.text = senArr[i]; + // collectionArr.push(newDoc); + // } + // const newCol = Docs.Create.CarouselDocument(collectionArr, { + // _width: 200, + // _height: 200, + // _layout_fitWidth: true, + // _layout_autoHeight: true, + // }); + // this.addDoc(newCol, this.sidebarId); + // this.addToCollection?.(newCol); + // }; /** * Transfers the image urls to actual image docs diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 7d8529a1c..cecaf17ff 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -417,6 +417,7 @@ export class PDFViewer extends ObservableReactComponent { // Changing which document to add the annotation to (the currently selected PDF) GPTPopup.Instance.setSidebarId('data_sidebar'); GPTPopup.Instance.addDoc = this._props.sidebarAddDoc; + AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument; // const newDoc = Docs.Create.ComparisonDocument({ _layout_isFlashcard: true, _width: 300, _height: 300 }); // this.props.addDocument?.(newDoc); }; -- cgit v1.2.3-70-g09d2