aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsharkiecodes <lanyi_stroud@brown.edu>2025-04-09 23:58:46 -0400
committersharkiecodes <lanyi_stroud@brown.edu>2025-04-09 23:58:46 -0400
commit22a40443193320487c27ce02bd3f134d13cb7d65 (patch)
tree22955d6cfc88e55b8843409094cc14cdf28647d1 /src
parentd0b117d1f31f911d46dffe69e2f5c40b2bc5de3e (diff)
cleaned up code
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/ImageBox.scss32
-rw-r--r--src/client/views/nodes/ImageBox.tsx494
2 files changed, 351 insertions, 175 deletions
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 3d6942e6f..d7a14a9df 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -245,3 +245,35 @@
color: black;
}
}
+.imageBox-regenerate-dialog {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: white;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
+ z-index: 10000;
+
+ h3 { margin-top: 0; }
+
+ input {
+ width: 300px;
+ padding: 8px;
+ margin-bottom: 10px;
+ }
+
+ .buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+
+ .generate-btn {
+ background: #0078d4;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ }
+ }
+ } \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 14adfaf1e..033b0b5a2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -353,197 +353,340 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
});
- // Add this method to process outpainting when resize is complete
- @action
- processOutpainting = async () => {
- const field = Cast(this.dataDoc[this.fieldKey], ImageField);
- if (!field) return;
+ @observable _showOutpaintPrompt: boolean = false;
+@observable _outpaintPromptInput: string = 'Extend this image naturally with matching content';
+
+@action
+openOutpaintPrompt = () => {
+ this._showOutpaintPrompt = true;
+};
+
+@action
+closeOutpaintPrompt = () => {
+ this._showOutpaintPrompt = false;
+};
+
+@action
+handlePromptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._outpaintPromptInput = e.target.value;
+};
+
+@action
+submitOutpaintPrompt = () => {
+ this.closeOutpaintPrompt();
+ this.processOutpaintingWithPrompt(this._outpaintPromptInput);
+};
+
+@action
+processOutpaintingWithPrompt = async (customPrompt: string) => {
+ 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.');
+ return;
+ }
- const origWidth = NumCast(this.Document._outpaintingOriginalWidth);
- const origHeight = NumCast(this.Document._outpaintingOriginalHeight);
+ // Set flag that outpainting is in progress
+ this._outpaintingInProgress = true;
- if (!origWidth || !origHeight) {
- console.error('Original dimensions (_outpaintingOriginalWidth/_outpaintingOriginalHeight) not set. Ensure resizeViewForOutpainting was called first.');
+ try {
+ const currentPath = this.choosePath(field.url);
+ const newWidth = NumCast(this.Document._width);
+ const newHeight = NumCast(this.Document._height);
+
+ // Revert dimensions if prompt is blank (acts like Cancel)
+ if (!customPrompt) {
+ this.Document._width = origWidth;
+ this.Document._height = origHeight;
+ this._outpaintingInProgress = false;
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 => {
- const dialog = document.createElement('div');
- Object.assign(dialog.style, {
- position: 'fixed',
- top: '50%',
- left: '50%',
- transform: 'translate(-50%, -50%)',
- background: 'white',
- padding: '20px',
- borderRadius: '8px',
- boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
- 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';
- input.value = 'Extend this image naturally with matching content';
- Object.assign(input.style, {
- width: '300px',
- padding: '8px',
- marginBottom: '10px',
- });
-
- const buttonContainer = document.createElement('div');
- Object.assign(buttonContainer.style, {
- display: 'flex',
- justifyContent: 'flex-end',
- 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',
- });
-
- 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('');
- };
+ // Optional: add loading indicator
+ const loadingOverlay = document.createElement('div');
+ loadingOverlay.style.position = 'absolute';
+ loadingOverlay.style.top = '0';
+ loadingOverlay.style.left = '0';
+ loadingOverlay.style.width = '100%';
+ loadingOverlay.style.height = '100%';
+ loadingOverlay.style.background = 'rgba(0,0,0,0.5)';
+ loadingOverlay.style.display = 'flex';
+ loadingOverlay.style.justifyContent = 'center';
+ loadingOverlay.style.alignItems = 'center';
+ loadingOverlay.innerHTML = '<div style="color: white; font-size: 16px;">Generating outpainted image...</div>';
+ this._mainCont?.appendChild(loadingOverlay);
+
+ const response = await Networking.PostToServer('/outpaintImage', {
+ imageUrl: currentPath,
+ prompt: customPrompt,
+ originalDimensions: { width: origWidth, height: origHeight },
+ newDimensions: { width: newWidth, height: newHeight },
+ });
- confirmButton.onclick = () => {
- const promptValue = input.value;
- document.body.removeChild(dialog);
- resolve(promptValue);
- };
- });
+ if (response && typeof response === 'object' && 'url' in response && typeof response.url === 'string') {
+ console.log('Received outpainted image:', response.url);
- // If user cancelled, reset dimensions to original
- if (!customPrompt) {
- this.Document._width = origWidth;
- this.Document._height = origHeight;
- this._outpaintingInProgress = false;
- return;
+ if (!this.dataDoc[this.fieldKey + '_alternates']) {
+ this.dataDoc[this.fieldKey + '_alternates'] = new List<Doc>();
}
- // Show loading indicator
- const loadingOverlay = document.createElement('div');
- loadingOverlay.style.position = 'absolute';
- loadingOverlay.style.top = '0';
- loadingOverlay.style.left = '0';
- loadingOverlay.style.width = '100%';
- loadingOverlay.style.height = '100%';
- loadingOverlay.style.background = 'rgba(0,0,0,0.5)';
- loadingOverlay.style.display = 'flex';
- loadingOverlay.style.justifyContent = 'center';
- 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('/outpaintImage', {
- imageUrl: currentPath,
- prompt: customPrompt,
- originalDimensions: {
- width: origWidth,
- height: origHeight,
- },
- newDimensions: {
- width: newWidth,
- height: newHeight,
- },
+ const originalDoc = Docs.Create.ImageDocument(field.url.href, {
+ title: `Original: ${this.Document.title}`,
+ _nativeWidth: Doc.NativeWidth(this.dataDoc),
+ _nativeHeight: Doc.NativeHeight(this.dataDoc),
});
- if (response && typeof response === 'object' && 'url' in response && typeof response.url === 'string') {
- 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.');
- }
-
- 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),
- });
-
- // 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);
+ Doc.AddDocToList(this.dataDoc, this.fieldKey + '_alternates', originalDoc);
- // Update native dimensions
- Doc.SetNativeWidth(this.dataDoc, newWidth);
- Doc.SetNativeHeight(this.dataDoc, newHeight);
+ // Replace with new outpainted image
+ this.dataDoc[this.fieldKey] = new ImageField(response.url);
- // Add AI metadata
- this.Document.ai = true;
- this.Document.ai_outpainted = true;
- this.Document.ai_outpaint_prompt = customPrompt;
- } else {
- // If failed, revert to original dimensions
- this.Document._width = origWidth;
- this.Document._height = origHeight;
- alert('Failed to outpaint image. Please try again.');
- }
+ Doc.SetNativeWidth(this.dataDoc, newWidth);
+ Doc.SetNativeHeight(this.dataDoc, newHeight);
- // Remove loading overlay
- this._mainCont?.removeChild(loadingOverlay);
- } catch (error) {
- console.error('Error during outpainting:', error);
- // Revert to original dimensions on error
+ this.Document.ai = true;
+ this.Document.ai_outpainted = true;
+ this.Document.ai_outpaint_prompt = customPrompt;
+ } else {
+ console.error('Unexpected API response:', response);
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;
- delete this.Document._originalDims;
+ alert('Failed to receive a valid image URL from server.');
}
- };
+
+ 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.');
+ } finally {
+ this._outpaintingInProgress = false;
+ delete this.Document._originalDims;
+ }
+};
+
+processOutpainting = () => {
+ this.openOutpaintPrompt();
+};
+
+componentUI = () => (
+ <>
+ {this._showOutpaintPrompt && (
+ <div className="imageBox-regenerate-dialog">
+ <h3>Outpaint Image</h3>
+ <p>Enter a prompt for extending the image:</p>
+ <input
+ type="text"
+ value={this._outpaintPromptInput}
+ onChange={this.handlePromptChange}
+ />
+ <div className="buttons">
+ <button onClick={this.closeOutpaintPrompt}>Cancel</button>
+ <button className="generate-btn" onClick={this.submitOutpaintPrompt}>
+ Generate
+ </button>
+ </div>
+ </div>
+ )}
+ </>
+);
+
+ // // Add this method to process outpainting when resize is complete
+ // @action
+ // 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.');
+ // 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 => {
+ // const dialog = document.createElement('div');
+ // Object.assign(dialog.style, {
+ // position: 'fixed',
+ // top: '50%',
+ // left: '50%',
+ // transform: 'translate(-50%, -50%)',
+ // background: 'white',
+ // padding: '20px',
+ // borderRadius: '8px',
+ // boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
+ // 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';
+ // input.value = 'Extend this image naturally with matching content';
+ // Object.assign(input.style, {
+ // width: '300px',
+ // padding: '8px',
+ // marginBottom: '10px',
+ // });
+
+ // const buttonContainer = document.createElement('div');
+ // Object.assign(buttonContainer.style, {
+ // display: 'flex',
+ // justifyContent: 'flex-end',
+ // 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',
+ // });
+
+ // 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('');
+ // };
+
+ // 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;
+ // this.Document._height = origHeight;
+ // this._outpaintingInProgress = false;
+ // return;
+ // }
+
+ // // Show loading indicator
+ // const loadingOverlay = document.createElement('div');
+ // loadingOverlay.style.position = 'absolute';
+ // loadingOverlay.style.top = '0';
+ // loadingOverlay.style.left = '0';
+ // loadingOverlay.style.width = '100%';
+ // loadingOverlay.style.height = '100%';
+ // loadingOverlay.style.background = 'rgba(0,0,0,0.5)';
+ // loadingOverlay.style.display = 'flex';
+ // loadingOverlay.style.justifyContent = 'center';
+ // 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('/outpaintImage', {
+ // imageUrl: currentPath,
+ // prompt: customPrompt,
+ // originalDimensions: {
+ // width: origWidth,
+ // height: origHeight,
+ // },
+ // newDimensions: {
+ // width: newWidth,
+ // height: newHeight,
+ // },
+ // });
+ // if (response && typeof response === 'object' && 'url' in response && typeof response.url === 'string') {
+ // 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.');
+ // }
+
+ // 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),
+ // });
+
+ // // 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;
+ // this.Document.ai_outpaint_prompt = customPrompt;
+ // } else {
+ // // If failed, revert to original dimensions
+ // this.Document._width = origWidth;
+ // this.Document._height = origHeight;
+ // alert('Failed to outpaint image. Please try again.');
+ // }
+
+ // // Remove loading overlay
+ // this._mainCont?.removeChild(loadingOverlay);
+ // } catch (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.');
+ // } finally {
+ // // Clear the outpainting flags
+ // this._outpaintingInProgress = false;
+ // delete this.Document._originalDims;
+ // }
+ // };
specificContextMenu = (): void => {
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
@@ -586,10 +729,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
funcs.push({
description: 'Outpaint Image',
event: () => {
- this.processOutpainting();
+ this.openOutpaintPrompt();
},
icon: 'brush',
});
+
// Add outpainting history option if the image was outpainted
this.Document.ai_outpainted &&
funcs.push({