aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ImageBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/ImageBox.tsx')
-rw-r--r--src/client/views/nodes/ImageBox.tsx329
1 files changed, 171 insertions, 158 deletions
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index caefbf542..017ef7191 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,11 +1,12 @@
+import { Button, Colors, Size, Type } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Slider, Tooltip } from '@mui/material';
import axios from 'axios';
-import { Colors, Button, Type, Size } from '@dash/components';
import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { extname } from 'path';
import * as React from 'react';
+import { AiOutlineSend } from 'react-icons/ai';
import ReactLoading from 'react-loading';
import { ClientUtils, DashColor, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
@@ -16,12 +17,14 @@ import { ObjectField } from '../../../fields/ObjectField';
import { Cast, DocCast, ImageCast, NumCast, RTFCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
+import { Upload } from '../../../server/SharedMediaTypes';
import { emptyFunction } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { DocUtils, FollowLinkScript } from '../../documents/DocUtils';
import { Networking } from '../../Network';
import { DragManager } from '../../util/DragManager';
+import { SettingsManager } from '../../util/SettingsManager';
import { SnappingManager } from '../../util/SnappingManager';
import { undoable, undoBatch } from '../../util/UndoManager';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -32,6 +35,9 @@ import { MarqueeAnnotator } from '../MarqueeAnnotator';
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 { SmartDrawHandler } from '../smartdraw/SmartDrawHandler';
import { StickerPalette } from '../smartdraw/StickerPalette';
import { StyleProp } from '../StyleProp';
import { DocumentView } from './DocumentView';
@@ -39,12 +45,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import { FocusViewOptions } from './FocusViewOptions';
import './ImageBox.scss';
import { OpenWhere } from './OpenWhere';
-import { Upload } from '../../../server/SharedMediaTypes';
-import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler';
-import { SettingsManager } from '../../util/SettingsManager';
-import { AiOutlineSend } from 'react-icons/ai';
-import { FireflyImageData } from '../smartdraw/FireflyConstants';
-import { DrawingFillHandler } from '../smartdraw/DrawingFillHandler';
+import { RichTextField } from '../../../fields/RichTextField';
export class ImageEditorData {
// eslint-disable-next-line no-use-before-define
@@ -83,7 +84,8 @@ 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 _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+ private _regenerateIconRef = React.createRef<HTMLDivElement>();
+ private _mainCont: HTMLDivElement | null = null;
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
imageRef: HTMLImageElement | null = null; // <video> ref
marqueeref = React.createRef<MarqueeAnnotator>();
@@ -108,6 +110,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
protected createDropTarget = (ele: HTMLDivElement) => {
+ this._mainCont = ele;
this._dropDisposer?.();
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.Document));
};
@@ -135,11 +138,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._disposers.sizer = reaction(
() => ({
forceFull: this._props.renderDepth < 1 || this.layoutDoc._showFullRes,
- scrSize: (this.ScreenToLocalBoxXf().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.layoutDoc._freeform_scale, 1),
+ scrSize: (NumCast(this.layoutDoc._freeform_scale, 1) / (this._props.DocumentView?.().screenToLocalScale() ?? 1)) * this._props.PanelWidth(),
selected: this._props.isSelected(),
}),
({ forceFull, scrSize, selected }) => {
- this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o';
+ this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 100 ? '_s' : scrSize < 400 ? '_m' : scrSize < 800 ? '_l' : '_o';
},
{ fireImmediately: true, delay: 1000 }
);
@@ -147,7 +150,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._disposers.path = reaction(
() => ({ nativeSize: this.nativeSize, width: NumCast(this.layoutDoc._width) }),
({ nativeSize, width }) => {
- if (layoutDoc === this.layoutDoc || !this.layoutDoc._height) {
+ if ((layoutDoc === this.layoutDoc && !this.layoutDoc._layout_nativeDimEditable) || !this.layoutDoc._height) {
this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth;
}
},
@@ -157,8 +160,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
() => this.layoutDoc.layout_scrollTop,
sTop => {
this._forcedScroll = true;
- !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(sTop));
- this._mainCont.current?.scrollTo({ top: NumCast(sTop) });
+ !this._ignoreScroll && this._mainCont && (this._mainCont.scrollTop = NumCast(sTop));
+ this._mainCont?.scrollTo({ top: NumCast(sTop) });
this._forcedScroll = false;
},
{ fireImmediately: true }
@@ -195,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 = () => {
@@ -315,6 +329,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return cropping;
};
+ docEditorView = action(() => {
+ const field = Cast(this.dataDoc[this.fieldKey], ImageField);
+ if (field) {
+ ImageEditorData.Open = true;
+ ImageEditorData.Source = this.choosePath(field.url);
+ ImageEditorData.AddDoc = this._props.addDocument;
+ ImageEditorData.RootDoc = this.Document;
+ }
+ });
+
specificContextMenu = (): void => {
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (field) {
@@ -343,7 +367,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const ext = extname(file);
return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
})(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
- }).then((info: Upload.ImageInformation) => {
+ }).then(res => {
+ const info = res as Upload.ImageInformation;
const img = Docs.Create.ImageDocument(info.accessPaths.agnostic.client, { title: 'expand:' + this.Document.title });
DocUtils.assignImageInfo(info, img);
this._props.addDocTab(img, OpenWhere.addRight);
@@ -352,21 +377,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
icon: 'expand-arrows-alt',
});
funcs.push({ description: 'Copy path', event: () => ClientUtils.CopyText(this.choosePath(field.url)), icon: 'copy' });
- funcs.push({
- description: 'Open Image Editor',
- event: action(() => {
- ImageEditorData.Open = true;
- ImageEditorData.Source = this.choosePath(field.url);
- ImageEditorData.AddDoc = this._props.addDocument;
- ImageEditorData.RootDoc = this.Document;
- }),
- icon: 'pencil-alt',
- });
+ funcs.push({ description: 'Open Image Editor', event: this.docEditorView, icon: 'pencil-alt' });
this.layoutDoc.ai &&
funcs.push({
description: 'Regenerate AI Image',
- event: action(e => {
- !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(e?.x || 0, e?.y || 0) : SmartDrawHandler.Instance.hideRegenerate();
+ event: action(() => {
+ if (!SmartDrawHandler.Instance.ShowRegenerate && this.DocumentView) {
+ const [x, y] = this.DocumentView().screenToViewTransform().inverse().transformPoint(NumCast(this.Document.width), 0);
+ this._props.docViewPath().slice(-2)[0]?.ComponentView?.showSmartDraw?.(x, y, true);
+ } else {
+ SmartDrawHandler.Instance.hideRegenerate();
+ }
}),
icon: 'pen-to-square',
});
@@ -381,7 +402,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// updateIcon = () => new Promise<void>(res => res());
updateIcon = (usePanelDimensions?: boolean) => {
- const contentDiv = this._mainCont.current;
+ const contentDiv = this._mainCont;
return !contentDiv
? new Promise<void>(res => res())
: UpdateIcon(
@@ -415,6 +436,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
getScrollHeight = () => (this._props.fitWidth?.(this.Document) !== false && NumCast(this.layoutDoc._freeform_scale, 1) === NumCast(this.dataDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
+ @computed get usingAlternate() {
+ const usePath = StrCast(this.Document[this.fieldKey + '_usePath']);
+ return 'alternate' === usePath || ('alternate:hover' === usePath && this._isHovering) || (':hover' === usePath && !this._isHovering);
+ }
+
@computed get nativeSize() {
TraceMobx();
if (this.paths.length && this.paths[0].includes('icon-hi')) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0 };
@@ -423,6 +449,20 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '_nativeOrientation'], 1);
return { nativeWidth, nativeHeight, nativeOrientation };
}
+ private _sideBtnWidth = 35;
+ /**
+ * How much the content of the view is being scaled based on its nesting and its fit-to-width settings
+ */
+ @computed get viewScaling() { return this.ScreenToLocalBoxXf().Scale * ( this._props.NativeDimScaling?.() || 1); } // prettier-ignore
+ /**
+ * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
+ */
+ @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth, 0.5 * Math.min(NumCast(this.Document.width)))* this.viewScaling; } // prettier-ignore
+ /**
+ * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
+ */
+ @computed get uiBtnScaling() { return Math.min(this.maxWidgetSize / this._sideBtnWidth, 1); } // prettier-ignore
+
@computed get overlayImageIcon() {
const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`];
return (
@@ -436,10 +476,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<span style={{ color: usePath === 'alternate' ? 'black' : undefined }}>
<em>alternate, </em>
</span>
- and show
<span style={{ color: usePath === 'alternate:hover' ? 'black' : undefined }}>
<em> alternate on hover</em>
</span>
+ and show
+ <span style={{ color: usePath === ':hover' ? 'black' : undefined }}>
+ <em> primary on hover</em>
+ </span>
</div>
}>
<div
@@ -447,13 +490,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ref={this._overlayIconRef}
onPointerDown={e =>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => {
- this.layoutDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
+ this.layoutDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : usePath === 'alternate:hover' ? ':hover' : undefined;
})
}
style={{
- display: (this._props.isContentActive() !== false && SnappingManager.CanEmbed) || this.dataDoc[this.fieldKey + '_alternates'] ? 'block' : 'none',
- width: 'min(10%, 25px)',
- height: 'min(10%, 25px)',
+ display: this._props.isContentActive() && (SnappingManager.CanEmbed || this.dataDoc[this.fieldKey + '_alternates']) ? 'block' : 'none',
+ transform: `scale(${this.uiBtnScaling})`,
+ width: this._sideBtnWidth,
+ height: this._sideBtnWidth,
background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
color: usePath === undefined ? 'black' : 'white',
}}>
@@ -462,6 +506,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
@@ -473,7 +535,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
.filter(url => url)
.map(url => this.choosePath(url)) ?? []; // acc ess the primary layout data of the alternate documents
const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
- return paths.length ? paths : [defaultUrl.href];
+ return paths.length ? paths.reverse() : [defaultUrl.href];
}
@computed get content() {
@@ -498,7 +560,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
transformOrigin = 'right top';
transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
}
- const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`];
return (
<div
@@ -510,7 +571,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._isHovering = false;
})}
key={this.layoutDoc[Id]}
- ref={this.createDropTarget}
onPointerDown={this.marqueeDown}>
<div className="imageBox-fader" style={{ opacity: backAlpha }}>
<img
@@ -518,7 +578,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ref={action((r: HTMLImageElement | null) => (this.imageRef = r))}
key="paths"
src={srcpath}
- style={{ transform, transformOrigin, objectFit: 'fill', height: '100%' }}
+ style={{ transform, transformOrigin }}
onError={action(e => {
this._error = e.toString();
})}
@@ -526,12 +586,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
width={nativeWidth}
/>
{fadepath === srcpath ? null : (
- <div className={`imageBox-fadeBlocker${(this._isHovering && usePath === 'alternate:hover') || usePath === 'alternate' ? '-hover' : ''}`} style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
+ <div className={`imageBox-fadeBlocker${this.usingAlternate ? '-hover' : ''}`} style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
<img alt="" className="imageBox-fadeaway" key="fadeaway" src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
</div>
)}
</div>
- {this.overlayImageIcon}
</div>
);
}
@@ -567,123 +626,72 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return (
<div className="imageBox-aiView">
<div className="imageBox-aiView-regenerate">
- <span className="imageBox-aiView-firefly">Firefly:</span>
+ <span className="imageBox-aiView-firefly" style={{ color: SnappingManager.userColor }}>
+ Firefly:
+ </span>
<input
+ style={{ color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}
className="imageBox-aiView-input"
aria-label="Edit instructions input"
type="text"
- value={this._regenInput}
+ value={this._regenInput || StrCast(this.Document.title)}
onChange={action(e => this._canInteract && (this._regenInput = e.target.value))}
placeholder={this._regenInput || StrCast(this.Document.title)}
/>
- <div className="imageBox-aiView-strength">
- <span className="imageBox-aiView-similarity">Similarity</span>
- <Slider
- className="imageBox-aiView-slider"
- sx={{
- '& .MuiSlider-track': { color: SettingsManager.userVariantColor },
- '& .MuiSlider-rail': { color: SettingsManager.userBackgroundColor },
- '& .MuiSlider-thumb': { color: SettingsManager.userVariantColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
- }}
- min={0}
- max={100}
- step={1}
- size="small"
- value={this._fireflyRefStrength}
- onChange={action((e, val) => this._canInteract && (this._fireflyRefStrength = val as number))}
- valueLabelDisplay="auto"
- />
- </div>
<div className="imageBox-aiView-regenerate-createBtn">
<Button
text="Create"
- type={Type.SEC}
+ type={Type.TERT}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userBackgroundColor}
// style={{ alignSelf: 'flex-end' }}
icon={this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
iconPlacement="right"
onClick={action(async () => {
this._regenerateLoading = true;
if (this._fireflyRefStrength) {
- DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title), this.Document)?.then(
- action(() => {
- this._regenerateLoading = false;
- })
- );
- } else
+ DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title), this.Document)?.then(action(() => (this._regenerateLoading = false)));
+ } else {
SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput || StrCast(this.Document.title), true).then(
action(newImgs => {
- if (newImgs[0]) {
- const url = newImgs[0].pathname;
+ const firstImg = newImgs[0];
+ if (isFireflyImageData(firstImg)) {
+ const url = firstImg.pathname;
const imgField = new ImageField(url);
this._prevImgs.length === 0 &&
this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: this.dataDoc.ai_firefly_seed as number, href: this.paths.lastElement(), pathname: field.url.pathname });
- this._prevImgs.unshift({ prompt: newImgs[0].prompt, seed: newImgs[0].seed, pathname: url });
+ this._prevImgs.unshift({ prompt: firstImg.prompt, seed: firstImg.seed, pathname: url });
this.dataDoc.ai_firefly_history = JSON.stringify(this._prevImgs);
- this.dataDoc.ai_firefly_prompt = newImgs[0].prompt;
+ this.dataDoc.ai_firefly_prompt = firstImg.prompt;
this.dataDoc[this.fieldKey] = imgField;
this._regenerateLoading = false;
this._regenInput = '';
}
})
);
+ }
})}
/>
</div>
</div>
- <div className="imageBox-aiView-options">
- <span className="imageBox-aiView-subtitle"> More: </span>
- <Button
- type={Type.TERT}
- text="Get Text"
- icon={<FontAwesomeIcon icon="font" />}
- color={SettingsManager.userBackgroundColor}
- iconPlacement="right"
- onClick={() => {
- Networking.PostToServer('/queryFireflyImageText', {
- file: (file => {
- const ext = extname(file);
- return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
- })(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
- }).then(text => alert(text));
- }}
- />
- <Button
- type={Type.TERT}
- text="Generative Fill"
- icon={<FontAwesomeIcon icon="fill" />}
- color={SettingsManager.userBackgroundColor}
- iconPlacement="right"
- onClick={action(() => {
- ImageEditorData.Open = true;
- ImageEditorData.Source = (field && this.choosePath(field.url)) || '';
- ImageEditorData.AddDoc = this._props.addDocument;
- ImageEditorData.RootDoc = this.Document;
- })}
- />
- <Button
- type={Type.TERT}
- text="Expand"
- icon={<FontAwesomeIcon icon="expand" />}
- color={SettingsManager.userBackgroundColor}
- iconPlacement="right"
- onClick={() => {
- Networking.PostToServer('/expandImage', {
- prompt: 'sunny skies',
- file: (file => {
- const ext = extname(file);
- return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
- })(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
- }).then((info: Upload.ImageInformation) => {
- const img = Docs.Create.ImageDocument(info.accessPaths.agnostic.client, { title: 'expand:' + this.Document.title });
- DocUtils.assignImageInfo(info, img);
- const genratedDocs = this.Document.generatedDocs
- ? DocCast(this.Document.generatedDocs)
- : Docs.Create.MasonryDocument([], { _width: 400, _height: 400, x: NumCast(this.Document.x) + NumCast(this.Document.width), y: NumCast(this.Document.y) });
- Doc.AddDocToList(genratedDocs, undefined, img);
- this.Document[DocData].generatedDocs = genratedDocs;
- if (!DocumentView.getFirstDocumentView(genratedDocs)) this._props.addDocTab(genratedDocs, OpenWhere.addRight);
- });
+ <div className="imageBox-aiView-strength">
+ <span className="imageBox-aiView-similarity" style={{ color: SnappingManager.userColor }}>
+ Similarity
+ </span>
+ <Slider
+ className="imageBox-aiView-slider"
+ sx={{
+ '& .MuiSlider-track': { color: SettingsManager.userColor },
+ '& .MuiSlider-rail': { color: SettingsManager.userBackgroundColor },
+ '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
}}
+ min={0}
+ max={100}
+ step={1}
+ size="small"
+ value={this._fireflyRefStrength}
+ onChange={action((e, val) => this._canInteract && (this._fireflyRefStrength = val as number))}
+ valueLabelDisplay="auto"
/>
</div>
</div>
@@ -726,25 +734,27 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
focus = (anchor: Doc, options: FocusViewOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options));
renderedPixelDimensions = async () => {
- const { nativeWidth: width, nativeHeight: height } = await Networking.PostToServer('/inspectImage', { source: this.paths[0] });
+ const res = await Networking.PostToServer('/inspectImage', { source: this.paths[0] });
+ const { nativeWidth: width, nativeHeight: height } = res as { nativeWidth: number; nativeHeight: number };
return { width, height };
};
savedAnnotations = () => this._savedAnnotations;
-
render() {
TraceMobx();
const borderRad = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding) as string;
const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this._props.NativeDimScaling?.() || 1)}px` : borderRad;
+ const alts = DocListCast(this.dataDoc[this.fieldKey + '_alternates']);
+ const doc = this.usingAlternate ? (alts.lastElement() ?? this.Document) : this.Document;
return (
<div
className="imageBox"
onContextMenu={this.specificContextMenu}
- ref={this._mainCont}
+ ref={this.createDropTarget}
onScroll={action(() => {
if (!this._forcedScroll) {
- if (this.layoutDoc._layout_scrollTop || this._mainCont.current?.scrollTop) {
+ if (this.layoutDoc._layout_scrollTop || this._mainCont?.scrollTop) {
this._ignoreScroll = true;
- this.layoutDoc._layout_scrollTop = this._mainCont.current?.scrollTop;
+ this.layoutDoc._layout_scrollTop = this._mainCont?.scrollTop;
this._ignoreScroll = false;
}
}
@@ -759,6 +769,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<CollectionFreeFormView
ref={this._ffref}
{...this._props}
+ Document={doc}
setContentViewBox={emptyFunction}
NativeWidth={returnZero}
NativeHeight={returnZero}
@@ -786,8 +797,10 @@ 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.current || !this.DocumentView || !this._annotationLayer.current ? null : (
+ {!this._mainCont || !this.DocumentView || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
Document={this.Document}
ref={this.marqueeref}
@@ -802,7 +815,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
savedAnnotations={this.savedAnnotations}
selectionText={returnEmptyString}
annotationLayer={this._annotationLayer.current}
- marqueeContainer={this._mainCont.current}
+ marqueeContainer={this._mainCont}
highlightDragSrcColor=""
anchorMenuCrop={this.crop}
// anchorMenuFlashcard={() => this.getImageDesc()}
@@ -839,5 +852,5 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
Docs.Prototypes.TemplateMap.set(DocumentType.IMG, {
layout: { view: ImageBox, dataField: 'data' },
- options: { acl: '', freeform: '', systemIcon: 'BsFileEarmarkImageFill' },
+ options: { acl: '', freeform: '', _layout_nativeDimEditable: true, systemIcon: 'BsFileEarmarkImageFill' },
});