diff options
author | eleanor-park <eleanor_park@brown.edu> | 2025-01-06 18:21:02 -0500 |
---|---|---|
committer | eleanor-park <eleanor_park@brown.edu> | 2025-01-06 18:21:02 -0500 |
commit | f273c97121300eb9b242f7272cc1cc56c396ca8b (patch) | |
tree | d0086e411c206ee86637ed38a5a6eacb4f67ef8f /src | |
parent | 1d62d867621b293c41ff8488ca5a3bd6010723d5 (diff) |
started a previous images display
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/ViewBoxInterface.ts | 3 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.scss | 17 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 62 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.scss | 28 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 43 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 35 |
6 files changed, 129 insertions, 59 deletions
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts index df08f2564..a66a20cf6 100644 --- a/src/client/views/ViewBoxInterface.ts +++ b/src/client/views/ViewBoxInterface.ts @@ -60,5 +60,6 @@ export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React search?: (str: string, bwd?: boolean, clear?: boolean) => boolean; dontRegisterView?: () => boolean; // KeyValueBox's don't want to register their views isUnstyledView?: () => boolean; // SchemaView and KeyValue are unstyled -- not titles, no opacity, no animations - componentAIView?: (top: number) => JSX.Element; + componentAIView?: () => JSX.Element; + componentAIViewHistory?: () => JSX.Element; } diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 7568e3b57..9490be98c 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -242,7 +242,7 @@ .contentFittingDocumentView * { ::-webkit-scrollbar-track { - background: none; + background: none; } } @@ -270,3 +270,18 @@ position: relative; } } + +.documentView-editorView-history { + position: absolute; + left: 0; + top: 0; +} + +.documentView-editorView { + width: 100%; + overflow-y: scroll; + + .documentView-editorView-resizer { + height: 5px; + } +} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ab58023e3..f79eadde8 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -723,31 +723,43 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString; const noBackground = this.Document.isGroup && !this._componentView?.isUnstyledView?.() && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent'); return ( - <div - className="documentView-contentsView" - style={{ - pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? (this._mounted ? 'all' : 'none'), - width: this._showAIEditor ? this.rpw() : undefined, - height: this._showAIEditor ? this.rph() : this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined, - justifySelf: 'center', - }}> - <DocumentContentsView - {...this._props} - layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')} - pointerEvents={this.contentPointerEvents} - setContentViewBox={this.setContentView} - childFilters={this.childFilters} - PanelHeight={this._showAIEditor ? this.rpw : this.panelHeight} - PanelWidth={this._showAIEditor ? this.rph : this._props.PanelWidth} - setHeight={this.setHeight} - isContentActive={this.isContentActive} - ScreenToLocalTransform={this.screenToLocalContent} - rootSelected={this.rootSelected} - onClickScript={this.onClickFunc} - setTitleFocus={this.setTitleFocus} - hideClickBehaviors={BoolCast(this.Document.hideClickBehaviors)} - /> - {this._showAIEditor && (this._componentView?.componentAIView?.(this.rph()) ?? null)} + <div> + <div + className="documentView-contentsView" + style={{ + pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? (this._mounted ? 'all' : 'none'), + width: this._showAIEditor ? this.rpw() : undefined, + height: this._showAIEditor ? this.rph() : this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined, + justifySelf: this._showAIEditor ? 'center' : undefined, + }}> + <DocumentContentsView + {...this._props} + layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')} + pointerEvents={this.contentPointerEvents} + setContentViewBox={this.setContentView} + childFilters={this.childFilters} + PanelHeight={this._showAIEditor ? this.rpw : this.panelHeight} + PanelWidth={this._showAIEditor ? this.rph : this._props.PanelWidth} + setHeight={this.setHeight} + isContentActive={this.isContentActive} + ScreenToLocalTransform={this.screenToLocalContent} + rootSelected={this.rootSelected} + onClickScript={this.onClickFunc} + setTitleFocus={this.setTitleFocus} + hideClickBehaviors={BoolCast(this.Document.hideClickBehaviors)} + /> + </div> + {this._showAIEditor && ( + <div className="documentView-editorView-history" style={{ height: this.rph(), width: this.rpw() / 2 }}> + {this._componentView?.componentAIViewHistory?.() ?? null} + </div> + )} + {this._showAIEditor && ( + <div className="documentView-editorView" style={{ height: this.panelHeight() - this.rph() }}> + <div className="documentView-editorView-resizer" /> + {this._componentView?.componentAIView?.() ?? null} + </div> + )} </div> ); } diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 03314e90f..9f1ac11ef 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -140,15 +140,29 @@ opacity: 0; } -.imageBox-aiView { +.imageBox-aiView-history { + display: flex; + flex-direction: column; + align-items: center; padding: 5px; - position: absolute; - overflow: scroll; + gap: 5px; + overflow-y: scroll; + + .imageBox-aiView-img { + width: 100%; + } +} + +.imageBox-aiView { + overflow-y: scroll; text-align: center; font-weight: bold; - margin-top: 5px; + align-content: center; + height: 100%; .imageBox-aiView-subtitle { + position: relative; + left: 5px; align-self: start; } @@ -156,6 +170,8 @@ .imageBox-aiView-options-container { font-weight: normal; text-align: start; + margin: 5px; + padding-left: 5px; } .imageBox-aiView-regenerate, @@ -166,4 +182,8 @@ flex-direction: row; gap: 5px; } + + .imageBox-aiView-input { + width: 50%; + } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index f00580d77..fc8b7bc27 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -7,8 +7,8 @@ import { observer } from 'mobx-react'; import { extname } from 'path'; import * as React from 'react'; import ReactLoading from 'react-loading'; -import { ClientUtils, DashColor, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils'; -import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { ClientUtils, DashColor, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils'; +import { Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; @@ -34,7 +34,7 @@ import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; import { StickerPalette } from '../smartdraw/StickerPalette'; import { StyleProp } from '../StyleProp'; -import { DocumentView } from './DocumentView'; +import { DocumentView, DocumentViewInternal } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; @@ -44,6 +44,7 @@ import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler'; import { Button } from 'browndash-components'; import { SettingsManager } from '../../util/SettingsManager'; import { AiOutlineSend } from 'react-icons/ai'; +import { returnEmptyDocViewList } from '../StyleProvider'; export class ImageEditorData { // eslint-disable-next-line no-use-before-define @@ -532,18 +533,30 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @observable private _regenInput = ''; @observable private _canInteract = true; @observable private _regenerateLoading = false; + @observable private _prevImgUrls: string[] = []; - componentAIView = (top: number) => { + componentAIViewHistory = () => { + return ( + <div className="imageBox-aiView-history"> + {this._prevImgUrls.map((url: string) => ( + <img className="imageBox-aiView-img" src={url} onClick={() => (this.dataDoc[this.fieldKey] = new ImageField(url))} /> + ))} + </div> + ); + }; + + componentAIView = () => { const field = Cast(this.dataDoc[this.fieldKey], ImageField); const showRegenerate = this.Document[DocData].ai; return ( - <div className="imageBox-aiView" style={{ top: top, width: NumCast(this.Document.width), height: NumCast(this.Document.width) - top }}> + <div className="imageBox-aiView"> Edit Image with AI {showRegenerate && ( <div className="imageBox-aiView-regenerate-container"> <text className="imageBox-aiView-subtitle">Regenerate AI Image</text> <div className="imageBox-aiView-regenerate"> <input + className="imageBox-aiView-input" aria-label="Edit instructions input" // className="smartdraw-input" type="text" @@ -556,14 +569,20 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { text="Regenerate" type={Type.SEC} // style={{ alignSelf: 'flex-end' }} - icon={this._regenerateLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} + icon={this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} iconPlacement="right" - onClick={action(async () => { - this._regenerateLoading = true; - await SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput); - this._regenerateLoading = false; - this._regenInput = ''; - })} + onClick={undoable( + action(async () => { + this._regenerateLoading = true; + SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true).then(newDocs => { + this._prevImgUrls.push(this.paths.lastElement()); + this.dataDoc[this.fieldKey] = new ImageField(newDocs[0]); + this._regenerateLoading = false; + this._regenInput = ''; + }); + }), + 'regenerate image' + )} /> <Button // style={{ alignSelf: 'flex-end' }} diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index 0c67c7a13..13a93367c 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -267,20 +267,23 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { /** * Calls Firefly API to create an image based on user input */ - createImageWithFirefly = (input: string, seed?: number) => { + createImageWithFirefly = (input: string, seed?: number, changeInPlace?: boolean) => { this._lastInput.text = input; const dims = FireflyDimensionsMap[this._imgDims]; return Networking.PostToServer('/queryFireflyImage', { prompt: input, width: dims.width, height: dims.height, seed: seed }).then(img => { - const imgDoc: Doc = Docs.Create.ImageDocument(img.accessPaths.agnostic.client, { - title: input.match(/^(.*?)~~~.*$/)?.[1] || input, - nativeWidth: dims.width, - nativeHeight: dims.height, - ai: 'firefly', - ai_firefly_seed: img.accessPaths.agnostic.client.match(/\/(\d+)upload/)[1], - ai_firefly_prompt: input, - }); - DocumentViewInternal.addDocTabFunc(imgDoc, OpenWhere.addRight); - this._selectedDocs.push(imgDoc); + if (!changeInPlace) { + const imgDoc: Doc = Docs.Create.ImageDocument(img.accessPaths.agnostic.client, { + title: input.match(/^(.*?)~~~.*$/)?.[1] || input, + nativeWidth: dims.width, + nativeHeight: dims.height, + ai: 'firefly', + ai_firefly_seed: img.accessPaths.agnostic.client.match(/\/(\d+)upload/)[1], + ai_firefly_prompt: input, + }); + DocumentViewInternal.addDocTabFunc(imgDoc, OpenWhere.addRight); + this._selectedDocs.push(imgDoc); + } + return img.accessPaths.agnostic.client; }); }; @@ -289,22 +292,22 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { * @param doc the drawing Docs to regenerate */ @action - regenerate = async (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string) => { + regenerate = async (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => { if (lastInput) this._lastInput = lastInput; if (lastResponse) this._lastResponse = lastResponse; if (regenInput) this._regenInput = regenInput; - await Promise.all( + return await Promise.all( drawingDocs.map(async doc => { const docData = doc[DocData]; if (docData.type == 'image') { const seed: number = docData?.ai_firefly_seed as number; if (this._regenInput !== '') { // if (this._selectedDoc) { - const newPrompt = `${docData.ai_firefly_prompt}, ${this._regenInput}`; - await this.createImageWithFirefly(newPrompt, seed); + const newPrompt = `${docData.ai_firefly_prompt} ~~~ ${this._regenInput}`; + return this.createImageWithFirefly(newPrompt, seed, changeInPlace); // } } else { - await this.createImageWithFirefly(this._lastInput.text || StrCast(docData.ai_firefly_prompt)); + return this.createImageWithFirefly(this._lastInput.text || StrCast(docData.ai_firefly_prompt), undefined, changeInPlace); } } if (docData.type == 'collection') { |