aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/generativeFill/GenerativeFill.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/generativeFill/GenerativeFill.tsx')
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFill.tsx157
1 files changed, 125 insertions, 32 deletions
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index 6dd80a5d1..0df05f4d7 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -3,14 +3,21 @@ import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler';
import { BrushHandler } from './generativeFillUtils/BrushHandler';
import { IconButton, TextField } from '@mui/material';
import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces';
-import { activeColor, canvasSize, eraserColor } from './generativeFillUtils/generativeFillConstants';
+import { activeColor, canvasSize, eraserColor, freeformRenderSize } from './generativeFillUtils/generativeFillConstants';
import { PointerHandler } from './generativeFillUtils/PointerHandler';
-import { BsBrush, BsEraser } from 'react-icons/bs';
+import { BsBrush, BsEraser, BsX } from 'react-icons/bs';
import { AiOutlineUpload } from 'react-icons/ai';
import { CiUndo, CiRedo } from 'react-icons/ci';
import Buttons from './GenerativeFillButtons';
-import React from 'react';
+import React = require('react');
import './GenerativeFill.scss';
+import { EditableText } from 'browndash-components';
+import { MainView } from '../../MainView';
+import { Doc } from '../../../../fields/Doc';
+import { Networking } from '../../../Network';
+import { Utils } from '../../../../Utils';
+import { DocUtils, Docs } from '../../../documents/Documents';
+import { NumCast } from '../../../../fields/Types';
/**
* For images not 1024x1024 fill in the rest in solid black, or a
@@ -33,7 +40,14 @@ interface ImageEdit {
children: ImageEdit[];
}
-const GenerativeFill = () => {
+interface GenerativeFillProps {
+ imageEditorOpen: boolean;
+ imageEditorSource: string;
+ imageRootDoc: Doc | undefined;
+ addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined;
+}
+
+const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc }: GenerativeFillProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const canvasBackgroundRef = useRef<HTMLCanvasElement>(null);
const drawingAreaRef = useRef<HTMLDivElement>(null);
@@ -51,8 +65,49 @@ const GenerativeFill = () => {
const [loading, setLoading] = useState(false);
// used to store the current image loaded to the main canvas
const currImg = useRef<HTMLImageElement | null>(null);
- // ref to store history
- const undoStack = useRef<ImageData[]>([]);
+ const originalImg = useRef<HTMLImageElement | null>(null);
+ // stores history of data urls
+ const undoStack = useRef<string[]>([]);
+ // stores redo stack
+ const redoStack = useRef<string[]>([]);
+
+ // Undo and Redo
+ const handleUndo = () => {
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx || !currImg.current || !canvasRef.current) return;
+
+ const target = undoStack.current[undoStack.current.length - 1];
+ if (!target) {
+ ImageUtility.drawImgToCanvas(currImg.current, canvasRef);
+ } else {
+ redoStack.current = [...redoStack.current, canvasRef.current.toDataURL()];
+ const img = new Image();
+ img.src = target;
+ ImageUtility.drawImgToCanvas(img, canvasRef);
+ undoStack.current = undoStack.current.slice(0, -1);
+ }
+ };
+
+ const handleRedo = () => {
+ // TODO: handle undo as well
+ const target = redoStack.current[redoStack.current.length - 1];
+ if (!target) {
+ } else {
+ const img = new Image();
+ img.src = target;
+ ImageUtility.drawImgToCanvas(img, canvasRef);
+ redoStack.current = redoStack.current.slice(0, -1);
+ }
+ };
+
+ const handleReset = () => {
+ if (!canvasRef.current || !currImg.current) return;
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx) return;
+ ctx.clearRect(0, 0, canvasSize, canvasSize);
+ undoStack.current = [];
+ ImageUtility.drawImgToCanvas(currImg.current, canvasRef, true);
+ };
// initiate brushing
const handlePointerDown = (e: React.PointerEvent) => {
@@ -61,6 +116,9 @@ const GenerativeFill = () => {
const ctx = ImageUtility.getCanvasContext(canvasRef);
if (!ctx) return;
+ undoStack.current = [...undoStack.current, canvasRef.current.toDataURL()];
+ redoStack.current = [];
+
setIsBrushing(true);
const { x, y } = PointerHandler.getPointRelativeToElement(canvas, e, canvasScale);
@@ -98,17 +156,13 @@ const GenerativeFill = () => {
// first load
useEffect(() => {
+ if (!imageEditorSource || imageEditorSource === '') return;
const img = new Image();
- img.src = '/assets/art.jpeg';
+ img.src = imageEditorSource;
ImageUtility.drawImgToCanvas(img, canvasRef);
currImg.current = img;
- }, [canvasRef]);
-
- useEffect(() => {
- if (!canvasBackgroundRef.current) return;
- const ctx = ImageUtility.getCanvasContext(canvasBackgroundRef);
- if (!ctx) return;
- }, [canvasBackgroundRef]);
+ originalImg.current = img;
+ }, [canvasRef, imageEditorSource]);
// handles brush sizing
useEffect(() => {
@@ -180,16 +234,9 @@ const GenerativeFill = () => {
const maskBlob = await ImageUtility.canvasToBlob(canvas);
const imgBlob = await ImageUtility.canvasToBlob(ImageUtility.getCanvasImg(img));
- // const res = await ImageUtility.getEdit(
- // imgBlob,
- // maskBlob,
- // input !== ""
- // ? input + " in the same style"
- // : "Fill in the image in the same style",
- // 1
- // );
+ const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2);
- const res = await ImageUtility.mockGetEdit();
+ // const res = await ImageUtility.mockGetEdit();
const { urls } = res as APISuccess;
const image = new Image();
image.src = urls[0];
@@ -202,11 +249,47 @@ const GenerativeFill = () => {
}
};
+ const onSave = async () => {
+ if (!currImg.current || !imageRootDoc) return;
+ try {
+ const src = currImg.current.src;
+ console.log(src);
+ const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] });
+ const source = Utils.prepend(result.accessPaths.agnostic.client);
+ console.log(source);
+ const newImg = Docs.Create.ImageDocument(source, {
+ x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + 20,
+ y: NumCast(imageRootDoc.y),
+ _height: freeformRenderSize,
+ _width: freeformRenderSize,
+ data_nativeWidth: result.nativeWidth,
+ data_nativeHeight: result.nativeHeight,
+ });
+
+ addDoc?.(newImg);
+ // Create link between prompt and image
+ DocUtils.MakeLink(imageRootDoc, newImg, { link_relationship: 'Image Edit' });
+ console.log('done');
+ } catch (err) {
+ console.log(err);
+ }
+ };
+
return (
- <div className="generativeFillContainer">
- <div className="controls">
+ <div className="generativeFillContainer" style={{ display: imageEditorOpen ? 'flex' : 'none' }}>
+ <div className="generativeFillControls">
<h1>Generative Fill</h1>
- <Buttons canvasRef={canvasRef} backgroundref={canvasBackgroundRef} currImg={currImg} getEdit={getEdit} undoStack={undoStack} loading={loading} />
+ <div style={{ display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
+ <Buttons canvasRef={canvasRef} currImg={currImg} getEdit={getEdit} loading={loading} onSave={onSave} onReset={handleReset} />
+ <IconButton
+ onClick={() => {
+ MainView.Instance.setImageEditorOpen(false);
+ MainView.Instance.setImageEditorSource('');
+ setEdits([]);
+ }}>
+ <BsX color={activeColor} />
+ </IconButton>
+ </div>
</div>
{/* Main canvas for editing */}
<div
@@ -245,26 +328,33 @@ const GenerativeFill = () => {
}}>
<BsBrush color={brushStyle === BrushStyle.ADD ? activeColor : 'inherit'} />
</IconButton>
- <IconButton
+ {/* <IconButton
onClick={() => {
setBrushStyle(BrushStyle.SUBTRACT);
}}>
<BsEraser color={brushStyle === BrushStyle.SUBTRACT ? activeColor : 'inherit'} />
- </IconButton>
+ </IconButton> */}
{/* Undo and Redo */}
- {/* <IconButton
+ <IconButton
onPointerDown={e => {
e.stopPropagation();
- console.log(undoStack.current);
+ handleUndo();
}}
onPointerUp={e => {
e.stopPropagation();
}}>
<CiUndo />
</IconButton>
- <IconButton onClick={() => {}}>
+ <IconButton
+ onPointerDown={e => {
+ e.stopPropagation();
+ handleRedo();
+ }}
+ onPointerUp={e => {
+ e.stopPropagation();
+ }}>
<CiRedo />
- </IconButton> */}
+ </IconButton>
</div>
{/* Edits box */}
<div className="editsBox">
@@ -292,12 +382,15 @@ const GenerativeFill = () => {
type="text"
label="Prompt"
placeholder="Prompt..."
+ InputLabelProps={{ style: { fontSize: '1.5rem' } }}
+ inputProps={{ style: { fontSize: '1.5rem' } }}
sx={{
backgroundColor: '#ffffff',
position: 'absolute',
bottom: '1rem',
transform: 'translateX(calc(50vw - 50%))',
width: 'calc(100vw - 4rem)',
+ scale: 1.2,
}}
/>
</div>