diff options
Diffstat (limited to 'src/client/views/pdf')
| -rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 21 | ||||
| -rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.scss | 52 | ||||
| -rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 129 | ||||
| -rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 3 |
4 files changed, 157 insertions, 48 deletions
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index e6b9cb382..dd8cf7002 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -131,7 +131,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { gptSummarize = async (e: React.PointerEvent) => { GPTPopup.Instance.setVisible(true); this.setHighlightRange(undefined); - this.setGPTMode(GPTPopupMode.SUMMARY); + GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY); GPTPopup.Instance.setLoading(true); try { @@ -183,6 +183,10 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { GPTPopup.Instance.setLoading(false); }; + gptImage = async () => { + console.log(this.GetAnchor(undefined, false)); + }; + /** * Replaces text suggestions from GPT. */ @@ -293,21 +297,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { </button> </Tooltip> )} - {/* <GPTPopup - key="gptpopup" - visible={this.showGPTPopup} - text={this.GPTpopupText} - highlightRange={this.highlightRange} - loading={this.loadingGPT} - callSummaryApi={this.gptSummarize} - callEditApi={this.gptEdit} - replaceText={this.replaceText} - mode={this.GPTMode} - /> */} {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : ( <IconButton tooltip={'Click to Record Annotation'} onPointerDown={this.audioDown} icon={<FontAwesomeIcon icon="microphone" />} color={StrCast(Doc.UserDoc().userColor)} /> )} - {this.canEdit() && <IconButton tooltip={'AI edit suggestions'} onPointerDown={this.gptEdit} icon={<FontAwesomeIcon icon="pencil-alt" />} color={StrCast(Doc.UserDoc().userColor)} />} + {/* Removed text editing for now, not quite ready */} + {/* {this.canEdit() && <IconButton tooltip={'AI edit suggestions'} onPointerDown={this.gptEdit} icon={<FontAwesomeIcon icon="pencil-alt" />} color={StrCast(Doc.UserDoc().userColor)} />} */} + {<IconButton tooltip={'Generate DALL-E Image'} onPointerDown={this.gptImage} icon={<FontAwesomeIcon icon="image" />} color={StrCast(Doc.UserDoc().userColor)} />} <Popup tooltip="Find document to link to selected text" type={Type.PRIM} icon={<FontAwesomeIcon icon={'search'} />} popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />} color={StrCast(Doc.UserDoc().userColor)} /> {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : ( <IconButton tooltip={'Click/Drag to create cropped image'} onPointerDown={this.cropDown} icon={<FontAwesomeIcon icon="image" />} color={StrCast(Doc.UserDoc().userColor)} /> diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss index 2f0ff83e2..478b7d4ba 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.scss +++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss @@ -24,6 +24,7 @@ $highlightedText: #82e0ff; .summary-heading { display: flex; + justify-content: space-between; align-items: center; border-bottom: 1px solid $greyborder; padding-bottom: 5px; @@ -110,6 +111,57 @@ $highlightedText: #82e0ff; } } +.image-content-wrapper { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + padding-bottom: 16px; + + .img-wrapper { + position: relative; + cursor: pointer; + + .img-container { + position: relative; + + img { + position: relative; + } + } + + .img-container::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + opacity: 0; + transition: opacity 0.3s ease; + } + + .btn-container { + position: absolute; + right: 8px; + bottom: 8px; + opacity: 0; + transition: opacity 0.3s ease; + } + + &:hover { + .img-container::after { + opacity: 1; + } + + .btn-container { + opacity: 1; + } + } + } +} + // Typist CSS .Typist .Cursor { display: inline-block; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 9f28cb5d1..aeee90d16 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -1,4 +1,5 @@ import React = require('react'); +import './GPTPopup.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -6,13 +7,14 @@ import ReactLoading from 'react-loading'; import Typist from 'react-typist'; import { Doc } from '../../../../fields/Doc'; import { DocUtils, Docs } from '../../../documents/Documents'; -import './GPTPopup.scss'; -import { Button, IconButton } from 'browndash-components'; -import { StrCast } from '../../../../fields/Types'; +import { Button, IconButton, Type } from 'browndash-components'; +import { NumCast, StrCast } from '../../../../fields/Types'; +import { CgClose } from 'react-icons/cg'; export enum GPTPopupMode { SUMMARY, EDIT, + IMAGE, } interface GPTPopupProps {} @@ -39,6 +41,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> { public setText = (text: string) => { this.text = text; }; + + @observable + public imageUrls: string[] = []; + @action + public setImgUrls = (imgs: string[]) => { + this.imageUrls = imgs; + }; + @observable public mode: GPTPopupMode = GPTPopupMode.SUMMARY; @action @@ -58,6 +68,8 @@ export class GPTPopup extends React.Component<GPTPopupProps> { public setDone = (done: boolean) => { this.done = done; }; + + // change what can be a ref into a ref @observable private sidebarId: string = ''; @action @@ -65,19 +77,30 @@ export class GPTPopup extends React.Component<GPTPopupProps> { this.sidebarId = id; }; - // reference - // if (focusNode) { - // const anchor = srcWeb?.ComponentView?.getAnchor?.(true); - // anchor && DocUtils.MakeLink(htmlDoc, anchor, {}); - // } + // pdfs and webpages + @observable + private targetAnchor: Doc | undefined; + @action + public setTargetAnchor = (anchor: Doc) => { + this.targetAnchor = anchor; + }; + @observable - private pdfAnchor: Doc | undefined; + private imgTargetDoc: Doc | undefined; @action - public setPdfAnchor = (anchor: Doc) => { - this.pdfAnchor = anchor; + public setImgTargetDoc = (anchor: Doc) => { + this.imgTargetDoc = anchor; + }; + + @observable + private textAnchor: Doc | undefined; + @action + public setTextAnchor = (anchor: Doc) => { + this.textAnchor = anchor; }; public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false; + public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined; /** * Transfers the summarization text to a sidebar annotation text document. @@ -90,13 +113,42 @@ export class GPTPopup extends React.Component<GPTPopupProps> { _layout_autoHeight: true, }); this.addDoc(newDoc, this.sidebarId); - if (this.pdfAnchor) { - DocUtils.MakeLink(newDoc, this.pdfAnchor, { + if (this.targetAnchor) { + DocUtils.MakeLink(newDoc, this.targetAnchor, { link_relationship: 'GPT Summary', }); } }; + /** + * Transfers the image urls to actual image docs + */ + private transferToImage = (source: string) => { + console.log('Text Anchor', this.textAnchor); + console.log('Whole doc anchor', this.imgTargetDoc); + const textAnchor = this.textAnchor ?? this.imgTargetDoc; + if (!textAnchor) return; + const newDoc = Docs.Create.ImageDocument(source, { + x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10, + y: NumCast(textAnchor.y), + _height: 200, + _width: 200, + data_nativeWidth: 1024, + data_nativeHeight: 1024, + }); + if (Doc.IsInMyOverlay(textAnchor)) { + newDoc.overlayX = textAnchor.x; + newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height); + Doc.AddToMyOverlay(newDoc); + } else { + this.addToCollection?.(newDoc); + } + // Create link between prompt and image + DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' }); + }; + + private getPreviewUrl = (source: string) => source.split('.').join('_m.'); + constructor(props: GPTPopupProps) { super(props); GPTPopup.Instance = this; @@ -108,6 +160,26 @@ export class GPTPopup extends React.Component<GPTPopupProps> { } }; + imageBox = () => { + return ( + <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}> + {this.heading('GENERATED IMAGE')} + <div className="image-content-wrapper"> + {this.imageUrls.map(rawSrc => ( + <div className="img-wrapper"> + <div className="img-container"> + <img key={rawSrc} src={this.getPreviewUrl(rawSrc)} width={150} height={150} alt="dalle generation" /> + </div> + <div className="btn-container"> + <Button text="Save Image" onClick={() => this.transferToImage(rawSrc)} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} /> + </div> + </div> + ))} + </div> + </div> + ); + }; + summaryBox = () => ( <> <div> @@ -135,30 +207,21 @@ export class GPTPopup extends React.Component<GPTPopupProps> { <div className="btns-wrapper"> {this.done ? ( <> - {/* <button className="icon-btn" onClick={this.callSummaryApi}> - <FontAwesomeIcon icon="redo-alt" size="lg" /> - </button> - <button - className="text-btn" - onClick={e => { - this.transferToText(); - }}> - Transfer to Text - </button> */} - <IconButton onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} /> - <Button text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} /> + <IconButton tooltip="Generate Again" onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} /> + <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} /> </> ) : ( <div className="summarizing"> <span>Summarizing</span> <ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} /> - <button - className="btn-secondary" - onClick={e => { + <Button + text="Stop Animation" + onClick={() => { this.setDone(true); - }}> - Stop Animation - </button> + }} + color={StrCast(Doc.UserDoc().userVariantColor)} + type={Type.TERT} + /> </div> )} </div> @@ -216,14 +279,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> { heading = (headingText: string) => ( <div className="summary-heading"> <label className="summary-text">{headingText}</label> - {this.loading && <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />} + {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />} </div> ); render() { return ( <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}> - {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()} + {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.editBox()} </div> ); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 4fc31ffe3..1d1c34f4f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -425,8 +425,7 @@ export class PDFViewer extends React.Component<IViewerProps> { GPTPopup.Instance.setSidebarId('data_sidebar'); const anchor = this._getAnchor(undefined, false); if (anchor) { - console.log(anchor); - GPTPopup.Instance.setPdfAnchor(anchor); + GPTPopup.Instance.setTargetAnchor(anchor); } GPTPopup.Instance.addDoc = this.props.sidebarAddDoc; }; |
