aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreleanor-park <eleanor_park@brown.edu>2025-01-06 18:21:02 -0500
committereleanor-park <eleanor_park@brown.edu>2025-01-06 18:21:02 -0500
commitf273c97121300eb9b242f7272cc1cc56c396ca8b (patch)
treed0086e411c206ee86637ed38a5a6eacb4f67ef8f
parent1d62d867621b293c41ff8488ca5a3bd6010723d5 (diff)
started a previous images display
-rw-r--r--src/client/views/ViewBoxInterface.ts3
-rw-r--r--src/client/views/nodes/DocumentView.scss17
-rw-r--r--src/client/views/nodes/DocumentView.tsx62
-rw-r--r--src/client/views/nodes/ImageBox.scss28
-rw-r--r--src/client/views/nodes/ImageBox.tsx43
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx35
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') {