diff options
| author | Sophie Zhang <sophie_zhang@brown.edu> | 2023-08-17 16:14:53 -0400 |
|---|---|---|
| committer | Sophie Zhang <sophie_zhang@brown.edu> | 2023-08-17 16:14:53 -0400 |
| commit | d3ecace5f03233e5d5ab2354c3f482418287ca9a (patch) | |
| tree | 5d065abd9151d2588f14da387f0b8074afb70d0f /src/client/views/nodes | |
| parent | 7fe84ba95c30a082f5146e684d0c31b61ec676df (diff) | |
broken version
Diffstat (limited to 'src/client/views/nodes')
5 files changed, 145 insertions, 86 deletions
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"> |
