diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 111 | ||||
-rw-r--r-- | src/client/views/smartdraw/DrawingFillHandler.tsx | 1 | ||||
-rw-r--r-- | src/server/ApiManagers/FireflyManager.ts | 363 |
3 files changed, 112 insertions, 363 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 61bb65cc4..77655bb23 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,318 +332,71 @@ export default class FireflyManager extends ApiManager { register({ method: Method.POST, - subscription: '/outpaintImageFour', - 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}`, - }, - body: JSON.stringify({ - image: { - source: { url: uploadUrl }, - }, - size: { - width: req.body.newDimensions.width, - height: req.body.newDimensions.height, - }, - prompt: req.body.prompt ?? '', - numVariations: 1, - mask: { - source: {url: uploadUrl}, - }, - 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(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'); - } - 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(); - }); - }), - }); - - - 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'); - }) + .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 }, + }, + 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), + }, + 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 { + 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.'); + }); + } + }) + ) ), }); - /* register({ + /* register({ method: Method.POST subscription: '/queryFireflyOutpaint', secureHandler: ({req, res}) => @@ -686,7 +439,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); }); |