aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/CurrentUserUtils.ts2
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx1
-rw-r--r--src/client/views/global/globalScripts.ts8
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx28
-rw-r--r--src/client/views/nodes/ImageBox.tsx6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx76
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFill.tsx115
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx6
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx1
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss2
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx99
-rw-r--r--src/client/views/pdf/PDFViewer.tsx4
13 files changed, 210 insertions, 140 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c9a5175bb..6592ecffc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -633,6 +633,8 @@ export class CurrentUserUtils {
{ title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "View All", icon: "object-group", toolTip: "Fit all Docs to View", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ // want the same style as toggle button, but don't want it to act as an actual toggle, so set disableToggle to true,
+ { title: "Fit All", icon: "arrows-left-right", toolTip: "Fit all Docs to View (persistent)", btnType: ButtonType.IconClickButton, ignoreClick: false, expertMode: false, toolType:"viewAllPersist", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "Arrange",icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 60eb64caa..8fba9b886 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -80,7 +80,6 @@ export class MainView extends React.Component {
@observable public imageEditorSource: string = '';
@action public setImageEditorSource = (source: string) => (this.imageEditorSource = source);
@observable public imageRootDoc: Doc | undefined;
- @action public setImageRootDoc = (doc: Doc) => (this.imageRootDoc = doc);
@observable public addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined;
@observable public LastButton: Opt<Doc>;
@@ -350,6 +349,7 @@ export class MainView extends React.Component {
fa.faMousePointer,
fa.faMusic,
fa.faObjectGroup,
+ fa.faArrowsLeftRight,
fa.faPause,
fa.faPen,
fa.faPenNib,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 29122cb91..f353dfab7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1599,6 +1599,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
() => this.rootDoc.fitContentOnce,
fitContentOnce => {
if (fitContentOnce) this.fitContentOnce();
+ this.rootDoc.fitContentOnce = false;
},
{ fireImmediately: true, name: 'fitContent' }
);
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 256377758..38bf1042d 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -85,10 +85,10 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
});
-ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
+ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'viewAllPersist', checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
// prettier-ignore
- const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+ const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll' | 'viewAllPersist', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
['grid', {
checkResult: (doc:Doc) => BoolCast(doc._freeform_backgroundGrid, false),
setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
@@ -101,6 +101,10 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snapli
checkResult: (doc:Doc) => BoolCast(doc._freeform_fitContentsToBox, false),
setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox,
}],
+ ['viewAllPersist', {
+ checkResult: (doc:Doc) => false,
+ setDoc: (doc:Doc) => doc.fitContentOnce = true
+ }],
['clusters', {
waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
checkResult: (doc:Doc) => BoolCast(doc._freeform_useClusters, false),
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 91eac675f..90ec212fc 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -29,6 +29,7 @@ export enum ButtonType {
DropdownList = 'dropdownList',
DropdownButton = 'dropdownBtn',
ClickButton = 'clickBtn',
+ IconClickButton = 'iconClickBtn',
ToggleButton = 'toggleBtn',
ColorButton = 'colorBtn',
ToolButton = 'toolBtn',
@@ -346,6 +347,32 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
);
}
+ @computed get iconClickButton() {
+ // Determine the type of toggle button
+ const buttonText: string = StrCast(this.rootDoc.buttonText);
+ const tooltip: string = StrCast(this.rootDoc.toolTip);
+
+ const script = ScriptCast(this.rootDoc.onClick);
+ const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
+ // Colors
+ const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+ const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+
+ return (
+ <Toggle
+ tooltip={`${tooltip}`}
+ toggleType={ToggleType.BUTTON}
+ type={Type.PRIM}
+ toggleStatus={toggleStatus}
+ text={buttonText}
+ color={color}
+ icon={this.Icon(color)!}
+ label={this.label}
+ onPointerDown={() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })}
+ />
+ );
+ }
+
/**
* Default
*/
@@ -393,6 +420,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
case ButtonType.DropdownButton: return this.dropdownButton;
case ButtonType.MultiToggleButton: return this.multiToggleButton;
case ButtonType.ToggleButton: return this.toggleButton;
+ case ButtonType.IconClickButton: return this.iconClickButton;
case ButtonType.ClickButton:
case ButtonType.ToolButton: return <IconButton {...btnProps} size={Size.LARGE} color={color} />;
case ButtonType.TextButton: return <Button {...btnProps} text={StrCast(this.rootDoc.buttonText)}/>;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4ce359f3f..352e0fbdc 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -252,8 +252,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
funcs.push({
description: 'Open Image Editor',
event: action(() => {
- MainView.Instance.setImageEditorOpen(true);
- MainView.Instance.setImageEditorSource(this.choosePath(field.url));
+ MainView.Instance.imageEditorOpen = true;
+ MainView.Instance.imageEditorSource = this.choosePath(field.url);
MainView.Instance.addDoc = this.props.addDocument;
MainView.Instance.imageRootDoc = this.rootDoc;
}),
@@ -306,7 +306,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
choosePath(url: URL) {
const lower = url.href.toLowerCase();
if (url.protocol === 'data') return url.href;
- if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf("dashblobstore") === -1) return Utils.CorsProxy(url.href);
+ if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return Utils.CorsProxy(url.href);
if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`;
const ext = extname(url.href);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 332f0f467..5acc45d02 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -946,44 +946,46 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text);
GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
GPTPopup.Instance?.setImgTargetDoc(this.rootDoc);
- GPTPopup.Instance.setImgUrls([]);
- GPTPopup.Instance.setMode(GPTPopupMode.IMAGE);
- GPTPopup.Instance.setVisible(true);
+ // GPTPopup.Instance.setImgUrls([]);
+ // GPTPopup.Instance.setMode(GPTPopupMode.IMAGE);
+ // GPTPopup.Instance.setVisible(true);
GPTPopup.Instance.addToCollection = this.props.addDocument;
- GPTPopup.Instance.setLoading(true);
-
- try {
- // make this support multiple images
- let image_urls = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
- console.log(image_urls);
- if (image_urls) {
- const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
- const source = Utils.prepend(result.accessPaths.agnostic.client);
- GPTPopup.Instance.setImgUrls([source]);
-
- // const newDoc = Docs.Create.ImageDocument(source, {
- // x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
- // y: NumCast(this.rootDoc.y),
- // _height: 200,
- // _width: 200,
- // data_nativeWidth: result.nativeWidth,
- // data_nativeHeight: result.nativeHeight,
- // });
- // if (Doc.IsInMyOverlay(this.rootDoc)) {
- // newDoc.overlayX = this.rootDoc.x;
- // newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
- // Doc.AddToMyOverlay(newDoc);
- // } else {
- // this.props.addDocument?.(newDoc);
- // }
- // // Create link between prompt and image
- // DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
- }
- } catch (err) {
- console.log(err);
- return '';
- }
- GPTPopup.Instance.setLoading(false);
+ // GPTPopup.Instance.setLoading(true);
+ GPTPopup.Instance.setImgDesc((this.dataDoc.text as RichTextField)?.Text);
+ GPTPopup.Instance.generateImage();
+
+ // try {
+ // // make this support multiple images
+ // let image_urls = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
+ // console.log(image_urls);
+ // if (image_urls) {
+ // const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
+ // const source = Utils.prepend(result.accessPaths.agnostic.client);
+ // GPTPopup.Instance.setImgUrls([source]);
+
+ // // const newDoc = Docs.Create.ImageDocument(source, {
+ // // x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
+ // // y: NumCast(this.rootDoc.y),
+ // // _height: 200,
+ // // _width: 200,
+ // // data_nativeWidth: result.nativeWidth,
+ // // data_nativeHeight: result.nativeHeight,
+ // // });
+ // // if (Doc.IsInMyOverlay(this.rootDoc)) {
+ // // newDoc.overlayX = this.rootDoc.x;
+ // // newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
+ // // Doc.AddToMyOverlay(newDoc);
+ // // } else {
+ // // this.props.addDocument?.(newDoc);
+ // // }
+ // // // Create link between prompt and image
+ // // DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
+ // }
+ // } catch (err) {
+ // console.log(err);
+ // return '';
+ // }
+ // GPTPopup.Instance.setLoading(false);
};
breakupDictation = () => {
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index f8f9fe077..99068d647 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -3,7 +3,7 @@ import React = require('react');
import { useEffect, useRef, useState } from 'react';
import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler';
import { BrushHandler } from './generativeFillUtils/BrushHandler';
-import { Box, IconButton, Slider, TextField } from '@mui/material';
+import { Box, Checkbox, FormControlLabel, IconButton, Slider, TextField } from '@mui/material';
import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces';
import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants';
import { PointerHandler } from './generativeFillUtils/PointerHandler';
@@ -19,6 +19,7 @@ import { Cast, DocCast, NumCast } from '../../../../fields/Types';
import { CollectionDockingView } from '../../collections/CollectionDockingView';
import { OpenWhere, OpenWhereMod } from '../DocumentView';
import { Oval } from 'react-loader-spinner';
+import { CheckBox } from '../../search/CheckBox';
/**
* For images not 1024x1024 fill in the rest in solid black, or a
@@ -72,6 +73,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const [saveLoading, setSaveLoading] = useState(false);
+ const [isNewCollection, setIsNewCollection] = useState(false);
// the current image in the main canvas
const currImg = useRef<HTMLImageElement | null>(null);
// the unedited version of each generation (parent)
@@ -81,9 +83,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
// stores redo stack
const redoStack = useRef<string[]>([]);
- // early stage properly, likely will get rid of
- const freeformPosition = useRef<number[]>([0, 0]);
-
// which urls were already saved to canvas
const savedSrcs = useRef<Set<string>>(new Set());
@@ -91,7 +90,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
const newCollectionRef = useRef<Doc | null>(null);
const parentDoc = useRef<Doc | null>(null);
const childrenDocs = useRef<Doc[]>([]);
- const addToExistingCollection = useRef<boolean>(false);
// Undo and Redo
const handleUndo = () => {
@@ -111,10 +109,13 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
};
const handleRedo = () => {
- // TODO: handle undo as well
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx || !currImg.current || !canvasRef.current) return;
+
const target = redoStack.current[redoStack.current.length - 1];
if (!target) {
} else {
+ undoStack.current = [...undoStack.current, canvasRef.current?.toDataURL()];
const img = new Image();
img.src = target;
ImageUtility.drawImgToCanvas(img, canvasRef);
@@ -188,7 +189,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
ImageUtility.drawImgToCanvas(img, canvasRef);
currImg.current = img;
originalImg.current = img;
- freeformPosition.current = [0, 0];
return () => {
console.log('cleanup');
@@ -197,7 +197,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
childrenDocs.current = [];
currImg.current = null;
originalImg.current = null;
- freeformPosition.current = [0, 0];
undoStack.current = [];
redoStack.current = [];
};
@@ -266,24 +265,25 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
// create first image
if (!newCollectionRef.current) {
- if (addToExistingCollection.current) {
+ if (!isNewCollection) {
+ // newcollection should stay null
+ } else {
+ if (!(originalImg.current && imageRootDoc)) return;
+ // create new collection and add it to the view
+ newCollectionRef.current = Docs.Create.FreeformDocument([], {
+ x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX,
+ y: NumCast(imageRootDoc.y),
+ _width: newCollectionSize,
+ _height: newCollectionSize,
+ title: 'Image edit collection',
+ });
+ DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false });
+ // add the doc to the main freeform
+ addDoc?.(newCollectionRef.current);
+ await createNewImgDoc(originalImg.current, true);
}
- if (!(originalImg.current && imageRootDoc)) return;
- console.log('creating first image');
- // create new collection and add it to the view
- newCollectionRef.current = Docs.Create.FreeformDocument([], {
- x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX,
- y: NumCast(imageRootDoc.y),
- _width: newCollectionSize,
- _height: newCollectionSize,
- title: 'Image edit collection',
- });
- DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false });
- // add the doc to the main freeform
- // addDoc?.(newCollectionRef.current);
- await createNewImgDoc(originalImg.current, true);
} else {
- parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1];
+ // parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1];
childrenDocs.current = [];
}
@@ -292,12 +292,24 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
const { urls } = res as APISuccess;
const image = new Image();
image.src = urls[0];
+
+ // need to crop images agh
+ // save to dash
+
+ // const imageRes = await Promise.all(
+ // urls.map(async url => {
+ // const newImg = new Image();
+ // newImg.src = url;
+ // await onSave(newImg);
+ // })
+ // );
+
+ // map each url to [url, imgDoc]
setEdits(urls);
+
ImageUtility.drawImgToCanvas(image, canvasRef);
currImg.current = image;
- onSave();
- freeformPosition.current[0] += 1;
- freeformPosition.current[1] = 0;
+ // onSave(currImg.current);
} catch (err) {
console.log(err);
}
@@ -324,7 +336,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
// creates a new image document and returns its reference
const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean): Promise<Doc | undefined> => {
- if (!newCollectionRef.current || !imageRootDoc) return;
+ if (!imageRootDoc) return;
const src = img.src;
const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] });
const source = Utils.prepend(result.accessPaths.agnostic.client);
@@ -332,7 +344,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
if (firstDoc) {
const x = 0;
const initialY = 0;
- console.log('first doc');
const newImg = Docs.Create.ImageDocument(source, {
x: x,
@@ -343,16 +354,19 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
data_nativeHeight: result.nativeHeight,
});
- Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
+ if (isNewCollection && newCollectionRef.current) {
+ Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
+ } else {
+ addDoc?.(newImg);
+ }
+
parentDoc.current = newImg;
return newImg;
} else {
if (!parentDoc.current) return;
const x = NumCast(parentDoc.current.x) + freeformRenderSize + offsetX;
// dummy position
- console.log('creating child elements');
const initialY = 0;
-
const newImg = Docs.Create.ImageDocument(source, {
x: x,
y: initialY,
@@ -363,21 +377,26 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
});
childrenDocs.current.push(newImg);
+
+ if (isNewCollection && newCollectionRef.current) {
+ Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
+ } else {
+ addDoc?.(newImg);
+ }
+
DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: 'Image Edit', link_displayLine: true });
adjustImgPositions();
-
- Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
return newImg;
}
};
// need to maybe call on every img click, not just when the save btn is clicked
- const onSave = async () => {
+ const onSave = async (img: HTMLImageElement) => {
setSaveLoading(true);
- if (!currImg.current || !originalImg.current || !imageRootDoc) return;
+ // if (!currImg.current || !originalImg.current || !imageRootDoc) return;
try {
console.log('creating another image');
- await createNewImgDoc(currImg.current, false);
+ await createNewImgDoc(img, false);
} catch (err) {
console.log(err);
}
@@ -387,7 +406,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
const handleViewClose = () => {
if (newCollectionRef.current) {
newCollectionRef.current.fitContentOnce = true;
- CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
+ // CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
}
MainView.Instance.setImageEditorOpen(false);
MainView.Instance.setImageEditorSource('');
@@ -399,7 +418,20 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
<div className="generativeFillControls">
<h1>AI Image Editor</h1>
<div style={{ display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
- <Buttons canvasRef={canvasRef} currImg={currImg} getEdit={getEdit} loading={loading} onSave={onSave} onReset={handleReset} />
+ <FormControlLabel
+ control={
+ <Checkbox
+ checked={isNewCollection}
+ onChange={e => {
+ setIsNewCollection(prev => !prev);
+ }}
+ />
+ }
+ label={'Create New Collection'}
+ labelPlacement="end"
+ sx={{ whiteSpace: 'nowrap' }}
+ />
+ <Buttons canvasRef={canvasRef} currImg={currImg} getEdit={getEdit} loading={loading} onReset={handleReset} />
<IconButton onClick={handleViewClose}>
<BsX color={activeColor} />
</IconButton>
@@ -491,13 +523,14 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
height={100}
src={edit}
onClick={async () => {
- // if (savedSrcs.current.has(edit)) return;
const img = new Image();
img.src = edit;
ImageUtility.drawImgToCanvas(img, canvasRef);
currImg.current = img;
savedSrcs.current.add(edit);
- await onSave();
+ undoStack.current = [];
+ redoStack.current = [];
+ await onSave(img);
}}
/>
))}
diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
index 53c6cec84..b4d56b408 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
@@ -11,14 +11,10 @@ interface ButtonContainerProps {
currImg: React.MutableRefObject<HTMLImageElement | null>;
getEdit: () => Promise<void>;
loading: boolean;
- onSave: () => Promise<void>;
onReset: () => void;
}
-const Buttons = ({ canvasRef, currImg, loading, getEdit, onSave, onReset }: ButtonContainerProps) => {
- const handleSave = () => {
- onSave();
- };
+const Buttons = ({ canvasRef, currImg, loading, getEdit, onReset }: ButtonContainerProps) => {
return (
<div className="generativeFillBtnContainer">
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 7404650d6..1be8fe6ab 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -130,6 +130,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
* @param e pointer down event
*/
gptSummarize = async (e: React.PointerEvent) => {
+ // move this logic to gptpopup, need to implement generate again
GPTPopup.Instance.setVisible(true);
this.setHighlightRange(undefined);
GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 478b7d4ba..5d966395c 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -123,9 +123,11 @@ $highlightedText: #82e0ff;
cursor: pointer;
.img-container {
+ pointer-events: none;
position: relative;
img {
+ pointer-events: all;
position: relative;
}
}
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index fc6fc1af8..943c38d42 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -10,6 +10,11 @@ import { DocUtils, Docs } from '../../../documents/Documents';
import { Button, IconButton, Type } from 'browndash-components';
import { NumCast, StrCast } from '../../../../fields/Types';
import { CgClose } from 'react-icons/cg';
+import { AnchorMenu } from '../AnchorMenu';
+import { gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { Networking } from '../../../Network';
+import { Utils } from '../../../../Utils';
export enum GPTPopupMode {
SUMMARY,
@@ -43,10 +48,17 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
};
@observable
- public imageUrls: string[] = [];
+ public imgDesc: string = '';
@action
- public setImgUrls = (imgs: string[]) => {
- this.imageUrls = imgs;
+ public setImgDesc = (text: string) => {
+ this.imgDesc = text;
+ };
+
+ @observable
+ public imgUrls: string[][] = [];
+ @action
+ public setImgUrls = (imgs: string[][]) => {
+ this.imgUrls = imgs;
};
@observable
@@ -102,6 +114,30 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
+ generateImage = async () => {
+ if (this.imgDesc === '') return;
+ this.setImgUrls([]);
+ this.setMode(GPTPopupMode.IMAGE);
+ this.setVisible(true);
+ this.setLoading(true);
+
+ try {
+ // make this support multiple images
+ let image_urls = await gptImageCall(this.imgDesc);
+ console.log(image_urls);
+ if (image_urls && image_urls[0]) {
+ const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
+ const source = Utils.prepend(result.accessPaths.agnostic.client);
+ console.log('Source', source);
+ this.setImgUrls([[image_urls[0], source]]);
+ }
+ } catch (err) {
+ console.log(err);
+ return '';
+ }
+ GPTPopup.Instance.setLoading(false);
+ };
+
/**
* Transfers the summarization text to a sidebar annotation text document.
*/
@@ -113,8 +149,9 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
_layout_autoHeight: true,
});
this.addDoc(newDoc, this.sidebarId);
- if (this.targetAnchor) {
- DocUtils.MakeLink(newDoc, this.targetAnchor, {
+ const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
+ if (anchor) {
+ DocUtils.MakeLink(newDoc, anchor, {
link_relationship: 'GPT Summary',
});
}
@@ -163,17 +200,22 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
{this.heading('GENERATED IMAGE')}
<div className="image-content-wrapper">
- {this.imageUrls.map(rawSrc => (
+ {this.imgUrls.map(rawSrc => (
<div className="img-wrapper">
<div className="img-container">
- <img key={rawSrc} src={this.getPreviewUrl(rawSrc)} width={150} height={150} alt="dalle generation" />
+ <img key={rawSrc[0]} src={rawSrc[0]} 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} />
+ <Button text="Save Image" onClick={() => this.transferToImage(rawSrc[1])} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
</div>
</div>
))}
</div>
+ {!this.loading && (
+ <>
+ <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+ </>
+ )}
</div>
);
};
@@ -227,43 +269,6 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
</>
);
- editBox = () => {
- const hr = this.highlightRange;
- return (
- <>
- <div>
- {this.heading('TEXT EDIT SUGGESTIONS')}
- <div className="content-wrapper">
- {hr && (
- <div>
- {this.text.slice(0, hr[0])} <span className="highlighted-text">{this.text.slice(hr[0], hr[1])}</span> {this.text.slice(hr[1])}
- </div>
- )}
- </div>
- </div>
- {hr && !this.loading && (
- <>
- <div className="btns-wrapper">
- <>
- <button className="icon-btn" onPointerDown={this.callEditApi}>
- <FontAwesomeIcon icon="redo-alt" size="lg" />
- </button>
- <button
- className="text-btn"
- onClick={e => {
- this.replaceText(this.text);
- }}>
- Replace Text
- </button>
- </>
- </div>
- {this.aiWarning()}
- </>
- )}
- </>
- );
- };
-
aiWarning = () =>
this.done ? (
<div className="ai-warning">
@@ -277,14 +282,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} /> : <IconButton color={StrCast(Doc.UserDoc().userColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
+ {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userVariantColor)} 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.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.editBox()}
+ {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : <></>}
</div>
);
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c9fee4813..1319a236d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -424,10 +424,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
// Changing which document to add the annotation to (the currently selected PDF)
GPTPopup.Instance.setSidebarId('data_sidebar');
- const anchor = this._getAnchor(undefined, false);
- if (anchor) {
- GPTPopup.Instance.setTargetAnchor(anchor);
- }
GPTPopup.Instance.addDoc = this.props.sidebarAddDoc;
};