diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/FilterPanel.scss | 18 | ||||
| -rw-r--r-- | src/client/views/FilterPanel.tsx | 3 | ||||
| -rw-r--r-- | src/client/views/InkingStroke.tsx | 14 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionCardDeckView.tsx | 221 | ||||
| -rw-r--r-- | src/client/views/global/globalScripts.ts | 26 | ||||
| -rw-r--r-- | src/client/views/nodes/IconTagBox.tsx | 23 | ||||
| -rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.scss | 56 | ||||
| -rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 319 |
8 files changed, 482 insertions, 198 deletions
diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss index 0b9076f3b..34c3d2fc1 100644 --- a/src/client/views/FilterPanel.scss +++ b/src/client/views/FilterPanel.scss @@ -283,6 +283,8 @@ .hotkey-title{ top: 6px; position: relative; + cursor: text; + } .hotkey-title-input{ @@ -290,6 +292,8 @@ border: none; border-color: transparent; outline: none; + cursor: text; + } } @@ -345,6 +349,20 @@ } + +.drawing-box{ + position: absolute; + z-index: 10000; + border-color: black; + border-style: solid; + border-width: medium; + border-radius: 10%; + background-color: #323232; + + + + +} // .sliderBox-outerDiv { // width: 30%;// width: calc(100% - 14px); // 14px accounts for handles that are at the max value of the slider that would extend outside the box // height: 40; // height: 100%; diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 17c5cb173..ca4e4df84 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -51,10 +51,13 @@ interface filterProps { @observer export class FilterPanel extends ObservableReactComponent<filterProps> { @observable _selectedFacetHeaders = new Set<string>(); + public static Instance: FilterPanel; + constructor(props: any) { super(props); makeObservable(this); + FilterPanel.Instance = this; } /** diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 62b79dd5a..a117ff3c3 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -447,13 +447,13 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() }), icon: 'paint-brush', }); - cm?.addItem({ - description: 'Create a hotkey', - event: action(() => { - InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton; - }), - icon: 'satellite', - }); + // cm?.addItem({ + // description: 'Create a hotkey', + // event: action(() => { + // InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton; + // }), + // icon: 'satellite', + // }); }, }; return ( diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 52f01e239..5dafb1088 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -28,6 +28,8 @@ import { SettingsManager } from '../../util/SettingsManager'; import { Tooltip } from '@mui/material'; import { dropActionType } from '../../util/DropActionTypes'; import { List } from '../../../fields/List'; +import { DocCast } from '../../../fields/Types'; +import { SelectionManager } from '../../util/SelectionManager'; enum cardSortings { Time = 'time', @@ -35,6 +37,7 @@ enum cardSortings { Color = 'color', Custom = 'custom', Chat = 'chat', + Tag = 'tag', None = '', } @observer @@ -91,7 +94,9 @@ export class CollectionCardView extends CollectionSubView() { childPairStringListAndUpdateSortDesc = async () => { const sortDesc = await this.childPairStringList(); // Await the promise to get the string result GPTPopup.Instance.setSortDesc(sortDesc.join()); - GPTPopup.Instance.onSortComplete = (sortResult: string) => this.processGptOutput(sortResult); + GPTPopup.Instance.onSortComplete = (sortResult: string, questionType: string, tag?: string) => this.processGptOutput(sortResult, questionType, tag); + GPTPopup.Instance.onQuizRandom = () => this.quizMode() + }; componentDidMount(): void { @@ -103,9 +108,17 @@ export class CollectionCardView extends CollectionSubView() { }); + // Reaction to cardSort changes this._disposers.sort = reaction( - () => ({ cardSort: this.cardSort }), - ({ cardSort}) => (cardSort === cardSortings.Chat ? this.openChatPopup() : GPTPopup.Instance.setVisible(false)) + () => this.cardSort, + (cardSort) => { + if (cardSort === cardSortings.Chat) { + this.openChatPopup(); + } else { + GPTPopup.Instance.setVisible(false); + } + // Force re-render to see if this resolves the sorting issue + } ); @@ -121,6 +134,7 @@ export class CollectionCardView extends CollectionSubView() { } @computed get cardSort() { + // console.log(StrCast(this.Document.cardSort) as any as cardSortings) return StrCast(this.Document.cardSort) as any as cardSortings; } @@ -151,6 +165,16 @@ export class CollectionCardView extends CollectionSubView() { return regularDocs; } + quizMode () { + + const randomIndex = Math.floor(Math.random() * this.childDocs.length) + console.log('hiiiiiii') + + SelectionManager.DeselectAll() + DocumentView.SelectView(DocumentView.getDocumentView(this.childDocs[randomIndex]), false) + + } + /** @@ -290,13 +314,48 @@ export class CollectionCardView extends CollectionSubView() { }; - @computed get sortedDocs() { + get sortedDocs() { // console.log("hi hi hi") // console.log(this.layoutDoc.cardSort_isDesc + "layoutdoc desc") - console.log(this.cardSort + "card sort") + // console.log(this.cardSort + "card sort") - return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.layoutDoc.cardSort_isDesc), this._docDraggedIndex); + return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.Document.cardSort_isDesc), this._docDraggedIndex); } + + tagValue(doc: Doc) { + const keys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); + + const isTagActive = (buttonID: number) => { + return BoolCast(doc[StrCast(Doc.UserDoc()[keys[buttonID]])]); + }; + + let base = ''; + let fraction = ''; + + for (let i = 0; i < keys.length; i++) { + if (isTagActive(i)) { + if (base === '') { + base = i.toString(); // First active tag becomes the base + } else { + fraction += i.toString(); // Subsequent active tags become part of the fraction + } + } + } + + // If no tag was active, return 0 by default + if (base === '') { + return 0; + } + + // Construct the final number by appending the fraction if it exists + const numberString = fraction ? `${base}.${fraction}` : base; + + // Convert the result to a number and return + return Number(numberString); + } + + + /** * Called in the sortedDocsType method. Compares the cards' value in regards to the desired sort type-- earlier cards are move to the * front, latter cards to the back @@ -305,7 +364,8 @@ export class CollectionCardView extends CollectionSubView() { * @param isDesc * @returns */ - sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => { + @action sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => { + // console.log('hiiii') // if (sortType === cardSortings.None) return docs; // if(sortType !== cardSortings.None){ @@ -319,12 +379,13 @@ export class CollectionCardView extends CollectionSubView() { case cardSortings.Color: return [ClientUtils.hexToHsv(StrCast(docA.backgroundColor)), ClientUtils.hexToHsv(StrCast(docB.backgroundColor))]; - case cardSortings.Custom: - return [CollectionCardView.getButtonGroup(this.cardSort_customField, docA) ?? 9999, - CollectionCardView.getButtonGroup(this.cardSort_customField, docB) ?? 9999]; + case cardSortings.Tag: + return [this.tagValue(docA) ?? 9999, + this.tagValue(docB) ?? 9999]; case cardSortings.Chat: - return [NumCast(docA.chat) ?? 9999, - NumCast(docB.chat) ?? 9999] + return [NumCast(docA.chatIndex) ?? 9999, + NumCast(docB.chatIndex) ?? 9999] + default: return [StrCast(docA.type), StrCast(docB.type)] @@ -487,12 +548,19 @@ export class CollectionCardView extends CollectionSubView() { * Converts the gpt output into a hashmap that can be used for sorting. lists are seperated by ==== while elements within the list are seperated by ~~~~~~ * @param gptOutput */ - @action processGptOutput = (gptOutput: string) => { + @action processGptOutput = (gptOutput: string, questionType: string, tag?: string) => { console.log("HIIII") console.log(StrCast(this.Document.cardSort) + "cardSort") // Split the string into individual list items const listItems = gptOutput.split('======').filter(item => item.trim() !== ''); + + + if (questionType == '2' || questionType == '4'){ + this.childDocs.forEach(d => { + d['chatFilter'] = false + }) + } // console.log(listItems + " LISTT"); // Debug: print the map contents @@ -509,10 +577,47 @@ export class CollectionCardView extends CollectionSubView() { // console.log("ITEM:", normalizedItem); if (doc) { - doc.chat = index; + switch (questionType){ + case '6': + doc.chatIndex = index + console.log(index) + break + case '1': + const allHotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles) + + let myTag = '' + + if (tag){ + for (let i=0; i< allHotKeys.length; i++){ + if (tag.includes(allHotKeys[i])){ + myTag = StrCast(Doc.UserDoc()[allHotKeys[i]]) + break + } + + else if (tag.includes(StrCast(Doc.UserDoc()[allHotKeys[i]]))){ + myTag = StrCast(Doc.UserDoc()[allHotKeys[i]]) + break + } + } + + if (myTag != ''){ + doc[myTag] = true + } + } + break + case '2': + case '4': + doc['chatFilter'] = true + Doc.setDocFilter(DocCast(this.Document.embedContainer), 'chatFilter', true, 'match'); + break + + + + } } else { console.warn(`No matching document found for item: ${normalizedItem}`); } + }); } @@ -528,100 +633,20 @@ export class CollectionCardView extends CollectionSubView() { const sortDesc = await this.childPairStringList(); // Await the promise to get the string result GPTPopup.Instance.setCardsDoneLoading(true); // Set dataDoneLoading to true after data is loaded GPTPopup.Instance.setSortDesc(sortDesc.join()); - GPTPopup.Instance.onSortComplete = (sortResult: string) => this.processGptOutput(sortResult); - }; - - /** - * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups - * @param doc - * @param cardSort - * @returns - */ - renderButtons = (doc: Doc, cardSort: cardSortings): JSX.Element | null => { - // if (cardSort !== cardSortings.Custom) return null; - - const amButtons = 4 - - // const amButtons = Math.max( - // 4, - // this.childDocs?.reduce((set, d) => { - // if (this.cardSort_customField) { - // set.add(NumCast(d[this.cardSort_customField])); - // } - // return set; - // }, new Set<number>()).size ?? 0 - // ); - - // const activeButtonIndex = CollectionCardView.getButtonGroup(this.cardSort_customField, doc); - - const totalWidth = amButtons * 72 + amButtons * 2 * 5 + 6; - - const iconMap: { [key: number]: any } = { - 0: 'star', - 1: 'heart', - 2: 'cloud', - 3: 'bolt' - }; - - return ( - <div className="card-button-container" style={{ width: `${totalWidth}px`, fontSize: '50px' }}> - {numberRange(amButtons).map(i => ( - <Tooltip key={i} title={<div className="dash-tooltip">Click to add/remove this card from the {iconMap[i]} group</div>}> - <button type="button" onClick={() => this.toggleButton(doc, iconMap[i] )}> - {this.getButtonIcon(doc, iconMap[i])} - </button> - </Tooltip> - ))} - </div> - ); - }; - - /** - * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt - * @param childPairIndex - * @param buttonID - * @param doc - */ - toggleButton = undoable((doc: Doc, icon: string) => { - - - - // this.cardSort_customField && (doc[this.cardSort_customField] = buttonID); - - // doc.cardSort_activeIcons = new List<string>() - - - // const list = StrListCast(doc.cardSort_activeIcons); - // doc.cardSort_activeIcons = new List<string>(list.includes(icon) ? list.filter(d => d !== icon) : [...list, icon]); - - BoolCast(doc[icon]) ? doc[icon] = false : doc[icon] = true - - - - // StrListCast(doc.cardSort_activeIcons).push(iconMap[buttonID]) - }, 'toggle card tag'); - - - getButtonIcon = (doc: Doc, icon: any): JSX.Element => { - - // const isActive = StrListCast(doc.cardSort_activeIcons).includes(icon) - const isActive = doc[icon] - - // console.log(StrListCast(doc.cardSort_activeIcons)) - const color = isActive ? '#4476f7' : '#323232'; - - return <FontAwesomeIcon icon={icon} size="lg" style={{ color }} />; + GPTPopup.Instance.onSortComplete = (sortResult: string, questionType: string, tag?: string) => this.processGptOutput(sortResult, questionType, tag); + GPTPopup.Instance.onQuizRandom = () => this.quizMode() }; /** * Actually renders all the cards */ renderCards = () => { + // console.log('christ') const anySelected = this.childDocs.some(doc => DocumentView.SelectedDocs().includes(doc)); const isEmpty = this.childDocsWithoutLinks.length === 0; const isDesc = BoolCast(this.Document.cardSort_isDesc) - console.log(this.childDocsWithoutLinks.length + "length") + // console.log(this.childDocsWithoutLinks.length + "length") if (isEmpty) { return ( diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index a48e3f9d9..1efcd84e4 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -146,7 +146,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' } // prettier-ignore - const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'pile' | 'toggle-chat', + const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'pile' | 'toggle-chat' | 'tag', { waitForRender?: boolean; checkResult: (doc: Doc) => any; @@ -193,6 +193,10 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "color", setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort === "color" ? doc.cardSort = '' : doc.cardSort = 'color', }], + ['tag', { + checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "tag", + setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort === "tag" ? doc.cardSort = '' : doc.cardSort = 'tag', + }], // ['heart', { // checkResult: (doc: Doc) => isAttrFiltered('heart'), // setDoc: (doc: Doc, dv: DocumentView) => { @@ -221,16 +225,16 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' // } // }], - ['chat', { - checkResult: (doc: Doc) => { + // ['chat', { + // checkResult: (doc: Doc) => { - if (StrCast(doc?.cardSort) === "chat"){ - return true - }} , - setDoc: (doc: Doc, dv: DocumentView) => { - doc.cardSort === "chat" ? doc.cardSort = '' : doc.cardSort = 'chat'; - }, - }], + // if (StrCast(doc?.cardSort) === "chat"){ + // return true + // }} , + // setDoc: (doc: Doc, dv: DocumentView) => { + // doc.cardSort === "chat" ? doc.cardSort = '' : doc.cardSort = 'chat'; + // }, + // }], ['up', { checkResult: (doc: Doc) => BoolCast(!doc?.cardSort_isDesc), setDoc: (doc: Doc, dv: DocumentView) => { @@ -248,6 +252,8 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' setDoc: (doc: Doc, dv: DocumentView) => { GPTPopup.Instance.setVisible(!GPTPopup.Instance.visible); GPTPopup.Instance.setMode(GPTPopupMode.SORT); + doc.cardSort === "chat" ? doc.cardSort = '' : doc.cardSort = 'chat'; + }, }], diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx index c72d48ef5..8aa6bff2b 100644 --- a/src/client/views/nodes/IconTagBox.tsx +++ b/src/client/views/nodes/IconTagBox.tsx @@ -26,6 +26,9 @@ import { setupMoveUpEvents } from "../../../ClientUtils"; import { returnFalse } from "../../../ClientUtils"; import { emptyFunction } from "../../../Utils"; import { CollectionViewType } from "../../documents/DocumentTypes"; +import { SnappingManager } from "../../util/SnappingManager"; +import { MainView } from "../MainView"; +import { PropertiesView } from "../PropertiesView"; export interface IconTagProps { @@ -129,7 +132,7 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> { renderButtons = (doc: Doc): JSX.Element | null => { // if (cardSort !== cardSortings.Custom) return null; - const amButtons = StrListCast(Doc.UserDoc().myFilterHotKeyTitles).length + const amButtons = (StrListCast(Doc.UserDoc().myFilterHotKeyTitles).length) + 1 const keys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles) @@ -146,7 +149,7 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> { // const activeButtonIndex = CollectionCardView.getButtonGroup(this.cardSort_customField, doc); - const totalWidth = amButtons * 35 + amButtons * 2 * 5 + 6; + const totalWidth = (amButtons -1) * 35 + (amButtons -1) * 2 * 5 + 6; const iconMap = (buttonID: number) => { return StrCast(Doc.UserDoc()[keys[buttonID]]) @@ -169,17 +172,31 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> { fontSize: '50px' }} > - {numberRange(amButtons).map(i => ( + {numberRange(amButtons-1).map(i => ( <Tooltip key={i} title={<div className="dash-tooltip">Click to add/remove this card from the {iconMap(i)} group</div>}> <button key = {i} type="button" onClick={() => this.toggleButton(doc, iconMap(i) )}> {this.getButtonIcon(doc, iconMap(i))} </button> </Tooltip> ))} + + {/* <Tooltip title={<div className="dash-tooltip">Click to customize these icons</div>}> + <button type="button" onClick={() => this.openHotKeyMenu() }> + <FontAwesomeIcon icon='gear' style={{ color: '#17438a', height: '30px', width: '30px'}} /> + </button> + </Tooltip> */} </div> ); }; + openHotKeyMenu = () => { + SnappingManager.PropertiesWidth < 5 && SnappingManager.SetPropertiesWidth(0); + SnappingManager.SetPropertiesWidth(MainView.Instance.propertiesWidth() < 15 ? Math.min(MainView.Instance._dashUIWidth - 50, 250) : 0); + + PropertiesView.Instance.CloseAll() + PropertiesView.Instance.openFilters = true + } + /** * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt * @param childPairIndex diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss index eaa3eaebf..1defd1a7f 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.scss +++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss @@ -78,15 +78,63 @@ $highlightedText: #82e0ff; align-items: center; flex-direction: column; - transform: translateY(30px); + .inputWrapper{ + display: flex; + justify-content: center; + align-items: center; + height: 60px; + position: absolute; + bottom: 0; + width: 100%; + background-color: white; + + } .searchBox-input{ - transform: translateY(-15px); - height: 50px; + height: 40px; border-radius: 10px; + position: absolute; + bottom: 10px; border-color: #5b97ff; + width: 90% + } + + .chat-wrapper { + display: flex; + flex-direction: column; + width: 100%; + max-height: calc(100vh - 80px); /* Height minus the input box and some padding */ + overflow-y: auto; + padding-bottom: 60px; /* Space for the input */ + } + + .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; + } + @@ -98,6 +146,8 @@ $highlightedText: #82e0ff; + + } // button { diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index d216cc421..1b44508d7 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -34,6 +34,13 @@ export enum GPTPopupMode { QUIZ } +export enum GPTQuizType { + CURRENT = 0, + CHOOSE = 1, + MULTIPLE = 2 + +} + @@ -151,7 +158,8 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { this.sortDesc = t; }; - @observable onSortComplete?: (sortResult: string) => void; + @observable onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void; + @observable onQuizRandom?: () => void; @observable cardsDoneLoading = false; @action setCardsDoneLoading(done: boolean) { @@ -165,6 +173,9 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { this.sortRespText = resp } + //mode where gpt says to select a specific card + // + @observable chatSortPrompt: string = "" @@ -179,39 +190,65 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { this.quizAnswer = e.target.value; }); + + @observable correctAnswer: string = '' + @observable setCorrectAnswer = (s: string) => { + this.correctAnswer = s + } + generateQuiz = async () => { this.setLoading(true); this.setSortDone(false); + const quizType = this.quizMode; const selected = DocumentView.SelectedDocs().lastElement(); - const questionText = 'Question: ' + StrCast(selected['gptInputText']); - if (StrCast(selected['gptRubric']) === '') { - const rubricText = 'Rubric: ' + await this.generateRubric(StrCast(selected['gptInputText']), selected) - } + const questionText = 'Question: ' + StrCast(selected['gptInputText']); - const rubricText = 'Rubric: ' + (StrCast(selected['gptRubric'])) - // const rubricText = 'Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text); - const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText; + if (StrCast(selected['gptRubric']) === '') { + const rubricText = 'Rubric: ' + await this.generateRubric(StrCast(selected['gptInputText']), selected) + } - try { - const res = await gptAPICall(queryText, GPTCallType.QUIZ); - if (!res) { - console.error('GPT call failed'); - return; - } - console.log(res) - this.setQuizResp(res) + const rubricText = 'Rubric: ' + (StrCast(selected['gptRubric'])) + // const rubricText = 'Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text); + const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText; - this.setLoading(false); - this.setSortDone(true); + try { + const res = await gptAPICall(queryText, GPTCallType.QUIZ); + if (!res) { + console.error('GPT call failed'); + return; + } + console.log(res) + this.setQuizResp(res) + this.conversationArray.push(res) + + this.setLoading(false); + this.setSortDone(true); + + // this._outputValue = res; + } catch (err) { + console.error('GPT call failed'); + } + + + if (this.onQuizRandom){ + this.onQuizRandom() + } - // this._outputValue = res; - } catch (err) { - console.error('GPT call failed'); - } + + + + + // switch(quizType){ + // default: + + // } + + + } @@ -254,11 +291,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { } - - /** - * Sorts cards in the CollectionCardDeckView - */ - generateSort = async () => { + generateCard = async () => { console.log(this.chatSortPrompt + "USER PROMPT") this.setLoading(true); this.setSortDone(false); @@ -268,17 +301,50 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { } try { - const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); + // const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); + const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE); + const questionNumber = questionType.split(' ')[0] + console.log(questionType) + let res = '' + + switch (questionNumber) { + case '1': + case '2': + case '4': + res = await gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt); + break + case '6': + res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); + break + default: + + const selected = DocumentView.SelectedDocs().lastElement(); + const questionText = StrCast(selected!['gptInputText']); + + + res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt); + break + } + + + // const res = await gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt); // Trigger the callback with the result if (this.onSortComplete) { - this.onSortComplete(res || 'Something went wrong :('); + 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*(?:------|$)/) || []; - const explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found'; + explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found'; + } // Set the extracted explanation to sortRespText this.setSortRespText(explanation); + this.conversationArray.push(this.sortRespText) + this.scrollToBottom() console.log(res); } @@ -289,6 +355,43 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { this.setLoading(false); this.setSortDone(true); }; + + + // /** + // * Sorts cards in the CollectionCardDeckView + // */ + // generateSort = async () => { + // console.log(this.chatSortPrompt + "USER PROMPT") + // this.setLoading(true); + // this.setSortDone(false); + + // if (this.regenerateCallback) { + // await this.regenerateCallback(); + // } + + // try { + // const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); + + // // Trigger the callback with the result + // if (this.onSortComplete) { + // this.onSortComplete(res || 'Something went wrong :('); + + // // Extract explanation surrounded by ------ at the top or both at the top and bottom + // const explanationMatch = res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) || []; + // const explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found'; + + // // Set the extracted explanation to sortRespText + // this.setSortRespText(explanation); + + // console.log(res); + // } + // } catch (err) { + // console.error(err); + // } + + // this.setLoading(false); + // this.setSortDone(true); + // }; /** @@ -422,19 +525,40 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { super(props); makeObservable(this); GPTPopup.Instance = this; + this.messagesEndRef = React.createRef(); + } + private messagesEndRef: React.RefObject<HTMLDivElement>; + + + scrollToBottom = () => { + setTimeout(() => { + // Code to execute after 1 second (1000 ms) + if (this.messagesEndRef.current) { + + this.messagesEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' }); + } + + }, 50); + } + componentDidUpdate = () => { if (this.loading) { this.setDone(false); } }; + @observable quizMode : GPTQuizType = GPTQuizType.CURRENT + @action setQuizMode (g: GPTQuizType) { + this.quizMode = g + } + cardMenu = () => ( <div className="btns-wrapper-gpt"> <Button - tooltip="Have ChatGPT sort your cards for you!" - text="Sort Cards!" + tooltip="Have ChatGPT sort, tag, define, or filter your cards for you!" + text="Modify/Sort Cards!" onClick={() => this.setMode(GPTPopupMode.SORT)} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} @@ -450,7 +574,16 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { <Button tooltip="Test your knowledge with ChatGPT!" text="Quiz Cards!" - onClick={() => this.setMode(GPTPopupMode.QUIZ)} + onClick={() => { + this.conversationArray = ['Define the selected card!'] + this.setMode(GPTPopupMode.QUIZ) + if (this.onQuizRandom){ + this.onQuizRandom() + } + + + + }} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} style={{ @@ -464,62 +597,94 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { /> </div> ) + + @observable conversationArray: string[] = ["Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. If you're not sure if something is possible-- just ask!"] + + + handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => { + if (e.key === 'Enter') { + e.stopPropagation(); + + if (isSort) { + this.conversationArray.push(this.chatSortPrompt); + await this.generateCard(); + this.chatSortPrompt = '' + + } else { + this.conversationArray.push(this.quizAnswer); + await this.generateQuiz(); + this.quizAnswer = '' + + } + + this.scrollToBottom(); + } + } cardActual = (opt: GPTPopupMode) => { const isSort = opt === GPTPopupMode.SORT // if (opt === GPTPopupMode.SORT) { return ( - !this.sortDone ? ( - <> - <div className="btns-wrapper-gpt"> + // !this.sortDone ? ( + // <> + <div className="btns-wrapper-gpt"> + {/* Chat bubble container with scroll */} + <div className="chat-wrapper"> + <div className="chat-bubbles"> + {this.conversationArray.map((message, index) => ( + <div + key={index} + className={`chat-bubble ${index % 2 === 1 ? 'user-message' : 'chat-message'}`} + > + {message} + </div> + ))} + + {/* Conditional Loading message */} + {(!this.cardsDoneLoading || this.loading) && ( + <div className={`chat-bubble chat-message`}> + ... + </div> + )} + </div> + + <div ref={this.messagesEndRef} style= {{height: '100px'}} /> + + </div> + + <div className="inputWrapper"> <input className="searchBox-input" defaultValue="" autoComplete="off" onChange={isSort ? this.sortPromptChanged : this.quizAnswerChanged} - onKeyDown={e => { - if (e.key === 'Enter') { - isSort ? this.generateSort() : this.generateQuiz(); + onKeyDown={(e) => { + this.handleKeyPress(e, isSort) + + } - e.stopPropagation(); - }} + } type="text" - placeholder={`${isSort ? 'How do you want to sort your cards?' : 'Define the selected card?'}`} - id="search-input" - style={{ width: '100%' }} - /> - {/* </div> - <div className="btns-wrapper-gpt"> */} - <Button - tooltip={`${isSort ? 'HaveChatGPT sort ypur cards for you!' : 'See how close you get to the right answer!'}`} - text={`${isSort ? 'Sort!' : 'Check!'}`} - onClick={isSort ? this.generateSort : this.generateQuiz} - color={StrCast(Doc.UserDoc().userVariantColor)} - type={Type.TERT} - style={{ - width: '100%', - textAlign: 'center', - color: '#ffffff', - fontSize: '16px', - height: '40%', - - }} - /> - </div> - </> - ) : ( - <div> - <div className="content-wrapper"> - <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : `${isSort ? this.sortRespText : this.quizRespText}`}</p> - <IconButton - tooltip="Generate Again" - onClick={() => this.setSortDone(false)} - icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} - color={StrCast(Doc.UserDoc().userVariantColor)} + placeholder={`${isSort ? 'Have ChatGPT sort, tag, define, or filter your cards for you!' : 'Define the selected card!'}`} /> </div> </div> - ) + + + // </> + // ) : ( + // <div> + // <div className="content-wrapper"> + // <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : `${isSort ? this.sortRespText : this.quizRespText}`}</p> + // <IconButton + // tooltip="Generate Again" + // onClick={() => this.setSortDone(false)} + // icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} + // color={StrCast(Doc.UserDoc().userVariantColor)} + // /> + // </div> + // </div> + // ) ); // } else if (opt === GPTPopupMode.QUIZ) { // return ( @@ -563,10 +728,10 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { }; sortBox = () => ( - <div> + <div style = {{height: '80%'}}> {this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')} <> - {!this.cardsDoneLoading || this.loading ? ( + {!this.cardsDoneLoading? ( <div className="content-wrapper"> <div className="loading-spinner"> <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> @@ -737,7 +902,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { tooltip="back" icon={<CgCornerUpLeft size="16px" />} onClick={() => this.mode = GPTPopupMode.CARD} - style = {{right: '-20%'}} + style = {{right: '50px', position: 'absolute'}} /> )} <IconButton |
