diff options
author | bobzel <zzzman@gmail.com> | 2025-02-28 14:52:39 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2025-02-28 14:52:39 -0500 |
commit | 532f0fa22281fef1e35e3d0a6064ee57e4673253 (patch) | |
tree | f9c8c9db02bdc5de70467226681784482d1e978d /src | |
parent | 2838b8d98ab88df974fd52ba96cf5046d99298cb (diff) |
added drop target for regenerating images
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DragManager.ts | 3 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.scss | 8 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 90 | ||||
-rw-r--r-- | src/client/views/smartdraw/DrawingFillHandler.tsx | 23 |
4 files changed, 83 insertions, 41 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 81ea840f1..6c8179c82 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -555,9 +555,12 @@ export namespace DragManager { let scrollAwaiter: Opt<NodeJS.Timeout>; let startWindowDragTimer: NodeJS.Timeout | undefined; + let startCanEmbed = SnappingManager.CanEmbed; const moveHandler = (e: PointerEvent) => { e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop if (docDragData) { + if (e.ctrlKey) SnappingManager.SetCanEmbed(true); + else if (!startCanEmbed) SnappingManager.SetCanEmbed(false); docDragData.userDropAction = e.ctrlKey && e.altKey ? dropActionType.copy : e.shiftKey ? dropActionType.move : e.ctrlKey ? dropActionType.embed : docDragData.defaultDropAction; const targClassName = e.target instanceof HTMLElement && typeof e.target.className === 'string' ? e.target.className : ''; if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(targClassName) && docDragData.draggedDocuments.length === 1) { diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 59e093683..671621bbe 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -122,6 +122,7 @@ } } } +.imageBox-regenerateDropTarget, .imageBox-alternateDropTarget { position: absolute; color: white; @@ -136,6 +137,13 @@ height: 100%; } } +.imageBox-regenerateDropTarget { + right: 30; + border-radius: 50%; + svg { + border-radius: 50%; + } +} .imageBox-fader img { position: absolute; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 55474cb7e..d122ca5b0 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -36,7 +36,7 @@ import { OverlayView } from '../OverlayView'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; import { DrawingFillHandler } from '../smartdraw/DrawingFillHandler'; -import { FireflyImageData, isFireflyImageData } from '../smartdraw/FireflyConstants'; +import { FireflyImageData, FireflyImageDimensions, isFireflyImageData } from '../smartdraw/FireflyConstants'; import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler'; import { StickerPalette } from '../smartdraw/StickerPalette'; import { StyleProp } from '../StyleProp'; @@ -45,6 +45,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; import { OpenWhere } from './OpenWhere'; +import { RichTextField } from '../../../fields/RichTextField'; export class ImageEditorData { // eslint-disable-next-line no-use-before-define @@ -83,6 +84,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { private _disposers: { [name: string]: IReactionDisposer } = {}; private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined; private _overlayIconRef = React.createRef<HTMLDivElement>(); + private _regenerateIconRef = React.createRef<HTMLDivElement>(); private _mainCont: HTMLDivElement | null = null; private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); imageRef: HTMLImageElement | null = null; // <video> ref @@ -196,36 +198,47 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this._searchInput = selection; }; - drop = undoable((e: Event, de: DragManager.DropEvent) => { - if (de.complete.docDragData) { - let added: boolean | undefined; - const targetIsBullseye = (ele: HTMLElement): boolean => { - if (!ele) return false; - if (ele === this._overlayIconRef.current) return true; - return targetIsBullseye(ele.parentElement as HTMLElement); - }; - if (de.metaKey || targetIsBullseye(e.target as HTMLElement)) { - added = de.complete.docDragData.droppedDocuments.reduce((last: boolean, drop: Doc) => { - this.layoutDoc[this.fieldKey + '_usePath'] = 'alternate:hover'; - return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_alternates', drop); - }, true); - } else if (de.altKey || !this.dataDoc[this.fieldKey]) { - const layoutDoc = de.complete.docDragData?.draggedDocuments[0]; - const targetField = Doc.LayoutFieldKey(layoutDoc); - const targetDoc = layoutDoc[DocData]; - if (targetDoc[targetField] instanceof ImageField) { - added = true; - this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField); - Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey); - Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey); + drop = undoable( + action((e: Event, de: DragManager.DropEvent) => { + if (de.complete.docDragData) { + let added: boolean | undefined; + const hitDropTarget = (ele: HTMLElement, dropTarget: HTMLDivElement | null): boolean => { + if (!ele) return false; + if (ele === dropTarget) return true; + return hitDropTarget(ele.parentElement as HTMLElement, dropTarget); + }; + if (de.metaKey || hitDropTarget(e.target as HTMLElement, this._overlayIconRef.current)) { + added = de.complete.docDragData.droppedDocuments.reduce((last: boolean, drop: Doc) => { + this.layoutDoc[this.fieldKey + '_usePath'] = 'alternate:hover'; + return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_alternates', drop); + }, true); + } else if (hitDropTarget(e.target as HTMLElement, this._regenerateIconRef.current)) { + this._regenerateLoading = true; + const drag = de.complete.docDragData.draggedDocuments.lastElement(); + const dragField = drag[Doc.LayoutFieldKey(drag)]; + const oldPrompt = StrCast(this.Document.ai_firefly_prompt, StrCast(this.Document.title)); + const newPrompt = (text: string) => (oldPrompt ? `${oldPrompt} ~~~ ${text}` : text); + DrawingFillHandler.drawingToImage(this.Document, 100, newPrompt(dragField instanceof RichTextField ? dragField.Text : ''), drag)?.then(action(() => (this._regenerateLoading = false))); + added = false; + } else if (de.altKey || !this.dataDoc[this.fieldKey]) { + const layoutDoc = de.complete.docDragData?.draggedDocuments[0]; + const targetField = Doc.LayoutFieldKey(layoutDoc); + const targetDoc = layoutDoc[DocData]; + if (targetDoc[targetField] instanceof ImageField) { + added = true; + this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField); + Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey); + Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey); + } } + added === false && e.preventDefault(); + added !== undefined && e.stopPropagation(); + return added; } - added === false && e.preventDefault(); - added !== undefined && e.stopPropagation(); - return added; - } - return false; - }, 'image drop'); + return false; + }), + 'image drop' + ); @undoBatch resolution = () => { @@ -485,6 +498,24 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { </Tooltip> ); } + @computed get regenerateImageIcon() { + return ( + <div + className="imageBox-regenerateDropTarget" + ref={this._regenerateIconRef} + onClick={() => DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs), { openLocation: OpenWhere.addRight })} + style={{ + display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_firefly_generatedDocs)) || this._regenerateLoading ? 'block' : 'none', + transform: `scale(${this.uiBtnScaling})`, + width: this._sideBtnWidth, + height: this._sideBtnWidth, + background: 'transparent', + // color: SettingsManager.userBackgroundColor, + }}> + {this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width="100%" height="100%" /> : <FontAwesomeIcon icon="portrait" color={SettingsManager.userColor} size="lg" />} + </div> + ); + } @computed get paths() { const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey])); // retrieve the primary image URL that is being rendered from the data doc @@ -807,6 +838,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { <ReactLoading type="spin" height={50} width={50} color={'blue'} /> </div> ) : null} + {this.regenerateImageIcon} {this.overlayImageIcon} {this.annotationLayer} {!this._mainCont || !this.DocumentView || !this._annotationLayer.current ? null : ( diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx index 7447f8afb..0a30b14b8 100644 --- a/src/client/views/smartdraw/DrawingFillHandler.tsx +++ b/src/client/views/smartdraw/DrawingFillHandler.tsx @@ -28,19 +28,15 @@ export class DrawingFillHandler { const hrefParts = ImageCast(styleImg).url.href.split('.'); return `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`; }); - DocumentView.GetDocImage(drawing)?.then(imageField => { + return DocumentView.GetDocImage(drawing)?.then(imageField => { if (imageField) { const aspectRatio = (drawing.width as number) / (drawing.height as number); - let dims: { width: number; height: number }; - if (aspectRatio > AspectRatioLimits[FireflyImageDimensions.Widescreen]) { - dims = FireflyDimensionsMap[FireflyImageDimensions.Widescreen]; - } else if (aspectRatio > AspectRatioLimits[FireflyImageDimensions.Landscape]) { - dims = FireflyDimensionsMap[FireflyImageDimensions.Landscape]; - } else if (aspectRatio < AspectRatioLimits[FireflyImageDimensions.Portrait]) { - dims = FireflyDimensionsMap[FireflyImageDimensions.Portrait]; - } else { - dims = FireflyDimensionsMap[FireflyImageDimensions.Square]; - } + const dims = (() => { + if (aspectRatio > AspectRatioLimits[FireflyImageDimensions.Widescreen]) return FireflyDimensionsMap[FireflyImageDimensions.Widescreen]; + if (aspectRatio > AspectRatioLimits[FireflyImageDimensions.Landscape]) return FireflyDimensionsMap[FireflyImageDimensions.Landscape]; + if (aspectRatio < AspectRatioLimits[FireflyImageDimensions.Portrait]) return FireflyDimensionsMap[FireflyImageDimensions.Portrait]; + return FireflyDimensionsMap[FireflyImageDimensions.Square]; + })(); const { href } = ImageCast(imageField).url; const hrefParts = href.split('.'); const structureUrl = `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`; @@ -62,7 +58,10 @@ export class DrawingFillHandler { _width: 500, data_nativeWidth: info.nativeWidth, data_nativeHeight: info.nativeHeight, - }) + }), + undefined, + undefined, + true ) ); if (!DocumentView.getFirstDocumentView(genratedDocs)) DocumentViewInternal.addDocTabFunc(genratedDocs, OpenWhere.addRight); |