From 64a9a1a982ec3f11adfed68cc0f18eb61059aff8 Mon Sep 17 00:00:00 2001 From: sharkiecodes Date: Sun, 11 May 2025 22:58:07 -0400 Subject: integrated outpainting with scrapbooks --- src/client/views/DocumentDecorations.tsx | 7 +- src/client/views/nodes/ImageBox.tsx | 1 - src/client/views/nodes/scrapbook/ScrapbookBox.tsx | 136 ++++++++++++++++++---- 3 files changed, 118 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index ab665e984..94e5e662c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -36,6 +36,7 @@ import { ImageBox } from './nodes/ImageBox'; import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { TagsView } from './TagsView'; +import { ScrapbookBox } from './nodes/scrapbook/ScrapbookBox'; interface DocumentDecorationsProps { PanelWidth: number; @@ -446,7 +447,7 @@ export class DocumentDecorations extends ObservableReactComponent { SnappingManager.SetIsResizing(DocumentView.Selected().lastElement()?.Document[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them DocumentView.Selected() - .filter(dv => e.shiftKey && dv.ComponentView instanceof ImageBox) + .filter(dv => e.shiftKey && dv.ComponentView instanceof ImageBox || dv.ComponentView instanceof ScrapbookBox) .forEach(dv => { dv.Document[dv.ComponentView!.fieldKey + '_outpaintOriginalWidth'] = NumCast(dv.Document._width); dv.Document[dv.ComponentView!.fieldKey + '_outpaintOriginalHeight'] = NumCast(dv.Document._height); @@ -502,7 +503,7 @@ export class DocumentDecorations extends ObservableReactComponent dv.ComponentView instanceof ImageBox) : []; + const outpainted = e.shiftKey ? DocumentView.Selected().filter(dv => dv.ComponentView instanceof ImageBox || dv.ComponentView instanceof ScrapbookBox) : []; const notOutpainted = e.shiftKey ? DocumentView.Selected().filter(dv => !outpainted.includes(dv)) : DocumentView.Selected(); // Special handling for shift-drag resize (outpainting of Images by resizing without scaling content - fill in with firefly GAI) @@ -765,7 +766,7 @@ export class DocumentDecorations extends ObservableReactComponent() { - state = { - loading: false, - src: '', - }; - - @observable createdDate: string; + @observable loading = false; + @observable src = ''; + @observable imgDoc: Doc | undefined; + private _disposers: { [name: string]: IReactionDisposer } = {}; + private imageBoxRef = React.createRef(); + // @observable configs : ScrapbookItemConfig[] constructor(props: FieldViewProps) { @@ -337,7 +342,7 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent() } } - + async generateAiImage() { this.setState({ loading: true }); @@ -365,10 +370,98 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent() componentDidMount() { //this.initScrapbook(ScrapbookPresetType.Default); this.setTitle(); - this.generateAiImage(); + this.generateAiImageCorrect(); + + this._disposers.propagateResize = reaction( + () => ({ w: this.layoutDoc._width, h: this.layoutDoc._height }), + (dims, prev) => { + // prev is undefined on the first run, so bail early + if (!prev || !SnappingManager.ShiftKey || !this.imgDoc) return; + + // either guard the ref… + const imageBox = this.imageBoxRef.current; + if (!imageBox) return; + + // …or just hard-code the fieldKey if you know it’s always `"data"` + const key = imageBox.props.fieldKey; + + runInAction(() => { + if(!this.imgDoc){ + return + } + // use prev.w/h (the *old* size) as your orig dims + this.imgDoc[key + '_outpaintOriginalWidth'] = prev.w; + this.imgDoc[key + '_outpaintOriginalHeight'] = prev.h; + ;(this.imageBoxRef.current as any).layoutDoc._width = dims.w + ;(this.imageBoxRef.current as any).layoutDoc._height = dims.h + + // tell the imageDoc to resize itself to the *new* scrapbook size + //this.imgDoc._width = dims.w; + //this.imgDoc._height = dims.h; + //Doc.SetNativeWidth(this.imgDoc, dims.w); + //Doc.SetNativeHeight(this.imgDoc, dims.h); + }); + } + ); + /* + this._disposers.propagateResize = reaction( + () => ({ w: this.layoutDoc._width, h: this.layoutDoc._height }), + ({ w, h }, prev) => { + // only when shift is held (i.e. outpaint mode) + if (SnappingManager.ShiftKey && this.imgDoc) { + const key = this.imageBoxRef.current!.props.fieldKey; // “data” + // record original size on the *image* doc: + this.imgDoc[key + '_outpaintOriginalWidth'] = this.imgDoc._width; + this.imgDoc[key + '_outpaintOriginalHeight'] = this.imgDoc._height; + } + } + );*/ + + + // this._disposers.outpaint = reaction( + // () => this.imgDoc?.[this.imgDoc.fieldKey + '_outpaintOriginalWidth'], + // originalWidth => { + // if (originalWidth !== undefined && !SnappingManager.ShiftKey) { + // this.imageBoxRef.current?.openOutpaintPrompt(); // ✅ CORRECT! + // } + // } + // ); } + async generateAiImageCorrect() { + this.loading = true; + + const prompt = 'A serene mountain landscape at sunrise, ultra-wide, pastel sky, abstract, scrapbook background'; + const dimensions = FireflyImageDimensions.Square; + + SmartDrawHandler.CreateWithFirefly(prompt, dimensions) + .then(action(doc => { + if (doc instanceof Doc) { + const imgField = ImageCast(doc.data); + if (imgField?.url.href) { + this.src = imgField.url.href; + const url = new ImageField(this.src); + this.imgDoc = Docs.Create.ImageDocument(url, { title: 'Generated Background', _width: 1792, _height: 2304, + _nativeWidth: 1792, _nativeHeight: 1792 + }, ); + } else { + alert('Image URL missing.'); + this.src = ''; + } + + } else { + alert('Failed to generate document.'); + } + })) + .catch(e => { + alert(`Generation error: ${e}`); + }) + .finally(action(() => { + this.loading = false; + })); + } + childRejectDrop = (de: DragManager.DropEvent, subView?: DocumentView) => { return true; // disable dropping documents onto any child of the scrapbook. }; @@ -446,18 +539,18 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent() }; render() { - const { loading, src } = this.state; + return (
- {loading && ( + {this.loading && (
)} - {loading && ( + {this.loading && (
@@ -465,15 +558,14 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent()
)} {/* Render AI-generated background */} - {src && ( - - )} - - + )}