diff options
| author | Sophie Zhang <sophie_zhang@brown.edu> | 2023-04-13 01:13:33 -0400 |
|---|---|---|
| committer | Sophie Zhang <sophie_zhang@brown.edu> | 2023-04-13 01:13:33 -0400 |
| commit | db582e135fceb6162d0c9cf00e2580fb1349fddb (patch) | |
| tree | a072ab129241e5ed06fb09d582d5339be3edb889 /src/client/views/pdf/GPTPopup/GPTPopup.tsx | |
| parent | a0ae93e3b14069c0de419fc5dcade84d460a0b30 (diff) | |
added text edits
Diffstat (limited to 'src/client/views/pdf/GPTPopup/GPTPopup.tsx')
| -rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx new file mode 100644 index 000000000..91bc0a7ff --- /dev/null +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -0,0 +1,186 @@ +import React = require('react'); +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import ReactLoading from 'react-loading'; +import Typist from 'react-typist'; +import { Doc } from '../../../../fields/Doc'; +import { Docs } from '../../../documents/Documents'; +import './GPTPopup.scss'; + +export enum GPTPopupMode { + SUMMARY, + EDIT, +} + +interface GPTPopupProps { + visible: boolean; + text: string; + loading: boolean; + mode: GPTPopupMode; + callSummaryApi: (e: React.PointerEvent) => Promise<void>; + callEditApi: (e: React.PointerEvent) => Promise<void>; + replaceText: (replacement: string) => void; + highlightRange?: number[]; +} + +@observer +export class GPTPopup extends React.Component<GPTPopupProps> { + static Instance: GPTPopup; + + @observable + private done: boolean = false; + @observable + private sidebarId: string = ''; + + @action + public setDone = (done: boolean) => { + this.done = done; + }; + @action + public setSidebarId = (id: string) => { + this.sidebarId = id; + }; + + public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false; + + /** + * Transfers the summarization text to a sidebar annotation text document. + */ + private transferToText = () => { + const newDoc = Docs.Create.TextDocument(this.props.text.trim(), { + _width: 200, + _height: 50, + _fitWidth: true, + _autoHeight: true, + }); + this.addDoc(newDoc, this.sidebarId); + }; + + constructor(props: GPTPopupProps) { + super(props); + GPTPopup.Instance = this; + } + + componentDidUpdate = () => { + if (this.props.loading) { + this.setDone(false); + } + }; + + summaryBox = () => ( + <> + <div> + {this.heading('SUMMARY')} + <div className="content-wrapper"> + {!this.props.loading && + (!this.done ? ( + <Typist + key={this.props.text} + avgTypingDelay={15} + cursor={{ hideWhenDone: true }} + onTypingDone={() => { + setTimeout(() => { + this.setDone(true); + }, 500); + }}> + {this.props.text} + </Typist> + ) : ( + this.props.text + ))} + </div> + </div> + {!this.props.loading && ( + <div className="btns-wrapper"> + {this.done ? ( + <> + <button className="icon-btn" onPointerDown={e => this.props.callSummaryApi(e)}> + <FontAwesomeIcon icon="redo-alt" size="lg" /> + </button> + <button + className="text-btn" + onClick={e => { + this.transferToText(); + }}> + Transfer to Text + </button> + </> + ) : ( + <div className="summarizing"> + <span>Summarizing</span> + <ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} /> + <button + className="btn-secondary" + onClick={e => { + this.setDone(true); + }}> + Stop Animation + </button> + </div> + )} + </div> + )} + </> + ); + + editBox = () => { + const hr = this.props.highlightRange; + return ( + hr && ( + <> + <div> + {this.heading('TEXT EDIT SUGGESTIONS')} + <div className="content-wrapper"> + <div> + {this.props.text.slice(0, hr[0])} <span className="highlighted-text">{this.props.text.slice(hr[0], hr[1])}</span> {this.props.text.slice(hr[1])} + </div> + </div> + </div> + {!this.props.loading && ( + <div className="btns-wrapper"> + <> + <button className="icon-btn" onPointerDown={e => this.props.callEditApi(e)}> + <FontAwesomeIcon icon="redo-alt" size="lg" /> + </button> + <button + className="text-btn" + onClick={e => { + this.props.replaceText(this.props.text); + }}> + Replace Text + </button> + </> + </div> + )} + {this.aiWarning()} + </> + ) + ); + }; + + aiWarning = () => + this.done ? ( + <div className="ai-warning"> + <FontAwesomeIcon icon="exclamation-circle" size="sm" style={{ paddingRight: '5px' }} /> + AI generated responses can contain inaccurate or misleading content. + </div> + ) : ( + <></> + ); + + heading = (headingText: string) => ( + <div className="summary-heading"> + <label className="summary-text">{headingText}</label> + {this.props.loading && <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />} + </div> + ); + + render() { + return ( + <div className="summary-box" style={{ display: this.props.visible ? 'flex' : 'none' }}> + {this.props.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()} + </div> + ); + } +} |
