diff options
author | alyssaf16 <alyssa_feinberg@brown.edu> | 2024-06-06 09:53:12 -0400 |
---|---|---|
committer | alyssaf16 <alyssa_feinberg@brown.edu> | 2024-06-06 09:53:12 -0400 |
commit | c76505f56a83625e3993427838aaee0c54c5fb81 (patch) | |
tree | 99f498b643b9862e5d1ec34b75ffdaeeb0a52877 | |
parent | 585f03bf45df4ac7ed61d22c9dbe10d8e453199d (diff) |
Fixes to chatcard and quizcard
-rw-r--r-- | src/client/apis/gpt/GPT.ts | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCarouselView.tsx | 7 | ||||
-rw-r--r-- | src/client/views/nodes/ComparisonBox.tsx | 43 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 19 |
4 files changed, 40 insertions, 31 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 05007960d..b036349dc 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -51,7 +51,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { model: 'gpt-4-turbo', maxTokens: 1024, temp: 0, - prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If there are no differences, say correct', + prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If the Rubric is incorrect, explain why. If there are no differences, say correct. If it is empty, say there is nothing for me to evaluate.', }, }; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 2893de762..53d14e6e0 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -35,6 +35,7 @@ export class CollectionCarouselView extends CollectionSubView() { private _dropDisposer?: DragManager.DragDropDisposer; @observable private _message = 'Drag a document'; get practiceField() { return this.fieldKey + "_practice"; } // prettier-ignore + get sideField() { return "_" + this.fieldKey + "_usePath"; } // prettier-ignore get starField() { return this.fieldKey + "_star"; } // prettier-ignore constructor(props: any) { @@ -131,9 +132,6 @@ export class CollectionCarouselView extends CollectionSubView() { curDoc.layout[this.practiceField] = val; this.advance(e); }; - clearContent = () => { - this.carouselItems?.map(doc => (doc.layout[this.practiceField] = undefined)); - }; captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string): any => { // first look for properties on the document in the carousel, then fallback to properties on the container const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined; @@ -146,7 +144,8 @@ export class CollectionCarouselView extends CollectionSubView() { setFilterMode = (mode: cardMode) => { this.layoutDoc.filterOp = mode; if (mode == cardMode.STAR) this.move(1); - this.clearContent(); + if (mode == cardMode.QUIZ) this.carouselItems?.map(doc => (doc.layout[this.sideField] = undefined)); + this.carouselItems?.map(doc => (doc.layout[this.practiceField] = undefined)); }; specificMenu = (): void => { const cm = ContextMenu.Instance; diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index c0c173d9a..6d4af2f3e 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -25,6 +25,8 @@ import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import ReactLoading from 'react-loading'; +import { ContextMenu } from '../ContextMenu'; +import { ContextMenuProps } from '../ContextMenuItem'; @observer export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @@ -282,7 +284,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() // Call the GPT model and get the output this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate'; this._outputValue = ''; - if (this._inputValue) this.askGPT(); + if (this._inputValue) this.askGPT(GPTCallType.QUIZ); }; @action handleRenderClick = () => { @@ -290,12 +292,22 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() this.layoutDoc[`_${this._props.fieldKey}_usePath`] = undefined; }; - animateRes = (resIndex: number, newText: string) => { + animateRes = (resIndex: number, newText: string, callType: GPTCallType) => { if (resIndex < newText.length) { // const marks = this._editorView?.state.storedMarks ?? []; - this._outputValue += newText[resIndex]; + switch (callType) { + case GPTCallType.CHATCARD: + DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text += newText[resIndex]; + break; + case GPTCallType.QUIZ: + this._outputValue += newText[resIndex]; + break; + default: + return; + } + // this._editorView?.dispatch(this._editorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(this._outputValue)); - setTimeout(() => this.animateRes(resIndex + 1, newText), 20); + setTimeout(() => this.animateRes(resIndex + 1, newText, callType), 20); } }; @@ -303,18 +315,22 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() * 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> => { + askGPT = async (callType: GPTCallType): Promise<string | undefined> => { 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; this._loading = true; + if (callType == GPTCallType.CHATCARD) { + DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = ''; + this.flipFlashcard(); + } try { - const res = await gptAPICall(queryText, GPTCallType.QUIZ); + const res = await gptAPICall(callType == GPTCallType.QUIZ ? queryText : questionText, callType); if (!res) { console.error('GPT call failed'); return; } - this.animateRes(0, '\n\n' + res); + this.animateRes(0, res, callType); // this._outputValue = res; console.log(res); } catch (err) { @@ -325,6 +341,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() layoutWidth = () => NumCast(this.layoutDoc.width, 200); layoutHeight = () => NumCast(this.layoutDoc.height, 200); + specificMenu = (): void => { + const cm = ContextMenu.Instance; + cm.addItem({ description: 'Create an Answer on the Back', event: () => this.askGPT(GPTCallType.CHATCARD), icon: 'pencil' }); + }; + render() { const clearButton = (which: string) => ( <Tooltip title={<div className="dash-tooltip">remove</div>}> @@ -425,7 +446,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() </div> <div className="submit-button" style={{ overflow: 'hidden', marginBottom: '2px', display: this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 'flex' : 'none' }}> <button type="button" onClick={this.handleRenderClick} style={{ borderRadius: '2px' }}> - Try Again + Redo the Question </button> </div> </div> @@ -436,6 +457,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() return ( <div className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} /* change className to easily disable/enable pointer events in CSS */ + onContextMenu={this.specificMenu} style={{ display: 'flex', flexDirection: 'column' }} onMouseEnter={() => { this.hoverFlip('alternate'); @@ -446,6 +468,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() // onPointerUp={() => (this._isAnyChildContentActive = true)} > {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)} + {this._loading ? ( + <div className="loading-spinner" style={{ position: 'absolute' }}> + <ReactLoading type="spin" height={30} width={30} color={'blue'} /> + </div> + ) : null} {this.overlayAlternateIcon} </div> ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8bf7b094f..a25249eac 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -111,6 +111,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document private _downTime: number = 0; private _lastTap: number = 0; private _doubleTap = false; + private _loading = false; private _mainCont = React.createRef<HTMLDivElement>(); private _titleRef = React.createRef<EditableView>(); private _dropDisposer?: DragManager.DragDropDisposer; @@ -501,21 +502,6 @@ 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 { - const 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(); @@ -574,9 +560,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => DocumentView.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 an answer on the back', 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' }); |