diff options
Diffstat (limited to 'src/client/views/pdf')
-rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 2 | ||||
-rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.scss | 20 | ||||
-rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 244 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 2 |
4 files changed, 216 insertions, 52 deletions
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 2f6824466..96f025ca0 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -133,7 +133,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { _layout_autoHeight: true, }); - this.addToCollection?.(newCol); + this.addToCollection?.(newCol); //this._props.addDocument(newCol) }; pointerDown = (e: React.PointerEvent) => { diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss index 6d8793f82..291f272ce 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.scss +++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss @@ -7,10 +7,13 @@ $highlightedText: #82e0ff; .summary-box { position: fixed; - bottom: 10px; - right: 10px; + top: 115px; + left: 75px; width: 250px; + height: 200px; min-height: 200px; + min-width: 180px; + border-radius: 16px; padding: 16px; padding-bottom: 0; @@ -21,6 +24,18 @@ $highlightedText: #82e0ff; background-color: #ffffff; box-shadow: 0 2px 5px #7474748d; color: $textgrey; + resize: both; /* Allows resizing */ + overflow: auto; + + .resize-handle { + width: 10px; + height: 10px; + background: #ccc; + position: absolute; + right: 0; + bottom: 0; + cursor: se-resize; + } .summary-heading { display: flex; @@ -84,6 +99,7 @@ $highlightedText: #82e0ff; font-size: 9px; padding: 10px; color: #ffffff; + width: 100%; background-color: $button; border-radius: 5px; } diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index cb5aad32d..71d437c3d 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -20,15 +20,19 @@ import './GPTPopup.scss'; import { SettingsManager } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; + export enum GPTPopupMode { SUMMARY, EDIT, IMAGE, FLASHCARD, DATA, + CARD, SORT, + QUIZ } + interface GPTPopupProps {} @observer @@ -149,6 +153,28 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { this.cardsDoneLoading = done; } + @observable sortRespText: string = '' + + @action setSortRespText(resp: string) { + this.sortRespText = resp + } + + + @observable chatSortPrompt: string = "" + + sortPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => { + this.chatSortPrompt = e.target.value; + }); + + @observable private regenerateCallback: (() => Promise<void>) | null = null; + + @action public setRegenerateCallback(callback: () => Promise<void>) { + this.regenerateCallback = callback; + } + + + + public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false; public createFilteredDoc: (axes?: any) => boolean = () => false; public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined; @@ -157,24 +183,38 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { * 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); + 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); }; - + + /** * Generates a Dalle image and uploads it to the server. */ @@ -314,59 +354,154 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { } }; + cardMenu = () => ( + <div className="btns-wrapper-gpt"> + <Button + tooltip="Have ChatGPT sort your cards for you!" + text="Sort Cards!" + onClick={this.generateSort} + color={StrCast(Doc.UserDoc().userVariantColor)} + type={Type.TERT} + style={{ + width: '90%', + textAlign: 'center', + color: '#ffffff', + fontSize: '16px', + }} + /> + <Button + tooltip="Test your knowledge with ChatGPT!" + text="Quiz Cards!" + onClick={this.generateSort} + color={StrCast(Doc.UserDoc().userVariantColor)} + type={Type.TERT} + style={{ + width: '90%', + textAlign: 'center', + color: '#ffffff', + fontSize: '16px', + }} + /> + </div> + + ) + + cardActual = (opt: string) => { + const isSort = opt === 'sort' + + if (opt === 'sort'){ + return ( + <> + <div className="btns-wrapper-gpt"> + <input + className="searchBox-input" + defaultValue="" + autoComplete="off" + onChange={this.sortPromptChanged} + onKeyDown={e => { + if (e.key === 'Enter') { + this.generateSort(); + } + e.stopPropagation(); + }} + type="text" + placeholder= {`How do you want to sort your cards ?` } + id="search-input" + style={{ width: '100%' }} + /> + </div> + <div className="btns-wrapper-gpt"> + <Button + tooltip="Have ChatGPT sort your cards for you!" + text="Sort!" + onClick={this.generateSort} + color={StrCast(Doc.UserDoc().userVariantColor)} + type={Type.TERT} + style={{ + width: '90%', + textAlign: 'center', + color: '#ffffff', + fontSize: '16px', + }} + /> + </div> + </> + ) + + } + +} + sortBox = () => ( - <> - <div> - {this.heading('SORTING')} - {this.loading ? ( + <> + <div> + {this.heading('SORTING')} + <> + {!this.cardsDoneLoading || this.loading ? ( <div className="content-wrapper"> <div className="loading-spinner"> <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> - <span>Loading...</span> + {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>} </div> </div> ) : ( - <> - {!this.cardsDoneLoading ? ( - <div className="content-wrapper"> - <div className="loading-spinner"> - <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> - <span>Reading Cards...</span> - </div> + !this.sortDone && ( + <> + <div className="btns-wrapper-gpt"> + <input + className="searchBox-input" + defaultValue="" + autoComplete="off" + onChange={this.sortPromptChanged} + onKeyDown={e => { + if (e.key === 'Enter') { + this.generateSort(); + } + e.stopPropagation(); + }} + type="text" + placeholder="How do you want to sort your cards ?" + id="search-input" + style={{ width: '100%' }} + /> </div> - ) : ( - !this.sortDone && ( - <div className="btns-wrapper-gpt"> - <Button - tooltip="Have ChatGPT sort your cards for you!" - text="Sort!" - onClick={this.generateSort} - color={StrCast(Doc.UserDoc().userVariantColor)} - type={Type.TERT} - style={{ - width: '90%', // Almost as wide as the container - textAlign: 'center', - color: '#ffffff', // White text - fontSize: '16px', // Adjust font size as needed - }} - /> - </div> - ) - )} - - {this.sortDone && ( - <div> - <div className="content-wrapper"> - <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : 'Sorting done! Feel free to move things around / regenerate :) !'}</p> - <IconButton tooltip="Generate Again" onClick={() => this.setSortDone(false)} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} /> - </div> + <div className="btns-wrapper-gpt"> + <Button + tooltip="Have ChatGPT sort your cards for you!" + text="Sort!" + onClick={this.generateSort} + color={StrCast(Doc.UserDoc().userVariantColor)} + type={Type.TERT} + style={{ + width: '90%', + textAlign: 'center', + color: '#ffffff', + fontSize: '16px', + }} + /> </div> - )} - </> + </> + ) )} - </div> - </> - ); + + {this.sortDone && ( + <div> + <div className="content-wrapper"> + <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : `${this.sortRespText}`}</p> + <IconButton + tooltip="Generate Again" + onClick={() => this.setSortDone(false)} + icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} + color={StrCast(Doc.UserDoc().userVariantColor)} + /> + </div> + </div> + )} + </> + </div> + </> +); + imageBox = () => ( <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}> {this.heading('GENERATED IMAGE')} @@ -518,9 +653,22 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { render() { return ( - <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}> - {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.DATA ? this.dataAnalysisBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.mode === GPTPopupMode.SORT ? this.sortBox() : null} + <div + className="summary-box" + style={{ display: this.visible ? 'flex' : 'none' }} + > + {this.mode === GPTPopupMode.SUMMARY + ? this.summaryBox() + : this.mode === GPTPopupMode.DATA + ? this.dataAnalysisBox() + : this.mode === GPTPopupMode.IMAGE + ? this.imageBox() + : this.mode === GPTPopupMode.SORT + ? this.sortBox() + : null} + <div className="resize-handle" /> </div> ); } + } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index c7d5e15b4..709e9a23c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -33,7 +33,7 @@ import './PDFViewer.scss'; // pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; // The workerSrc property shall be specified. -Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.3.136/build/pdf.worker.mjs'; +Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.2.67/build/pdf.worker.mjs'; interface IViewerProps extends FieldViewProps { pdfBox: PDFBox; |