aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ImageBox.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-05-02 13:09:24 -0400
committerbobzel <zzzman@gmail.com>2025-05-02 13:09:24 -0400
commitd4659e2bd3ddb947683948083232c26fb1227f39 (patch)
tree360d62ca4fa46954d4264e735dcb8c44332bfb50 /src/client/views/nodes/ImageBox.tsx
parent57827157823219ffb71047fd65217ebbd2420a9d (diff)
fixed up image outpaint dialog and added options for placementt.
Diffstat (limited to 'src/client/views/nodes/ImageBox.tsx')
-rw-r--r--src/client/views/nodes/ImageBox.tsx156
1 files changed, 127 insertions, 29 deletions
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 31a135fa7..d16baada6 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,4 +1,4 @@
-import { Button, Colors, EditableText, Size, Type } from '@dash/components';
+import { Button, Colors, EditableText, IconButton, Size, Toggle, ToggleType, Type } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Slider, Tooltip } from '@mui/material';
import axios from 'axios';
@@ -353,6 +353,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
openOutpaintPrompt = () => {
+ this._outpaintVAlign = '';
+ this._outpaintAlign = '';
this._showOutpaintPrompt = true;
};
@@ -362,6 +364,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
@action
+ cancelOutpaintPrompt = () => {
+ const origWidth = NumCast(this.Document[this.fieldKey + '_outpaintOriginalWidth']);
+ const origHeight = NumCast(this.Document[this.fieldKey + '_outpaintOriginalHeight']);
+ this.Document._width = origWidth;
+ this.Document._height = origHeight;
+ this._outpaintingInProgress = false;
+ this.closeOutpaintPrompt();
+ };
+
+ @action
handlePromptChange = (val: string | number) => {
this._outpaintPromptInput = '' + val;
};
@@ -377,17 +389,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (!field) return;
- const origWidth = NumCast(this.Document[this.fieldKey + '_outpaintOriginalWidth']);
- const origHeight = NumCast(this.Document[this.fieldKey + '_outpaintOriginalHeight']);
-
// Set flag that outpainting is in progress
this._outpaintingInProgress = true;
// Revert dimensions if prompt is blank (acts like Cancel)
if (!customPrompt) {
- this.Document._width = origWidth;
- this.Document._height = origHeight;
- this._outpaintingInProgress = false;
+ this.cancelOutpaintPrompt();
return;
}
@@ -410,11 +417,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
loadingOverlay.innerHTML = '<div style="color: white; font-size: 16px;">Generating outpainted image...</div>';
this._mainCont?.appendChild(loadingOverlay);
+ const origWidth = NumCast(this.Document[this.fieldKey + '_outpaintOriginalWidth']);
+ const origHeight = NumCast(this.Document[this.fieldKey + '_outpaintOriginalHeight']);
const response = await Networking.PostToServer('/outpaintImage', {
imageUrl: currentPath,
prompt: customPrompt,
originalDimensions: { width: Math.min(newWidth, origWidth), height: Math.min(newHeight, origHeight) },
newDimensions: { width: newWidth, height: newHeight },
+ halignment: this._outpaintAlign,
+ valignment: this._outpaintVAlign,
});
const error = ('error' in response && (response.error as string)) || '';
@@ -447,8 +458,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.Document[this.fieldKey + '_outpaintOriginalWidth'] = undefined;
this.Document[this.fieldKey + '_outpaintOriginalHeight'] = undefined;
} else {
- this.Document._width = origWidth;
- this.Document._height = origHeight;
+ this.cancelOutpaintPrompt();
alert('Failed to receive a valid image URL from server.');
}
batch.end();
@@ -456,30 +466,106 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._mainCont?.removeChild(loadingOverlay);
} catch (error) {
- console.error('Error during outpainting:', error);
- this.Document._width = origWidth;
- this.Document._height = origHeight;
- alert('An error occurred while outpainting. Please try again.');
+ this.cancelOutpaintPrompt();
+ alert('An error occurred while outpainting.' + error);
} finally {
runInAction(() => (this._outpaintingInProgress = false));
}
};
- componentUI = () =>
+ @observable _outpaintAlign = '';
+ @observable _outpaintVAlign = '';
+ @computed get outpaintVertical() {
+ return this._props.PanelWidth() / this._props.PanelHeight() < this.nativeSize.nativeWidth / this.nativeSize.nativeHeight;
+ }
+
+ componentUI = (/* boundsLeft: number, boundsTop: number*/) =>
!this._showOutpaintPrompt ? null : (
- <div key="imageBox-componentui" className="imageBox-regenerate-dialog" style={{ backgroundColor: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}>
- <h3>Outpaint Image</h3>
- <EditableText
- placeholder="Enter a prompt for extending the image:"
- setVal={val => this.handlePromptChange(val)}
- val={this._outpaintPromptInput}
- type={Type.TERT}
- color={SettingsManager.userColor}
- background={SettingsManager.userBackgroundColor}
- />
- <div className="buttons">
- <Button text="Cancel" type={Type.TERT} onClick={this.closeOutpaintPrompt} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} />
- <Button text="Generate" type={Type.TERT} onClick={this.submitOutpaintPrompt} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} />
+ <div
+ key="imageBox-componentui"
+ className="imageBox-regenerate-dialog"
+ style={{
+ top: -70 + (this._props.DocumentView?.().getBounds?.top ?? 0),
+ left: this._props.DocumentView?.().getBounds?.left ?? 0,
+ backgroundColor: SettingsManager.userBackgroundColor,
+ color: SettingsManager.userColor,
+ }}>
+ <div style={{ position: 'absolute', top: 5, right: 5 }}>
+ <IconButton type={Type.TERT} onClick={this.cancelOutpaintPrompt} icon={<FontAwesomeIcon icon="times" color={'red'} />} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} />
+ </div>
+ <div>Outpaint Image</div>
+ <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
+ <EditableText
+ placeholder="Enter a prompt for extending the image:"
+ setVal={val => this.handlePromptChange(val)}
+ val={this._outpaintPromptInput}
+ type={Type.TERT}
+ color={SettingsManager.userColor}
+ background={SettingsManager.userBackgroundColor}
+ />
+ <div className="buttons" style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
+ <IconButton type={Type.TERT} onClick={this.submitOutpaintPrompt} icon={<AiOutlineSend />} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} />
+ <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
+ {this.outpaintVertical ? null : (
+ <Toggle
+ type={Type.TERT}
+ toggleType={ToggleType.BUTTON}
+ toggleStatus={this._outpaintAlign === 'left' && this._outpaintVAlign === ''}
+ onClick={action(() => (this._outpaintAlign = 'left'))}
+ icon={<FontAwesomeIcon icon="chevron-left" color={SnappingManager.userColor} />}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userVariantColor}
+ />
+ )}
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
+ {!this.outpaintVertical ? null : (
+ <Toggle
+ type={Type.TERT}
+ toggleType={ToggleType.BUTTON}
+ toggleStatus={this._outpaintAlign === '' && this._outpaintVAlign === 'top'}
+ onClick={action(() => (this._outpaintVAlign = 'top'))}
+ icon={<FontAwesomeIcon icon="chevron-up" color={SnappingManager.userColor} />}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userColor}
+ />
+ )}
+ <Toggle
+ type={Type.TERT}
+ toggleType={ToggleType.BUTTON}
+ toggleStatus={this._outpaintAlign === '' && this._outpaintVAlign === ''}
+ onClick={action(() => {
+ this._outpaintAlign = '';
+ this._outpaintVAlign = '';
+ })}
+ icon={<FontAwesomeIcon icon="bullseye" color={SnappingManager.userColor} />}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userColor}
+ />
+ {!this.outpaintVertical ? null : (
+ <Toggle
+ type={Type.TERT}
+ toggleType={ToggleType.BUTTON}
+ toggleStatus={this._outpaintAlign === '' && this._outpaintVAlign === 'bottom'}
+ onClick={action(() => (this._outpaintVAlign = 'bottom'))}
+ icon={<FontAwesomeIcon icon="chevron-down" color={SnappingManager.userColor} />}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userColor}
+ />
+ )}
+ </div>
+ {this.outpaintVertical ? null : (
+ <Toggle
+ type={Type.TERT}
+ toggleType={ToggleType.BUTTON}
+ toggleStatus={this._outpaintAlign === 'right' && this._outpaintVAlign === ''}
+ onClick={action(() => (this._outpaintAlign = 'right'))}
+ icon={<FontAwesomeIcon icon="chevron-right" color={SnappingManager.userColor} />}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userColor}
+ />
+ )}
+ </div>
+ </div>
</div>
</div>
);
@@ -718,13 +804,25 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
})}
key={this.layoutDoc[Id]}
onPointerDown={this.marqueeDown}>
- <div className="imageBox-fader" style={{ opacity: backAlpha }}>
+ <div
+ className="imageBox-fader"
+ style={{
+ opacity: backAlpha,
+ flexDirection: this._outpaintVAlign ? 'row' : 'column',
+ alignItems: this._outpaintAlign === 'left' || this._outpaintVAlign === 'top' ? 'flex-start' : this._outpaintAlign === 'right' || this._outpaintVAlign === 'bottom' ? 'flex-end' : undefined,
+ }}>
<img
alt=""
ref={action((r: HTMLImageElement | null) => (this.imageRef = r))}
key="paths"
src={srcpath}
- style={{ transform, transformOrigin, height: this.Document[this.fieldKey + '_outpaintOriginalWidth'] !== undefined ? '100%' : undefined }}
+ style={{
+ position: 'relative',
+ transform,
+ transformOrigin,
+ width: this._outpaintAlign ? 'max-content' : this._outpaintAlign ? '100%' : undefined,
+ height: this._outpaintVAlign ? 'max-content' : this.Document[this.fieldKey + '_outpaintOriginalWidth'] !== undefined ? '100%' : undefined,
+ }}
onError={action(e => (this._error = e.toString()))}
draggable={false}
width={nativeWidth}