aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx79
-rw-r--r--src/client/views/collections/CollectionView.tsx15
-rw-r--r--src/client/views/nodes/ComparisonBox.scss143
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx109
-rw-r--r--src/client/views/nodes/DocumentView.tsx24
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx5
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx27
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx45
-rw-r--r--src/client/views/pdf/PDFViewer.tsx4
9 files changed, 340 insertions, 111 deletions
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 7f5176123..d45b0822b 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -33,29 +33,53 @@ export class CollectionCarouselView extends CollectionSubView() {
}
};
+ /**
+ * Goes to the next flashcard in the stack and filters
+ * based on the the currently selected option.
+ */
advance = (e: React.MouseEvent) => {
e.stopPropagation();
this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + 1) % this.childLayoutPairs.length;
var startInd = this.layoutDoc._carousel_index;
// if the star filter is selected
- if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'star') {
- // go to a new index that is starred, skip the ones that aren't
+ if (this.layoutDoc[`filterOp`] == 'star') {
+ // go to a flashcard that is starred, skip the ones that aren't
while (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`] && (startInd + 1) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) {
startInd = (startInd + 1) % this.childLayoutPairs.length;
}
this.layoutDoc._carousel_index = startInd;
+ // if there aren't any starred, show all cards
+ if (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`]) {
+ this.layoutDoc[`filterOp`] = 'all';
+ }
}
// if the practice filter is selected
- if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice') {
+ if (this.layoutDoc[`filterOp`] == 'practice') {
// go to a new index that is missed, skip the ones that are correct
while (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct' && (startInd + 1) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) {
startInd = (startInd + 1) % this.childLayoutPairs.length;
}
this.layoutDoc._carousel_index = startInd;
+
+ // if the user has gone through all of the cards and gotten them all correct, show all cards and exit practice mode
+ if (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct') {
+ this.layoutDoc[`filterOp`] = 'all';
+
+ // set all the cards to missed
+ for (var i = 0; i < this.childLayoutPairs.length; i++) {
+ const curDoc = this.childLayoutPairs?.[NumCast(i)];
+ curDoc.layout[`${this.fieldKey}_missed`] = undefined;
+ }
+ }
}
};
+
+ /**
+ * Goes to the previous flashcard in the stack and filters
+ * based on the the currently selected option.
+ */
goback = (e: React.MouseEvent) => {
e.stopPropagation();
this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
@@ -63,38 +87,58 @@ export class CollectionCarouselView extends CollectionSubView() {
var startInd = this.layoutDoc._carousel_index;
// if the star filter is selected
- if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'star') {
+ if (this.layoutDoc[`filterOp`] == 'star') {
// go to a new index that is starred, skip the ones that aren't
while (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`] && (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) {
startInd = (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
}
this.layoutDoc._carousel_index = startInd;
+ // if there aren't any starred, show all cards
+ if (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`]) {
+ this.layoutDoc[`filterOp`] = 'all';
+ }
}
// if the practice filter is selected
- if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice') {
+ if (this.layoutDoc[`filterOp`] == 'practice') {
// go to a new index that is missed, skip the ones that are correct
while (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct' && (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) {
startInd = (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
}
+
this.layoutDoc._carousel_index = startInd;
+
+ // See all flashcards when finish going through practice mode and set all of the flashcards back to
+ if (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct') {
+ this.layoutDoc[`filterOp`] = 'all';
+
+ for (var i = 0; i < this.childLayoutPairs.length; i++) {
+ const curDoc = this.childLayoutPairs?.[NumCast(i)];
+ curDoc.layout[`${this.fieldKey}_missed`] = undefined;
+ }
+ }
}
};
+ /*
+ * Stars the document when the star button is pressed.
+ */
star = (e: React.MouseEvent) => {
e.stopPropagation();
- // stars the document when the button is pressed
const curDoc = this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)];
+ if (!curDoc) return;
if (curDoc.layout[`${this.fieldKey}_star`] == undefined) curDoc.layout[`${this.fieldKey}_star`] = true;
else curDoc.layout[`${this.fieldKey}_star`] = !curDoc.layout[`${this.fieldKey}_star`];
};
+ /*
+ * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode.
+ */
missed = (e: React.MouseEvent, val: string) => {
e.stopPropagation();
const curDoc = this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)];
curDoc.layout[`${this.fieldKey}_missed`] = val;
- this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + 1) % this.childLayoutPairs.length;
- this.advance;
+ this.advance(e);
};
captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string): any => {
@@ -154,6 +198,7 @@ export class CollectionCarouselView extends CollectionSubView() {
);
}
@computed get buttons() {
+ if (!this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)]) return;
return (
<>
<div key="back" className="carouselView-back" onClick={this.goback}>
@@ -165,10 +210,10 @@ export class CollectionCarouselView extends CollectionSubView() {
<div key="star" className="carouselView-star" onClick={this.star}>
<FontAwesomeIcon icon={'star'} color={this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)].layout[`${this.fieldKey}_star`] ? 'yellow' : 'gray'} size={'1x'} />
</div>
- <div key="remove" className="carouselView-remove" onClick={e => this.missed(e, 'missed')} style={{ visibility: this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice' ? 'visible' : 'hidden' }}>
+ <div key="remove" className="carouselView-remove" onClick={e => this.missed(e, 'missed')} style={{ visibility: this.layoutDoc[`filterOp`] == 'practice' ? 'visible' : 'hidden' }}>
<FontAwesomeIcon icon={'xmark'} color={'red'} size={'1x'} />
</div>
- <div key="check" className="carouselView-check" onClick={e => this.missed(e, 'correct')} style={{ visibility: this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice' ? 'visible' : 'hidden' }}>
+ <div key="check" className="carouselView-check" onClick={e => this.missed(e, 'correct')} style={{ visibility: this.layoutDoc[`filterOp`] == 'practice' ? 'visible' : 'hidden' }}>
<FontAwesomeIcon icon={'check'} color={'green'} size={'1x'} />
</div>
</>
@@ -185,6 +230,18 @@ export class CollectionCarouselView extends CollectionSubView() {
color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color),
}}>
{this.content}
+ {/* Displays a message to the user to add more flashcards if they are in practice mode and no flashcards are there. */}
+ <p
+ style={{
+ display: !this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)] && this.layoutDoc[`filterOp`] == 'practice' ? 'flex' : 'none',
+ justifyContent: 'center',
+ alignItems: 'center',
+ height: '100%',
+ zIndex: '-1',
+ }}>
+ Add flashcards!
+ </p>
+ {/* Displays a message to the user that a flashcard was recently missed if they had previously gotten it wrong. */}
<p
style={{
color: 'red',
@@ -192,7 +249,7 @@ export class CollectionCarouselView extends CollectionSubView() {
position: 'relative',
left: '10px',
top: '10px',
- visibility: this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)].layout[`${this.fieldKey}_missed`] == 'missed' ? 'visible' : 'hidden',
+ display: this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)] ? (this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)].layout[`${this.fieldKey}_missed`] == 'missed' ? 'block' : 'none') : 'none',
}}>
Recently missed!
</p>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 168176edf..2f0f2a773 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -80,6 +80,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
super(props);
makeObservable(this);
this._annotationKeySuffix = returnEmptyString;
+ this.layoutDoc[`filterOp`] = 'all';
}
componentDidMount() {
@@ -176,14 +177,18 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
return newRendition;
});
+ // creates menu option for flashcard filters
const revealOptions = cm.findByDescription('Filter Flashcards');
const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : [];
- revealItems.push({ description: 'All', event: () => (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'all'), icon: 'eye-slash' });
- revealItems.push({ description: 'Star', event: () => (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'star'), icon: 'hand-point-up' });
- revealItems.push({ description: 'Practice Mode', event: () => (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'practice'), icon: 'rotate' });
+ revealItems.push({ description: 'All', event: () => (this.layoutDoc[`filterOp`] = 'all'), icon: 'layer-group' });
+ revealItems.push({ description: 'Star', event: () => (this.layoutDoc[`filterOp`] = 'star'), icon: 'star' });
+ revealItems.push({ description: 'Practice Mode', event: () => (this.layoutDoc[`filterOp`] = 'practice'), icon: 'check' });
+ revealItems.push({ description: 'Quiz Cards', event: () => (this.layoutDoc[`filterOp`] = 'quiz'), icon: 'pencil' });
- //revealItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront?.(dv.Document, false)), icon: 'arrow-up' });
- !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
+ // only show the filter options if it is a collection of type Carousel view
+ if (this.Document?._type_collection === CollectionViewType.Carousel) {
+ !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
+ }
const options = cm.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index 39c864b2b..093b9c004 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -1,4 +1,5 @@
.comparisonBox-interactive,
+.quiz-card,
.comparisonBox {
border-radius: inherit;
width: 100%;
@@ -7,6 +8,40 @@
z-index: 0;
pointer-events: none;
display: flex;
+ p {
+ color: rgb(0, 0, 0);
+ -webkit-text-stroke-color: black;
+ -webkit-text-stroke-width: 0.2px;
+ }
+
+ .input-box {
+ position: relative;
+ padding: 10px;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ }
+
+ .submit-button {
+ position: relative;
+ padding-bottom: 10px;
+ padding-left: 5px;
+ padding-right: 5px;
+ width: 100%;
+ height: 15%;
+ display: flex;
+
+ button {
+ flex: 1;
+ position: relative;
+ }
+ }
+ textarea {
+ flex: 1;
+ padding: 10px;
+ position: relative;
+ resize: none;
+ }
.clip-div {
position: absolute;
@@ -95,4 +130,112 @@
display: flex;
}
}
+ // .input-box {
+ // position: relative;
+ // padding: 10px;
+ // }
+ // input[type='text'] {
+ // flex: 1;
+ // position: relative;
+ // margin-right: 10px;
+ // width: 100px;
+ // }
+}
+
+// .quiz-card {
+// position: relative;
+
+// input[type='text'] {
+// flex: 1;
+// position: relative;
+// margin-right: 10px;
+// width: 100px;
+// }
+// }
+.QuizCard {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+
+ .QuizCard-wrapper {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ .QuizCardBox {
+ /* existing code */
+
+ .DIYNodeBox-iframe {
+ height: 100%;
+ width: 100%;
+ border: none;
+ }
+ }
+
+ .search-bar {
+ display: flex;
+ justify-content: left;
+ align-items: left;
+ width: 100%;
+ padding: 10px;
+
+ input[type='text'] {
+ flex: 1;
+ margin-right: 10px;
+ }
+
+ button {
+ padding: 5px 10px;
+ }
+ }
+
+ .content {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ .diagramBox {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ svg {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ }
+ }
+ }
+
+ .loading-circle {
+ position: relative;
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ border: 3px solid #ccc;
+ border-top-color: #333;
+ animation: spin 1s infinite linear;
+ }
+
+ @keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+ }
+ }
}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 19fccce8a..9fd4d696a 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
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 { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents, unimplementedFunction } from '../../../Utils';
import { Doc, Opt, DocListCast } from '../../../fields/Doc';
import { DocCast, NumCast, RTFCast, StrCast } from '../../../fields/Types';
import { DocUtils, Docs } from '../../documents/Documents';
@@ -34,6 +34,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
makeObservable(this);
}
+ @observable inputValue = '';
+ @observable outputValue = '';
+ @observable loading = false;
+ @observable errorMessage = '';
+ @observable outputMessage = '';
+
+ @action handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+ this.inputValue = e.target.value;
+ console.log(this.inputValue);
+ };
+
@observable _animating = '';
@computed get clipWidth() {
@@ -160,7 +171,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
e => {
const de = new DragManager.DocumentDragData([DocCast(this.dataDoc[which])], dropActionType.move);
de.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
- //this.clearDoc(which);
return addDocument(doc);
};
de.canEmbed = true;
@@ -181,15 +191,24 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true);
_closeRef = React.createRef<HTMLDivElement>();
+ /**
+ * Flips a flashcard to the alternate side for the user to view.
+ */
flipFlashcard = () => {
const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
-
this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : undefined;
};
+
+ /**
+ * Changes the view option to hover for a flashcard.
+ */
hoverFlip = (side: string | undefined) => {
if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hover') this.layoutDoc[`_${this._props.fieldKey}_usePath`] = side;
};
+ /**
+ * Creates the button used to flip the flashcards.
+ */
@computed get overlayAlternateIcon() {
const usepath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
return (
@@ -203,21 +222,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
this.flipFlashcard();
console.log('Print Front of cards: ' + RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text);
console.log('Print Back of cards: ' + RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text);
-
- //const queryText = RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text;
- // DocCast(this.dataDoc[this.fieldKey + '_1'])[DocData].text = 'hello';
- // const mes = gptAPICall(queryText, GPTCallType.COMPLETION).trim();
- // const res = await gptAPICall(queryText, GPTCallType.COMPLETION)
- // console.log(res);
- //.then(value => (DocCast(this.dataDoc[this.fieldKey + '_1']).text = value.trim()));
- if (usepath !== 'alternate') {
- this.askGPT();
- }
}
})
}
style={{
- //display: this._props.isContentActive() && !SnappingManager.IsDragging ? 'flex' : 'none',
background: usepath === 'alternate' ? 'white' : 'black',
color: usepath === 'alternate' ? 'black' : 'white',
}}>
@@ -227,15 +235,34 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
);
}
+ @action handleRenderGPTClick = () => {
+ // Call the GPT model and get the output
+ this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate';
+ this.outputValue = '';
+ if (this.inputValue) this.askGPT();
+ };
+
+ @action handleRenderClick = () => {
+ // Call the GPT model and get the output
+ this.layoutDoc[`_${this._props.fieldKey}_usePath`] = undefined;
+ };
+
+ /**
+ * Calls the GPT model to create QuizCards. Evaluates how similar the user's response is to the alternate
+ * side of the flashcard.
+ */
askGPT = async (): Promise<string | undefined> => {
- const queryText = RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text;
+ const questionText = 'Question: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text);
+ const rubricText = ' Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text);
+ const queryText = questionText + ' UserAnswer: ' + this.inputValue + '. ' + rubricText;
+
try {
- let res = await gptAPICall(StrCast(queryText), GPTCallType.COMPLETION);
+ let res = await gptAPICall(queryText, GPTCallType.QUIZ);
if (!res) {
console.error('GPT call failed');
return;
}
- DocCast(this.dataDoc[this.fieldKey + '_0'])[DocData].text = res;
+ this.outputValue = res;
console.log(res);
} catch (err) {
console.error('GPT call failed');
@@ -292,8 +319,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
);
};
const displayBox = (which: string, index: number, cover: number) => {
- // if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hide/reveal') this.layoutDoc[this.clipHeightKey] = 100;
- // else this.layoutDoc.height = 300;
return (
<div className={`${index === 0 ? 'before' : 'after'}Box-cont`} key={which} style={{ width: this._props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
{displayDoc(which)}
@@ -326,36 +351,54 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
if (this.Document._layout_isFlashcard) {
const side = this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 1 : 0;
- // add text box when first created
+ // add text box to each side when comparison box is first created
if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] == 'empty')) {
const dataSplit = StrCast(this.dataDoc.data).split('Answer');
const newDoc = Docs.Create.TextDocument(dataSplit[1]);
+ // if there is text from the pdf ai cards, put the question on the front side.
newDoc[DocData].text = dataSplit[1];
this.addDoc(newDoc, this.fieldKey + '_0');
}
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[DocData].text = 'placeholder...';
+ // if there is text from the pdf ai cards, put the answer on the alternate side.
+ newDoc[DocData].text = dataSplit[0];
this.addDoc(newDoc, this.fieldKey + '_1');
}
- if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hide/reveal') {
- {
- return (
- <div className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} style={{ display: 'flex', flexDirection: 'column' }}>
- {displayBox(`${this.fieldKey}_0`, side, this._props.PanelHeight() - 3)}
- {displayBox(`${this.fieldKey}_1`, 1, this._props.PanelHeight() - 3)}
+ // render the QuizCards
+ if (DocCast(this.Document.embedContainer) && DocCast(this.Document.embedContainer)[`filterOp`] == 'quiz') {
+ return (
+ <div className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} style={{ display: 'flex', flexDirection: 'column' }}>
+ <p style={{ color: 'white', padding: 10 }}>{StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text)}</p>
+ {/* {StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text)} */}
+ <div className={'input-box'}>
+ {
+ <textarea
+ value={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? this.outputValue : this.inputValue}
+ onChange={this.handleInputChange}
+ readOnly={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate'}
+ />
+ }
</div>
- );
- }
- } else {
+ <div className="submit-button" style={{ display: this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 'none' : 'flex' }}>
+ <button onClick={this.handleRenderGPTClick}>Submit</button>
+ </div>
+ <div className="submit-button" style={{ display: this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 'flex' : 'none' }}>
+ <button onClick={this.handleRenderClick}>Edit Your Response</button>
+ </div>
+ </div>
+ );
+ }
+
+ // render a normal flashcard when not a QuizCard
+ else {
return (
<div
className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} /* change className to easily disable/enable pointer events in CSS */
- // style={{ display: 'flex', flexDirection: 'column' }}
- // style={{ position: 'absolute', top: '0px' }}
+ style={{ display: 'flex', flexDirection: 'column' }}
onMouseEnter={() => {
this.hoverFlip('alternate');
}}
@@ -363,12 +406,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
this.hoverFlip(undefined);
}}>
{displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)}
-
{this.overlayAlternateIcon}
</div>
);
}
} else {
+ // render a comparison box that compares items side by side
return (
<div className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
{displayBox(`${this.fieldKey}_2`, 1, this._props.PanelWidth() - 3)}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 26e5ec622..e1b501c5a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -48,6 +48,9 @@ import { LinkAnchorBox } from './LinkAnchorBox';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { PresEffect, PresEffectDirection } from './trails';
import { FieldsDropdown } from '../FieldsDropdown';
+import { RTFCast } from '../../../fields/Types';
+import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT';
+
interface Window {
MediaRecorder: MediaRecorder;
}
@@ -517,6 +520,21 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
input.click();
};
+ askGPT = async (): Promise<string | undefined> => {
+ const queryText = RTFCast(DocCast(this.dataDoc[this.props.fieldKey + '_1']).text)?.Text;
+ try {
+ let res = await gptAPICall('Question: ' + StrCast(queryText), GPTCallType.CHATCARD);
+ if (!res) {
+ console.error('GPT call failed');
+ return;
+ }
+ DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
+ console.log(res);
+ } catch (err) {
+ console.error('GPT call failed');
+ }
+ };
+
onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
if (e && this.layoutDoc.layout_hideContextMenu && Doc.noviceMode) {
e.preventDefault();
@@ -569,17 +587,19 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => LightboxView.Instance.SetLightboxDoc(this.Document), icon: 'external-link-alt' });
}
appearanceItems.push({ description: 'Pin', event: () => this._props.pinToPres(this.Document, {}), icon: 'eye' });
+ if (this.Document._layout_isFlashcard) {
+ appearanceItems.push({ description: 'Create ChatCard', event: () => this.askGPT(), icon: 'id-card' });
+ }
!Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this._props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
!appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'compass' });
+ // creates menu for the user to select how to reveal the flashcards
if (this.Document._layout_isFlashcard) {
const revealOptions = cm.findByDescription('Reveal Options');
const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : [];
revealItems.push({ description: 'Hover', event: () => (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover'), icon: 'hand-point-up' });
revealItems.push({ description: 'Flip', event: () => (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip'), icon: 'rotate' });
- revealItems.push({ description: 'Hide & Reveal', event: () => (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hide/reveal'), icon: 'eye-slash' });
- //revealItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront?.(dv.Document, false)), icon: 'arrow-up' });
!revealOptions && cm.addItem({ description: 'Reveal Options', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 4fce72cdc..3192ac537 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -178,6 +178,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@observable
private gptRes: string = '';
+ public makeAIFlashcards: () => void = unimplementedFunction;
+ public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
+
public static PasteOnLoad: ClipboardEvent | undefined;
private static SelectOnLoad: Doc | undefined;
public static SetSelectOnLoad(doc: Doc) {
@@ -959,7 +962,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const options = cm.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' });
- optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' });
+ optionItems.push({ description: `Ask GPT-3`, event: () => this.makeAIFlashcards(), icon: 'lightbulb' });
this._props.renderDepth &&
optionItems.push({
description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 7cb6a20f4..a0c3cf487 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -76,7 +76,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
* @param e pointer down event
*/
gptSummarize = async (e: React.PointerEvent) => {
- // move this logic to gptpopup, need to implement generate again
GPTPopup.Instance.setVisible(true);
GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
GPTPopup.Instance.setLoading(true);
@@ -90,24 +89,28 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
GPTPopup.Instance.setLoading(false);
};
+ /**
+ * Invokes the API with the selected text and stores it in the selected text.
+ * @param e pointer down event
+ */
gptFlashcards = async (e: React.PointerEvent) => {
- // move this logic to gptpopup, need to implement generate again
- // GPTPopup.Instance.setVisible(true);
- // GPTPopup.Instance.setMode(GPTPopupMode.FLASHCARD);
- // GPTPopup.Instance.setLoading(true);
-
+ const queryText = this.selectedText;
try {
- const res = await gptAPICall(this.selectedText, GPTCallType.FLASHCARD);
-
+ const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
+ console.log(res);
GPTPopup.Instance.setText(res || 'Something went wrong.');
- this.transferToFlashcard(res);
+ this.transferToFlashcard(res || 'Something went wrong');
} catch (err) {
console.error(err);
}
GPTPopup.Instance.setLoading(false);
};
+ /*
+ * Transfers the flashcard text generated by GPT on flashcards and creates a collection out them.
+ */
transferToFlashcard = (text: string) => {
+ // put each question generated by GPT on the front of the flashcard
const senArr = text.split('Question');
const collectionArr: Doc[] = [];
for (var i = 1; i < senArr.length; i++) {
@@ -116,10 +119,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
newDoc.text = senArr[i];
collectionArr.push(newDoc);
}
+ // create a new carousel collection of these flashcards
const newCol = Docs.Create.CarouselDocument(collectionArr, {
- _width: 200,
+ _width: 250,
_height: 200,
- _layout_fitWidth: true,
+ _layout_fitWidth: false,
_layout_autoHeight: true,
});
@@ -215,6 +219,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
)}
+ {/* Adds a create flashcards option to the anchor menu, which calls the gptFlashcard method. */}
<IconButton
tooltip="Create flashcards" //
onPointerDown={this.gptFlashcards}
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 32c1721ec..0b741c85e 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -144,15 +144,6 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
_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, {
@@ -161,25 +152,6 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
}
};
- // 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
*/
@@ -244,23 +216,6 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
);
};
- flashcardBox = () => {
- // const textArr = this.text.split(".");
- // textArr.forEach(function(sentence) {
- // console.log(sentence);
-
- // });
- // const newDoc = Docs.Create.ComparisonDocument();
- // this.addToCollection?.(newDoc);
- // // const newDoc = Docs.Create.ComparisonDocument();
- // DocUtils.copyDragFactory(Doc.UserDoc().emptyFlashcard as Doc);
- // // this.addToCollection?.(newDoc);
- // // return newDoc;
- // <ComparisonBox/>
- const newDoc = Docs.Create.TextDocument('Hello there');
- this.addDoc?.(newDoc);
- };
-
data = () => {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index cecaf17ff..fe1ed8159 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -414,12 +414,10 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
}
- // Changing which document to add the annotation to (the currently selected PDF)
GPTPopup.Instance.setSidebarId('data_sidebar');
GPTPopup.Instance.addDoc = this._props.sidebarAddDoc;
+ // allows for creating collection
AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument;
- // const newDoc = Docs.Create.ComparisonDocument({ _layout_isFlashcard: true, _width: 300, _height: 300 });
- // this.props.addDocument?.(newDoc);
};
@action