aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/nodes/ImageBox.tsx111
-rw-r--r--src/client/views/smartdraw/DrawingFillHandler.tsx1
-rw-r--r--src/server/ApiManagers/FireflyManager.ts363
3 files changed, 118 insertions, 357 deletions
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 114d5226b..a7dd5de15 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,7 +1,6 @@
import { Button, Colors, Size, Type } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Slider, Tooltip } from '@mui/material';
-import { DimensionField } from '../../../fields/DimensionField';
import axios from 'axios';
import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction } from 'mobx';
import { observer } from 'mobx-react';
@@ -359,32 +358,31 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
processOutpainting = async () => {
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (!field) return;
-
+
const origWidth = NumCast(this.Document._outpaintingOriginalWidth);
const origHeight = NumCast(this.Document._outpaintingOriginalHeight);
-
+
if (!origWidth || !origHeight) {
- console.error("Original dimensions (_outpaintingOriginalWidth/_outpaintingOriginalHeight) not set. Ensure resizeViewForOutpainting was called first.");
+ console.error('Original dimensions (_outpaintingOriginalWidth/_outpaintingOriginalHeight) not set. Ensure resizeViewForOutpainting was called first.');
return;
}
-
+
//alert(`Original dimensions: ${origWidth} x ${origHeight}`);
-
-
+
// Set flag that outpainting is in progress
this._outpaintingInProgress = true;
-
+
try {
// Get the current path to the image
const currentPath = this.choosePath(field.url);
-
+
// Get original and new dimensions for calculating mask
const newWidth = NumCast(this.Document._width);
const newHeight = NumCast(this.Document._height);
-
+
// Optional: Ask user for a prompt to guide the outpainting
- let prompt = "Extend this image naturally with matching content";
- const customPrompt = await new Promise<string>((resolve) => {
+ let prompt = 'Extend this image naturally with matching content';
+ const customPrompt = await new Promise<string>(resolve => {
const dialog = document.createElement('div');
Object.assign(dialog.style, {
position: 'fixed',
@@ -395,16 +393,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
padding: '20px',
borderRadius: '8px',
boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
- zIndex: '10000'
+ zIndex: '10000',
});
-
+
const title = document.createElement('h3');
title.style.marginTop = '0';
title.textContent = 'Outpaint Image';
-
+
const description = document.createElement('p');
description.textContent = 'Enter a prompt for extending the image:';
-
+
const input = document.createElement('input');
input.id = 'outpaint-prompt';
input.type = 'text';
@@ -412,50 +410,50 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
Object.assign(input.style, {
width: '300px',
padding: '8px',
- marginBottom: '10px'
+ marginBottom: '10px',
});
-
+
const buttonContainer = document.createElement('div');
Object.assign(buttonContainer.style, {
display: 'flex',
justifyContent: 'flex-end',
- gap: '10px'
+ gap: '10px',
});
-
+
const cancelButton = document.createElement('button');
cancelButton.textContent = 'Cancel';
-
+
const confirmButton = document.createElement('button');
confirmButton.textContent = 'Generate';
Object.assign(confirmButton.style, {
background: '#0078d4',
color: 'white',
border: 'none',
- padding: '8px 16px'
+ padding: '8px 16px',
});
-
+
buttonContainer.appendChild(cancelButton);
buttonContainer.appendChild(confirmButton);
-
+
dialog.appendChild(title);
dialog.appendChild(description);
dialog.appendChild(input);
dialog.appendChild(buttonContainer);
-
+
document.body.appendChild(dialog);
-
+
cancelButton.onclick = () => {
document.body.removeChild(dialog);
- resolve("");
+ resolve('');
};
-
+
confirmButton.onclick = () => {
const promptValue = input.value;
document.body.removeChild(dialog);
resolve(promptValue);
};
});
-
+
// If user cancelled, reset dimensions to original
if (!customPrompt) {
this.Document._width = origWidth;
@@ -463,7 +461,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._outpaintingInProgress = false;
return;
}
-
+
// Show loading indicator
const loadingOverlay = document.createElement('div');
loadingOverlay.style.position = 'absolute';
@@ -477,52 +475,50 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
loadingOverlay.style.alignItems = 'center';
loadingOverlay.innerHTML = '<div style="color: white; font-size: 16px;">Generating outpainted image...</div>';
this._mainCont?.appendChild(loadingOverlay);
-
+
// Call the outpaint API
- const response = await Networking.PostToServer('/outpaintImageFour', {
+ const response = await Networking.PostToServer('/outpaintImage', {
imageUrl: currentPath,
prompt: customPrompt,
originalDimensions: {
width: origWidth,
- height: origHeight
+ height: origHeight,
},
newDimensions: {
width: newWidth,
- height: newHeight
- }
+ height: newHeight,
+ },
});
if (response && typeof response === 'object' && 'url' in response && typeof response.url === 'string') {
- console.log("Response is valid and contains URL:", response.url);
-
+ console.log('Response is valid and contains URL:', response.url);
} else {
- console.error("Unexpected API response:", response);
- alert("Failed to receive a valid image URL from server.");
+ console.error('Unexpected API response:', response);
+ alert('Failed to receive a valid image URL from server.');
}
-
-
+
if (response && 'url' in response && typeof response.url === 'string') {
// Save the original image as an alternate
if (!this.dataDoc[this.fieldKey + '_alternates']) {
this.dataDoc[this.fieldKey + '_alternates'] = new List<Doc>();
}
-
+
// Create a copy of the current image as an alternate
const originalDoc = Docs.Create.ImageDocument(field.url.href, {
title: `Original: ${this.Document.title}`,
_nativeWidth: Doc.NativeWidth(this.dataDoc),
- _nativeHeight: Doc.NativeHeight(this.dataDoc)
+ _nativeHeight: Doc.NativeHeight(this.dataDoc),
});
-
+
// Add to alternates
Doc.AddDocToList(this.dataDoc, this.fieldKey + '_alternates', originalDoc);
-
+
// Update the image with the outpainted version
this.dataDoc[this.fieldKey] = new ImageField(response.url);
-
+
// Update native dimensions
Doc.SetNativeWidth(this.dataDoc, newWidth);
Doc.SetNativeHeight(this.dataDoc, newHeight);
-
+
// Add AI metadata
this.Document.ai = true;
this.Document.ai_outpainted = true;
@@ -531,18 +527,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// If failed, revert to original dimensions
this.Document._width = origWidth;
this.Document._height = origHeight;
- alert("Failed to outpaint image. Please try again.");
+ alert('Failed to outpaint image. Please try again.');
}
-
+
// Remove loading overlay
this._mainCont?.removeChild(loadingOverlay);
-
} catch (error) {
- console.error("Error during outpainting:", error);
+ console.error('Error during outpainting:', error);
// Revert to original dimensions on error
- this.Document._width = origWidth
- this.Document._height = origHeight
- alert("An error occurred while outpainting. Please try again.");
+ this.Document._width = origWidth;
+ this.Document._height = origHeight;
+ alert('An error occurred while outpainting. Please try again.');
} finally {
// Clear the outpainting flags
this._outpaintingInProgress = false;
@@ -608,12 +603,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
icon: this.Document.savedAsSticker ? 'clipboard-check' : 'file-arrow-down',
});
// Add new outpainting option
- funcs.push({
- description: 'Outpaint Image',
+ funcs.push({
+ description: 'Outpaint Image',
event: () => {
this.processOutpainting();
- },
- icon: 'brush'
+ },
+ icon: 'brush',
});
// Add outpainting history option if the image was outpainted
this.Document.ai_outpainted &&
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index c672bc718..37799a98d 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -48,6 +48,7 @@ export class DrawingFillHandler {
.then(res => {
const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { _width: 400, _height: 400 });
drawing[DocData].ai_firefly_generatedDocs = genratedDocs;
+ if ('error' in res) throw new Error(res.error as string);
(res as Upload.ImageInformation[]).map(info =>
Doc.AddDocToList(
genratedDocs,
diff --git a/src/server/ApiManagers/FireflyManager.ts b/src/server/ApiManagers/FireflyManager.ts
index 1e0fdb595..8b0fa6ec7 100644
--- a/src/server/ApiManagers/FireflyManager.ts
+++ b/src/server/ApiManagers/FireflyManager.ts
@@ -8,7 +8,7 @@ import { DashUploadUtils } from '../DashUploadUtils';
import { _error, _invalid, _success, Method } from '../RouteManager';
import { Directory, filesDirectory } from '../SocketData';
import ApiManager, { Registration } from './ApiManager';
-import { Upload } from '../SharedMediaTypes';
+import { Upload } from '../SharedMediaTypes';
export default class FireflyManager extends ApiManager {
getBearerToken = () =>
@@ -332,315 +332,80 @@ export default class FireflyManager extends ApiManager {
register({
method: Method.POST,
- subscription: '/outpaintImageFour',
+ subscription: '/outpaintImage',
secureHandler: ({ req, res }) =>
-
- this.uploadImageToDropbox(req.body.imageUrl, req.user as DashUserModel)
- .then(uploadUrl => {
- if (uploadUrl instanceof Error) {
- _invalid(res, uploadUrl.message);
- return;
- }
- return this.getBearerToken()
- .then(tokenResponse => tokenResponse?.json())
- .then((tokenData: { access_token: string }) =>
- fetch('https://firefly-api.adobe.io/v3/images/expand', {
- method: 'POST',
- headers: {
- //'Content-Type': 'application/json',
- 'Accept': 'application/json',
- 'x-api-key': process.env._CLIENT_FIREFLY_CLIENT_ID ?? '',
- 'Authorization': `Bearer ${tokenData.access_token}`,
+ this.uploadImageToDropbox(req.body.imageUrl, req.user as DashUserModel).then(uploadUrl => {
+ if (uploadUrl instanceof Error) {
+ _invalid(res, uploadUrl.message);
+ return;
+ }
+ return this.getBearerToken()
+ .then(tokenResponse => tokenResponse?.json())
+ .then((tokenData: { access_token: string }) =>
+ fetch('https://firefly-api.adobe.io/v3/images/expand', {
+ method: 'POST',
+ headers: [
+ ['Content-Type', 'application/json'],
+ ['Accept', 'application/json'],
+ ['x-api-key', process.env._CLIENT_FIREFLY_CLIENT_ID ?? ''],
+ ['Authorization', `Bearer ${tokenData.access_token}`],
+ ],
+ body: JSON.stringify({
+ image: {
+ source: { url: uploadUrl },
},
- body: JSON.stringify({
- image: {
- source: { url: uploadUrl },
- },
- size: {
- width: req.body.newDimensions.width,
- height: req.body.newDimensions.height,
+ // mask: {
+ // source: { url: uploadUrl },
+ // },
+ size: {
+ width: Math.round(req.body.newDimensions.width),
+ height: Math.round(req.body.newDimensions.height),
+ },
+ prompt: req.body.prompt ?? '',
+ numVariations: 1,
+ placement: {
+ inset: {
+ left: 0,
+ top: 0,
+ right: Math.round(req.body.newDimensions.width - req.body.originalDimensions.width),
+ bottom: Math.round(req.body.newDimensions.height - req.body.originalDimensions.height),
},
- prompt: req.body.prompt ?? '',
- numVariations: 1,
- placement: {
- inset: {
- left: 0,
- top: 0,
- right: req.body.newDimensions.width - req.body.originalDimensions.width,
- bottom: req.body.newDimensions.height - req.body.originalDimensions.height,
- },
- alignment: {
- horizontal: 'center',
- vertical: 'center',
- },
+ alignment: {
+ horizontal: 'center',
+ vertical: 'center',
},
- }),
- })
- )
- .then(expandResp => expandResp?.json())
- .then(expandData => {
- if (expandData.error_code || !expandData.outputs?.[0]?.image?.url) {
- console.error('Firefly validation error:', expandData);
- _error(res, expandData.message ?? 'Failed to generate image');
- } else {
- DashUploadUtils.UploadImage(expandData.outputs[0].image.url)
- .then((info: Upload.ImageInformation | Error) => {
- if (info instanceof Error) {
- _invalid(res, info.message);
- } else {
- _success(res, { url: info.accessPaths.agnostic.client });
- }
- })
- .catch(uploadErr => {
- console.error('DashUpload Error:', uploadErr);
- _error(res, 'Failed to upload generated image.');
- });
- }
+ },
+ }),
})
- .catch(err => {
- console.error('Firefly request error:', err);
- _error(res, 'Failed to expand image');
- });
- }),
- });
- register({
- method: Method.POST,
- subscription: '/outpaintImageThree',
- secureHandler: ({ req, res }) =>
- new Promise<void>(resolver => {
- this.uploadImageToDropbox(req.body.imageUrl, req.user as DashUserModel)
- .then(dropboxImgUrl => {
- if (dropboxImgUrl instanceof Error) {
- _invalid(res, dropboxImgUrl.message);
- throw new Error('Error uploading image to dropbox');
+ )
+ .then(expandResp => expandResp?.json())
+ .then(expandData => {
+ if (expandData.error_code || !expandData.outputs?.[0]?.image?.url) {
+ console.error('Firefly validation error:', expandData);
+ _error(res, expandData.message ?? 'Failed to generate image');
+ } else {
+ return DashUploadUtils.UploadImage(expandData.outputs[0].image.url)
+ .then((info: Upload.ImageInformation | Error) => {
+ if (info instanceof Error) {
+ _invalid(res, info.message);
+ } else {
+ _success(res, { url: info.accessPaths.agnostic.client });
+ }
+ })
+ .catch(uploadErr => {
+ console.error('DashUpload Error:', uploadErr);
+ _error(res, 'Failed to upload generated image.');
+ });
}
- return dropboxImgUrl;
})
- .then(dropboxImgUrl =>
- this.getBearerToken().then(tokenResponse =>
- tokenResponse?.json().then((tokenData: { access_token: string }) =>
- fetch('https://firefly-api.adobe.io/v3/images/expand', {
- method: 'POST',
- headers: [
- ['Content-Type', 'application/json'],
- ['Accept', 'application/json'],
- ['x-api-key', process.env._CLIENT_FIREFLY_CLIENT_ID ?? ''],
- ['Authorization', `Bearer ${tokenData.access_token}`],
- ],
- body: JSON.stringify({
- image: {
- source: {
- url: dropboxImgUrl,
- },
- },
- numVariations: 1,
- prompt: req.body.prompt,
- size: {
- width: req.body.newDimensions.width,
- height: req.body.newDimensions.height,
- },
- placement: {
- inset: {
- left: 0,
- top: 0,
- right: req.body.newDimensions.width - req.body.originalDimensions.width,
- bottom: req.body.newDimensions.height - req.body.originalDimensions.height,
- },
- alignment: {
- horizontal: 'center',
- vertical: 'center',
- },
- },
- }),
- })
- .then(resp => resp.json())
- .then(expandData => {
- if (expandData.error_code || !expandData.outputs?.[0]?.image?.url) {
- _invalid(res, expandData.message ?? 'Failed to expand image');
- resolver();
- } else {
- DashUploadUtils.UploadImage(expandData.outputs[0].image.url)
- .then(info => {
- if (info instanceof Error) {
- _invalid(res, info.message);
- } else {
- _success(res, { url: info.accessPaths.agnostic.client });
- }
- })
- .then(resolver)
- .catch(uploadErr => {
- console.error('DashUpload Error:', uploadErr);
- _invalid(res, 'Failed to upload generated image.');
- resolver();
- });
- }
- })
- .catch(err => {
- console.error('Firefly API Error:', err);
- _error(res, 'Failed to expand image');
- resolver();
- })
- )
- )
- )
.catch(err => {
- console.error('Dropbox Error:', err);
- _error(res, 'Failed to upload image to Dropbox');
- resolver();
+ console.error('Firefly request error:', err);
+ _error(res, 'Failed to expand image');
});
}),
});
-
-
- register({
- method: Method.POST,
- subscription: '/outpaintImageTwo',
- secureHandler: async ({ req, res }) => {
- try {
- const uploadUrl = await this.uploadImageToDropbox(req.body.imageUrl, req.user as DashUserModel);
-
- if (uploadUrl instanceof Error) {
- _invalid(res, uploadUrl.message);
- return;
- }
-
- const tokenResponse = await this.getBearerToken();
- const tokenData = await tokenResponse?.json();
-
- const body = JSON.stringify({
- image: { source: { url: uploadUrl } },
- prompt: req.body.prompt,
- numVariations: 1,
- size: {
- width: req.body.newDimensions.width,
- height: req.body.newDimensions.height,
- },
- placement: {
- inset: {
- left: 0,
- top: 0,
- right: req.body.newDimensions.width - req.body.originalDimensions.width,
- bottom: req.body.newDimensions.height - req.body.originalDimensions.height,
- },
- alignment: {
- horizontal: 'center',
- vertical: 'center',
- },
- },
- });
-
- console.log('Sending outpainting request:', body);
-
- const expandResp = await fetch('https://firefly-api.adobe.io/v3/images/expand', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'application/json',
- 'x-api-key': process.env._CLIENT_FIREFLY_CLIENT_ID ?? '',
- Authorization: `Bearer ${tokenData.access_token}`,
- },
- body,
- });
-
- const expandData = await expandResp.json();
- console.log('Received expandData:', expandData);
-
- if (expandData.error_code || !expandData.outputs?.[0]?.image?.url) {
- console.error('Expand API Error:', expandData);
- _error(res, expandData.message ?? 'Failed to generate image');
- return;
- }
-
- const uploadedInfo = await DashUploadUtils.UploadImage(expandData.outputs[0].image.url);
-
- if (uploadedInfo instanceof Error) {
- console.error('Upload Error:', uploadedInfo.message);
- _invalid(res, uploadedInfo.message);
- return;
- }
-
- console.log('Successfully uploaded image URL:', uploadedInfo.accessPaths.agnostic.client);
- _success(res, { url: uploadedInfo.accessPaths.agnostic.client });
- } catch (err) {
- console.error('Unexpected error during outpainting:', err);
- _error(res, 'Unexpected error during outpainting');
- }
- },
- });
-
- register({
-
-
- method: Method.POST,
- subscription: '/outpaintImage',
- secureHandler: ({ req, res }) =>
- this.uploadImageToDropbox(req.body.imageUrl, req.user as DashUserModel).then(uploadUrl =>
- uploadUrl instanceof Error
- ? _invalid(res, uploadUrl.message)
- : this.getBearerToken()
- .then(tokenResponse => tokenResponse?.json())
- .then((tokenData: { access_token: string }) =>
- fetch('https://firefly-api.adobe.io/v3/images/expand', {
- method: 'POST',
- headers: [
- ['Content-Type', 'application/json'],
- ['Accept', 'application/json'],
- ['x-api-key', process.env._CLIENT_FIREFLY_CLIENT_ID ?? ''],
- ['Authorization', `Bearer ${tokenData.access_token}`],
- ],
- body: JSON.stringify({
- image: { source: { url: uploadUrl } },
- numVariations: 1,
- size: {
- width: req.body.newDimensions.width,
- height: req.body.newDimensions.height,
- },
- prompt: req.body.prompt,
- placement: {
- inset: {
- left: 0,
- top: 0,
- right: 0, //req.body.newDimensions.width - req.body.originalDimensions.width,
- bottom: 0 //req.body.newDimensions.height - req.body.originalDimensions.height,
- },
- alignment: {
- horizontal: 'center',
- vertical: 'center',
- },
- },
- }),
- })
- )
- .then(expandResp => expandResp.json())
- .then(expandData => {
- if (expandData.error_code || !expandData.outputs?.[0]?.image?.url) {
- _error(res, expandData.message ?? "Failed to generate image");
- } else {
- DashUploadUtils.UploadImage(expandData.outputs[0].image.url)
- .then((info: Upload.ImageInformation | Error) => {
- if (info instanceof Error) {
- _invalid(res, info.message);
- } else {
- // THIS IS CRUCIAL: Respond exactly how your front end expects
- console.log("Successfully uploaded image. URL:", info.accessPaths.agnostic.client);
- _success(res, { url: info.accessPaths.agnostic.client });
- }
- })
- .catch(uploadErr => {
-
- console.error('DashUpload Error:', uploadErr);
- _error(res, 'Failed to upload generated image.');
- });
- }
- })
- .catch(err => {
- console.error(err);
- _error(res, 'Failed to expand image');
- })
- ),
- });
- /* register({
+ /* register({
method: Method.POST
subscription: '/queryFireflyOutpaint',
secureHandler: ({req, res}) =>
@@ -683,7 +448,7 @@ export default class FireflyManager extends ApiManager {
: this.expandImage(uploadUrl, req.body.prompt).then(text => {
if (text.error_code) _error(res, text.message);
else
- DashUploadUtils.UploadImage(text.outputs[0].image.url).then(info => {
+ return DashUploadUtils.UploadImage(text.outputs[0].image.url).then(info => {
if (info instanceof Error) _invalid(res, info.message);
else _success(res, info);
});