diff options
author | bobzel <zzzman@gmail.com> | 2024-09-17 21:05:17 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-09-17 21:05:17 -0400 |
commit | 78f348cd1406cfaf3d0247973b826783ab3532b5 (patch) | |
tree | c4aee676a52995bdbe2ea95df1763d5c08eaf198 | |
parent | bde7bd192b1a81e3fd1c4ead13e821b95b42f3cf (diff) |
from last
-rw-r--r-- | src/client/views/FilterPanel.tsx | 170 | ||||
-rw-r--r-- | src/client/views/global/globalScripts.ts | 14 | ||||
-rw-r--r-- | src/client/views/nodes/IconTagBox.tsx | 22 | ||||
-rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 282 |
4 files changed, 204 insertions, 284 deletions
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index f3f447e22..8133a4d0d 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -1,34 +1,29 @@ /* eslint-disable react/jsx-props-no-spreading */ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@mui/material'; import { action, computed, makeObservable, observable, ObservableMap } from 'mobx'; -import { observer } from 'mobx-react'; +import { observer, useLocalObservable } from 'mobx-react'; import * as React from 'react'; +import { useEffect, useRef } from 'react'; import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider'; import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai'; import { CiCircleRemove } from 'react-icons/ci'; import { Doc, DocListCast, Field, FieldType, LinkedTo, StrListCast } from '../../fields/Doc'; +import { DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; +import { DocCast, StrCast } from '../../fields/Types'; +import { Button, CurrentUserUtils } from '../util/CurrentUserUtils'; import { SearchUtil } from '../util/SearchUtil'; import { SnappingManager } from '../util/SnappingManager'; -import { undoable, undoBatch } from '../util/UndoManager'; +import { undoable } from '../util/UndoManager'; import { FieldsDropdown } from './FieldsDropdown'; import './FilterPanel.scss'; import { DocumentView } from './nodes/DocumentView'; +import { ButtonType } from './nodes/FontIconBox/FontIconBox'; import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; import { ObservableReactComponent } from './ObservableReactComponent'; -import { Button } from '../util/CurrentUserUtils'; -import { ButtonType } from './nodes/FontIconBox/FontIconBox'; -import { DocCast } from '../../fields/Types'; -import { CurrentUserUtils } from '../util/CurrentUserUtils'; -import { StrCast } from '../../fields/Types'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { DocData } from '../../fields/DocSymbols'; -import { Tooltip } from '@mui/material'; -import { useLocalObservable } from 'mobx-react'; -import { useRef } from 'react'; -import { useEffect } from 'react'; - interface filterProps { Document: Doc; @@ -37,8 +32,7 @@ interface filterProps { @observer export class FilterPanel extends ObservableReactComponent<filterProps> { @observable _selectedFacetHeaders = new Set<string>(); - public static Instance: FilterPanel; - + public static Instance: FilterPanel; constructor(props: filterProps) { super(props); @@ -227,7 +221,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { }; /** - * Allows users to add a filter hotkey to the properties panel. Will also update the multitoggle at the top menu and the + * Allows users to add a filter hotkey to the properties panel. Will also update the multitoggle at the top menu and the * icontags tht are displayed on the documents themselves * @param hotKey tite of the new hotkey */ @@ -246,21 +240,20 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}' }, }; - const currHotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles) + const currHotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); - Doc.UserDoc().myFilterHotKeyTitles = new List<string>(currHotKeys.concat(hotKey)) + Doc.UserDoc().myFilterHotKeyTitles = new List<string>(currHotKeys.concat(hotKey)); - Doc.UserDoc()[hotKey] = 'bolt' + Doc.UserDoc()[hotKey] = 'bolt'; const newBtn = CurrentUserUtils.setupContextMenuBtn(newKey, filter); newBtn.isSystem = newBtn[DocData].isSystem = undefined; - const subDocs = DocListCast(filter.data) - const opts = subDocs[subDocs.length-1] + const subDocs = DocListCast(filter.data); + const opts = subDocs[subDocs.length - 1]; Doc.AddDocToList(filter, 'data', newBtn, opts, true); - } + }; - /** * Renders the newly formed hotkey icon buttons * @returns the buttons to be rendered @@ -268,16 +261,16 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { hotKeyButtons = () => { const selected = DocumentView.SelectedDocs().lastElement(); const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); - + // Selecting a button should make it so that the icon on the top filter panel becomes said icon const buttons = hotKeys.map((hotKey, i) => ( <Tooltip key={hotKey} title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> - <HotKeyIconButton hotKey={hotKey} selected = {selected}/> + <HotKeyIconButton hotKey={hotKey} selected={selected} /> </Tooltip> )); - + return buttons; - } + }; // @observable iconPanelMap: Map<string, number> = new Map(); @@ -465,18 +458,16 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { } } - - interface HotKeyButtonProps { hotKey: string; - selected?: Doc + selected?: Doc; } /** - * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon, - * title, and delete. + * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon, + * title, and delete. */ -const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, selected}) => { +const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, selected }) => { const state = useLocalObservable(() => ({ isActive: false, isEditing: false, @@ -496,7 +487,7 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select }, setHotKey(newHotKey: string) { this.myHotKey = newHotKey; - } + }, })); const panelRef = useRef<HTMLDivElement>(null); @@ -506,31 +497,29 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select state.toggleActive(); }; - const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles) + const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); const buttons = DocCast(Doc.UserDoc().myContextMenuBtns); const filter = DocCast(buttons.Filter); /** * The doc of the button in the context menu that corresponds to the current hotkey - * @returns + * @returns */ const myHotKeyDoc = () => { - const hotKeyDocs = DocListCast(filter.data) - return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0] - - } + const hotKeyDocs = DocListCast(filter.data); + return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0]; + }; /** - * Removes a hotkey from list + * Removes a hotkey from list */ const removeHotKey = () => { Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc()); - - } + }; /** * Deselects if the user clicks outside the button - * @param event + * @param event */ const handleClickOutside = (event: MouseEvent) => { if (panelRef.current && !panelRef.current.contains(event.target as Node)) { @@ -538,24 +527,23 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select if (state.isEditing) { state.stopEditing(); - updateFromInput() + updateFromInput(); } } }; /** - * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new + * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new * hotkey with the current icon */ - const updateFromInput = undoable (() => { - const myDoc = myHotKeyDoc() - Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => k === hotKey ? state.myHotKey : k)); - Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]) - Doc.UserDoc()[hotKey] = '' - myDoc.title = state.myHotKey - myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility` - }, '') - + const updateFromInput = undoable(() => { + const myDoc = myHotKeyDoc(); + Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => (k === hotKey ? state.myHotKey : k))); + Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]); + Doc.UserDoc()[hotKey] = ''; + myDoc.title = state.myHotKey; + myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`; + }, ''); useEffect(() => { document.addEventListener('mousedown', handleClickOutside); @@ -564,18 +552,21 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select }; }, []); - const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard' ]; + const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard']; /** * Panel of icons the user can choose from to represent their tag */ const iconPanel = iconOpts.map((icon, i) => ( - <button key={i} onClick={undoable((e: React.MouseEvent) => { - e.stopPropagation; - Doc.UserDoc()[hotKey] = icon; - myHotKeyDoc().icon = icon - }, '')} className='icon-panel-button'> - <FontAwesomeIcon icon={icon as any} color = {SnappingManager.userColor}/> + <button + key={i} + onClick={undoable((e: React.MouseEvent) => { + e.stopPropagation; + Doc.UserDoc()[hotKey] = icon; + myHotKeyDoc().icon = icon; + }, '')} + className="icon-panel-button"> + <FontAwesomeIcon icon={icon as any} color={SnappingManager.userColor} /> </button> )); @@ -584,62 +575,57 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select */ return ( - <div className={`filterHotKey-button`} - onClick={(e) => { + <div + className={`filterHotKey-button`} + onClick={e => { e.stopPropagation(); state.startEditing(); setTimeout(() => inputRef.current?.focus(), 0); - }} - > + }}> <div className={`hotKey-icon-button ${state.isActive ? 'active' : ''}`} ref={panelRef}> <Tooltip title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> <button type="button" - className='hotKey-icon' + className="hotKey-icon" onClick={(e: React.MouseEvent) => { - e.stopPropagation(); + e.stopPropagation(); handleClick(); - }} - > - <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as any} size="2xl" color={SnappingManager.userColor}/> + }}> + <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as any} size="2xl" color={SnappingManager.userColor} /> </button> </Tooltip> - {state.isActive && ( - <div className="icon-panel"> - {iconPanel} - </div> - )} + {state.isActive && <div className="icon-panel">{iconPanel}</div>} </div> {state.isEditing ? ( <input ref={inputRef} type="text" value={state.myHotKey.toUpperCase()} - onChange={(e) => state.setHotKey(e.target.value)} + onChange={e => state.setHotKey(e.target.value)} onBlur={() => { state.stopEditing(); - updateFromInput() + updateFromInput(); }} - onKeyDown={(e) => { + onKeyDown={e => { if (e.key === 'Enter') { state.stopEditing(); - updateFromInput() - + updateFromInput(); } }} - className='hotkey-title-input' + className="hotkey-title-input" /> ) : ( - <p className='hotkey-title'>{hotKey.toUpperCase()}</p> + <p className="hotkey-title">{hotKey.toUpperCase()}</p> )} - <button className='hotKey-close' onClick={(e: React.MouseEvent) => { - e.stopPropagation(); - Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey)); - removeHotKey() - - }}> - <FontAwesomeIcon icon={'x' as any} color={SnappingManager.userColor}/> + <button + className="hotKey-close" + onClick={(e: React.MouseEvent) => { + e.stopPropagation(); + Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey)); + removeHotKey(); + }}> + <FontAwesomeIcon icon={'x' as any} color={SnappingManager.userColor} /> </button> </div> ); -})
\ No newline at end of file +}); diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 99bd537b1..6ccbf28f8 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -2,13 +2,13 @@ import { Colors } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { aggregateBounds } from '../../../Utils'; -import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { InkTool } from '../../../fields/InkField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; import { Gestures } from '../../../pen-gestures/GestureTypes'; -import { DocumentType } from '../../documents/DocumentTypes'; +import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { UndoManager, undoable } from '../../util/UndoManager'; @@ -35,16 +35,12 @@ import { ImageBox } from '../nodes/ImageBox'; import { VideoBox } from '../nodes/VideoBox'; import { WebBox } from '../nodes/WebBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; -import { NumListCast, StrListCast } from '../../../fields/Doc'; -import { List } from '../../../fields/List'; -import { CollectionViewType } from '../../documents/DocumentTypes'; // import { InkTranscription } from '../InkTranscription'; import { Docs } from '../../documents/Documents'; -import { CollectionSubView } from '../collections/CollectionSubView'; -import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup'; -import { PropertiesView } from '../PropertiesView'; -import { MainView } from '../MainView'; import { SnappingManager } from '../../util/SnappingManager'; +import { MainView } from '../MainView'; +import { PropertiesView } from '../PropertiesView'; +import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup'; // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function IsNoneSelected() { return DocumentView.Selected().length <= 0; diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx index e6c0961ed..e076e691a 100644 --- a/src/client/views/nodes/IconTagBox.tsx +++ b/src/client/views/nodes/IconTagBox.tsx @@ -1,23 +1,19 @@ -import React from 'react'; -import { observer } from 'mobx-react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@mui/material'; import { computed } from 'mobx'; -import { ObservableReactComponent } from '../ObservableReactComponent'; -import { NumCast } from '../../../fields/Types'; -import { Doc } from '../../../fields/Doc'; +import { observer } from 'mobx-react'; +import React from 'react'; import { numberRange } from '../../../Utils'; -import { Tooltip } from '@mui/material'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { undoable } from '../../util/UndoManager'; -import { BoolCast } from '../../../fields/Types'; -import { DocCast } from '../../../fields/Types'; -import './IconTagBox.scss'; +import { Doc, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; -import { StrListCast } from '../../../fields/Doc'; -import { StrCast } from '../../../fields/Types'; +import { BoolCast, DocCast, NumCast, StrCast } from '../../../fields/Types'; import { CollectionViewType } from '../../documents/DocumentTypes'; import { SnappingManager } from '../../util/SnappingManager'; +import { undoable } from '../../util/UndoManager'; import { MainView } from '../MainView'; +import { ObservableReactComponent } from '../ObservableReactComponent'; import { PropertiesView } from '../PropertiesView'; +import './IconTagBox.scss'; export interface IconTagProps { doc: Doc; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 53bedbb65..ed3f99377 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -3,7 +3,7 @@ import { Button, IconButton, Type } from 'browndash-components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { CgClose, CgPathBack, CgArrowLeftO, CgCornerUpLeft } from 'react-icons/cg'; +import { CgClose, CgCornerUpLeft } from 'react-icons/cg'; import ReactLoading from 'react-loading'; import { TypeAnimation } from 'react-type-animation'; import { ClientUtils } from '../../../../ClientUtils'; @@ -11,16 +11,14 @@ import { Doc } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { Networking } from '../../../Network'; import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT'; -import { Docs } from '../../../documents/Documents'; import { DocUtils } from '../../../documents/DocUtils'; -import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { AnchorMenu } from '../AnchorMenu'; -import './GPTPopup.scss'; +import { Docs } from '../../../documents/Documents'; import { SettingsManager } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; +import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocumentView } from '../../nodes/DocumentView'; -import { DocCast } from '../../../../fields/Types'; -import { RTFCast } from '../../../../fields/Types'; +import { AnchorMenu } from '../AnchorMenu'; +import './GPTPopup.scss'; export enum GPTPopupMode { SUMMARY, @@ -30,21 +28,15 @@ export enum GPTPopupMode { DATA, CARD, SORT, - QUIZ + QUIZ, } export enum GPTQuizType { CURRENT = 0, CHOOSE = 1, - MULTIPLE = 2 - + MULTIPLE = 2, } - - - - - interface GPTPopupProps {} @observer @@ -168,30 +160,29 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { this.cardsDoneLoading = done; } - @observable sortRespText: string = '' + @observable sortRespText: string = ''; @action setSortRespText(resp: string) { - this.sortRespText = resp + this.sortRespText = resp; } - @observable chatSortPrompt: string = "" + @observable chatSortPrompt: string = ''; sortPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => { this.chatSortPrompt = e.target.value; }); - @observable quizAnswer: string = "" + @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 conversationArray: string[] = ['Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. ']; /** * When the cards are in quiz mode in the card view, allows gpt to determine whether the user's answer was correct - * @returns + * @returns */ generateQuiz = async () => { this.setLoading(true); @@ -201,14 +192,13 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { 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 rubricText = 'Rubric: ' + (await this.generateRubric(StrCast(selected['gptInputText']), selected)); } - const rubricText = 'Rubric: ' + (StrCast(selected['gptRubric'])) + const rubricText = 'Rubric: ' + StrCast(selected['gptRubric']); const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText; try { @@ -217,23 +207,20 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { console.error('GPT call failed'); return; } - console.log(res) - this.setQuizResp(res) - this.conversationArray.push(res) + console.log(res); + this.setQuizResp(res); + this.conversationArray.push(res); this.setLoading(false); this.setSortDone(true); - } catch (err) { console.error('GPT call failed'); } - - if (this.onQuizRandom){ - this.onQuizRandom() + if (this.onQuizRandom) { + this.onQuizRandom(); } - - } + }; /** * Generates a rubric by which to compare the user's answer to @@ -241,105 +228,95 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { * @param doc the doc the user is providing info about * @returns gpt's response */ - generateRubric = async (inputText: string, doc:Doc) => { + generateRubric = async (inputText: string, doc: Doc) => { try { - const res = await gptAPICall(inputText, GPTCallType.RUBRIC); - doc['gptRubric']= res; - return res + const res = await gptAPICall(inputText, GPTCallType.RUBRIC); + doc['gptRubric'] = res; + return res; } catch (err) { console.error('GPT call failed'); } - - } - - + }; @observable private regenerateCallback: (() => Promise<void>) | null = null; /** * Callback function that causes the card view to update the childpair string list - * @param callback + * @param callback */ @action public setRegenerateCallback(callback: () => Promise<void>) { 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 + @observable quizRespText: string = ''; + @action setQuizResp(resp: string) { + this.quizRespText = resp; } /** * Generates a response to the user's question depending on the type of their question */ generateCard = async () => { - console.log(this.chatSortPrompt + "USER PROMPT") + 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); const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE); - const questionNumber = questionType.split(' ')[0] - console.log(questionType) - let res = '' + 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 + break; case '6': res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); - break + break; default: - const selected = DocumentView.SelectedDocs().lastElement(); const questionText = StrCast(selected!['gptInputText']); - res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt); - break + break; } // Trigger the callback with the result if (this.onSortComplete) { this.onSortComplete(res || 'Something went wrong :(', questionNumber, questionType.split(' ').slice(1).join(' ')); - let explanation = res + 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'; + } - 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); - this.conversationArray.push(this.sortRespText) - this.scrollToBottom() - + this.conversationArray.push(this.sortRespText); + this.scrollToBottom(); + console.log(res); } } catch (err) { console.error(err); } - + this.setLoading(false); this.setSortDone(true); }; @@ -476,21 +453,16 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { makeObservable(this); GPTPopup.Instance = this; this.messagesEndRef = React.createRef(); - } - - scrollToBottom = () => { - setTimeout(() => { - // Code to execute after 1 second (1000 ms) - if (this.messagesEndRef.current) { - + setTimeout(() => { + // Code to execute after 1 second (1000 ms) + if (this.messagesEndRef.current) { this.messagesEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' }); - } - - }, 50); - } + } + }, 50); + }; componentDidUpdate = () => { if (this.loading) { @@ -498,9 +470,9 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { } }; - @observable quizMode : GPTQuizType = GPTQuizType.CURRENT - @action setQuizMode (g: GPTQuizType) { - this.quizMode = g + @observable quizMode: GPTQuizType = GPTQuizType.CURRENT; + @action setQuizMode(g: GPTQuizType) { + this.quizMode = g; } cardMenu = () => ( @@ -517,21 +489,18 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { textAlign: 'center', color: '#ffffff', fontSize: '16px', - marginBottom: '10px' + marginBottom: '10px', }} /> <Button tooltip="Test your knowledge with ChatGPT!" text="Quiz Cards!" onClick={() => { - this.conversationArray = ['Define the selected card!'] - this.setMode(GPTPopupMode.QUIZ) - if (this.onQuizRandom){ - this.onQuizRandom() + this.conversationArray = ['Define the selected card!']; + this.setMode(GPTPopupMode.QUIZ); + if (this.onQuizRandom) { + this.onQuizRandom(); } - - - }} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} @@ -541,13 +510,10 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { color: '#ffffff', fontSize: '16px', height: '40%', - }} /> </div> - ) - - + ); handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => { if (e.key === 'Enter') { @@ -556,85 +522,72 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { if (isSort) { this.conversationArray.push(this.chatSortPrompt); await this.generateCard(); - this.chatSortPrompt = '' - + this.chatSortPrompt = ''; } else { this.conversationArray.push(this.quizAnswer); await this.generateQuiz(); - this.quizAnswer = '' - + this.quizAnswer = ''; } this.scrollToBottom(); } - } - + }; + cardActual = (opt: GPTPopupMode) => { - const isSort = opt === GPTPopupMode.SORT - return ( - - <div className="btns-wrapper-gpt"> - <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> - ))} - {(!this.cardsDoneLoading || this.loading) && ( - <div className={`chat-bubble chat-message`}> - ... - </div> - )} + const isSort = opt === GPTPopupMode.SORT; + return ( + <div className="btns-wrapper-gpt"> + <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> + ))} + {(!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="" - value={isSort ? this.chatSortPrompt : this.quizAnswer} // Controlled input - autoComplete="off" - onChange={isSort ? this.sortPromptChanged : this.quizAnswerChanged} - onKeyDown={(e) => { - this.handleKeyPress(e, isSort) + <div ref={this.messagesEndRef} style={{ height: '100px' }} /> + </div> - - } - } - type="text" - placeholder={`${isSort ? 'Have ChatGPT sort, tag, define, or filter your cards for you!' : 'Define the selected card!'}`} - /> - </div> - </div> - ); + <div className="inputWrapper"> + <input + className="searchBox-input" + defaultValue="" + value={isSort ? this.chatSortPrompt : this.quizAnswer} // Controlled input + autoComplete="off" + onChange={isSort ? this.sortPromptChanged : this.quizAnswerChanged} + 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!'}`} + /> + </div> + </div> + ); }; - + sortBox = () => ( - <div style = {{height: '80%'}}> + <div style={{ height: '80%' }}> {this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')} <> - {!this.cardsDoneLoading? ( + {!this.cardsDoneLoading ? ( <div className="content-wrapper"> <div className="loading-spinner"> <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>} </div> </div> + ) : this.mode === GPTPopupMode.CARD ? ( + this.cardMenu() ) : ( - (this.mode === GPTPopupMode.CARD ? this.cardMenu() : this.cardActual(this.mode)) // Call the functions to render JSX - )} + this.cardActual(this.mode) + ) // Call the functions to render JSX + } </> </div> ); - - imageBox = () => ( <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}> @@ -785,32 +738,25 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { <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' }} /> )} <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => { - this.setVisible(false)}} + this.setVisible(false); + }} /> - </> )} </div> ); - render() { let content; - + switch (this.mode) { case GPTPopupMode.SUMMARY: content = this.summaryBox(); @@ -829,16 +775,12 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { default: content = null; } - + return ( - <div - className="summary-box" - style={{ display: this.visible ? 'flex' : 'none' }} - > + <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}> {content} <div className="resize-handle" /> </div> ); } - } |