aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/pdf')
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx21
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss52
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx129
-rw-r--r--src/client/views/pdf/PDFViewer.tsx3
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;
};