diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 14 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.scss | 3 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 59 | ||||
| -rw-r--r-- | src/client/views/nodes/IconTagBox.tsx | 24 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.scss | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 96 | ||||
| -rw-r--r-- | src/client/views/nodes/imageEditor/ImageEditor.tsx | 5 | ||||
| -rw-r--r-- | src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts | 6 |
8 files changed, 81 insertions, 128 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 940c4cb99..3805b0dca 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -148,7 +148,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF public static getValues(doc: Doc, time: number, fillIn: boolean = true) { return CollectionFreeFormDocumentView.animFields.reduce( (p, val) => { - p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce( + p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : [])!.reduce( (prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev), undefined as unknown as number ); @@ -161,7 +161,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF public static getStringValues(doc: Doc, time: number) { return CollectionFreeFormDocumentView.animStringFields.reduce( (p, val) => { - p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev), undefined as unknown as string); + p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])])!.reduce((prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev), undefined as unknown as string); return p; }, {} as { [val: string]: Opt<string> } @@ -171,7 +171,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF public static setStringValues(time: number, d: Doc, vals: { [val: string]: Opt<string> }) { const timecode = Math.round(time); Object.keys(vals).forEach(val => { - const findexed = Cast(d[`${val}_indexed`], listSpec('string'), []).slice(); + const findexed = Cast(d[`${val}_indexed`], listSpec('string'), [])!.slice(); findexed[timecode] = vals[val] || ''; d[`${val}_indexed`] = new List<string>(findexed); }); @@ -180,7 +180,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) { const timecode = Math.round(time); Object.keys(vals).forEach(val => { - const findexed = Cast(d[`${val}_indexed`], listSpec('number'), []).slice(); + const findexed = Cast(d[`${val}_indexed`], listSpec('number'), [])!.slice(); findexed[timecode] = vals[val] as unknown as number; d[`${val}_indexed`] = new List<number>(findexed); }); @@ -204,15 +204,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF docs.forEach(doc => { this.animFields.forEach(val => { const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as number); + (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as number); }); this.animStringFields.forEach(val => { const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as string); + (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as string); }); this.animDataFields(doc).forEach(val => { const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as InkField); + (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as InkField); }); }); return newTimer; diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 5ac66f2cd..c4351a200 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -119,6 +119,7 @@ display: flex; justify-content: center; align-items: center; + margin: auto; position: relative; // allows contents to be positioned relative/below title > .formattedTextBox { position: absolute; // position a child text box @@ -302,6 +303,6 @@ background: transparent; .documentView-editorView-resizer { - height: 5px; + height: 2px; } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d756f3226..bdb97d7bb 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -648,11 +648,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field rootSelected = () => this._rootSelected; panelHeight = () => this._props.PanelHeight() - this.headerMargin - 2 * NumCast(this.Document.borderWidth); - screenToLocalContent = () => - this._props - .ScreenToLocalTransform() - .translate(-NumCast(this.Document.borderWidth), -this.headerMargin - NumCast(this.Document.borderWidth)) - .scale(this.viewingAiEditor() ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1); + aiShift = () => (!this.viewingAiEditor() ? 0 : (this._props.PanelWidth() - this.aiContentsWidth()) / 2); + aiScale = () => (this.viewingAiEditor() ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1); onClickFunc = () => (this.disableClickScriptFunc ? undefined : this.onClickHdlr); setHeight = (height: number) => { !this._props.suppressSetHeight && (this.layoutDoc._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), height + 2 * NumCast(this.Document.borderWidth))); } // prettier-ignore setContentView = action((view: ViewBoxInterface<FieldViewProps>) => (this._componentView = view)); @@ -678,7 +675,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field return this._props.styleProvider?.(doc, props, property); }; - @observable _aiWinHeight = 88; + @observable _aiWinHeight = 32; TagsBtnHeight = 22; @computed get currentScale() { @@ -703,33 +700,21 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field @computed get uiBtnScaling() { return Math.max(this.maxWidgetSize / this.TagsBtnHeight, 1) * Math.min(1, this.viewScaling); } // prettier-ignore aiContentsWidth = () => (this.aiContentsHeight() * (this._props.NativeWidth?.() || 1)) / (this._props.NativeHeight?.() || 1); - aiContentsHeight = () => Math.max(10, this._props.PanelHeight() - this._aiWinHeight * this.uiBtnScaling); + aiContentsHeight = () => Math.max(10, this._props.PanelHeight() - (this._aiWinHeight + (this.tagsOverlayFunc() ? 22 : 0)) * this.uiBtnScaling); @computed get aiEditor() { return ( - <> - <div - className="documentView-editorView-history" - ref={r => this.historyRef(this._oldAiWheel, (this._oldAiWheel = r))} - style={{ - transform: `scale(${this.uiBtnScaling})`, - height: this.aiContentsHeight() / this.uiBtnScaling, - width: ((this._props.PanelWidth() - this.aiContentsWidth()) * 0.95) / this.uiBtnScaling, - }}> - {this._componentView?.componentAIViewHistory?.() ?? null} - </div> - <div - className="documentView-editorView" - style={{ - background: SnappingManager.userVariantColor, - width: `${100 / this.uiBtnScaling}%`, // - transform: `scale(${this.uiBtnScaling})`, - }} - ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}> - <div className="documentView-editorView-resizer" /> - {this._componentView?.componentAIView?.() ?? null} - {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null} - </div> - </> + <div + className="documentView-editorView" + style={{ + background: SnappingManager.userVariantColor, + width: `${100 / this.uiBtnScaling}%`, // + transform: `scale(${this.uiBtnScaling})`, + }} + ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}> + <div className="documentView-editorView-resizer" /> + {this._componentView?.componentAIView?.() ?? null} + {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null} + </div> ); } @computed get tagsOverlay() { @@ -780,7 +765,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field PanelHeight={this.viewingAiEditor() ? this.aiContentsHeight : this.panelHeight} setHeight={this.setHeight} isContentActive={this.isContentActive} - ScreenToLocalTransform={this.screenToLocalContent} rootSelected={this.rootSelected} onClickScript={this.onClickFunc} setTitleFocus={this.setTitleFocus} @@ -1291,7 +1275,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { if (this.ComponentView?.screenBounds?.()) { return this.ComponentView.screenBounds(); } - const xf = this.screenToContentsTransform().scale(this.nativeScaling).inverse(); + const xf = this.screenToContentBoundsTransform().inverse(); const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)]; // transition is returned so that the bounds will 'update' at the end of an animated transition. This is needed by xAnchor in LinkBox @@ -1498,17 +1482,22 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { isHovering = () => this._isHovering; selfView = () => this; /** - * @returns Transform to the document view (in the coordinate system of whatever contains the DocumentView) + * @returns Transform to the document view's available panel space (in the coordinate system of whatever contains the DocumentView) */ screenToViewTransform = () => this._props.ScreenToLocalTransform(); /** + * @returns Transform to the document view after centering in available panel space(in the coordinate system of whatever contains the DocumentView) + */ + private screenToContentBoundsTransform = () => this.screenToViewTransform().translate(-this.centeringX, -this.centeringY); + /** * @returns Transform to the coordinate system of the contents of the document view (includes native dimension scaling and centering) */ screenToContentsTransform = () => this._props .ScreenToLocalTransform() .translate(-this.centeringX, -this.centeringY) - .scale(1 / this.nativeScaling); + .translate(-(this._docViewInternal?.aiShift() ?? 0), 0) + .scale((this._docViewInternal?.aiScale() ?? 1) / this.nativeScaling); htmlOverlay = () => { const effect = StrCast(this._htmlOverlayEffect?.presentation_effect, StrCast(this._htmlOverlayEffect?.followLinkAnimEffect)); diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx index 0bbd6a0d3..d04ec3a10 100644 --- a/src/client/views/nodes/IconTagBox.tsx +++ b/src/client/views/nodes/IconTagBox.tsx @@ -58,16 +58,20 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> { const tag = StrCast(key.toolType); const color = dv._props.styleProvider?.(dv.layoutDoc, dv.ComponentView?._props, StyleProp.FontColor) as string; return ( - <Toggle - tooltip={`Click to add/remove the tag ${tag}`} - toggleStatus={TagItem.docHasTag(dv.Document, tag)} - toggleType={ToggleType.BUTTON} - icon={<FontAwesomeIcon className={`fontIconBox-icon-${ToggleType.BUTTON}`} icon={icon} color={color} />} - size={Size.XSMALL} - type={Type.PRIM} - onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))} - color={color} - /> + <div> + {' '} + {/* tooltips require the wrapped item to be an element ref */} + <Toggle + tooltip={`Click to add/remove the tag ${tag}`} + toggleStatus={TagItem.docHasTag(dv.Document, tag)} + toggleType={ToggleType.BUTTON} + icon={<FontAwesomeIcon className={`fontIconBox-icon-${ToggleType.BUTTON}`} icon={icon} color={color} />} + size={Size.XSMALL} + type={Type.PRIM} + onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))} + color={color} + /> + </div> ); }; diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 9f7a5d03f..5a6292fab 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -236,7 +236,7 @@ .imageBox-aiView-input { overflow: hidden; text-overflow: ellipsis; - max-width: 65%; + max-width: 80%; width: 100%; color: black; } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d16baada6..bf6915570 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,6 +1,6 @@ -import { Button, Colors, EditableText, IconButton, Size, Toggle, ToggleType, Type } from '@dash/components'; +import { Button, Colors, EditableText, IconButton, NumberDropdown, Size, Toggle, ToggleType, Type } from '@dash/components'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Slider, Tooltip } from '@mui/material'; +import { Tooltip } from '@mui/material'; import axios from 'axios'; import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -37,7 +37,6 @@ 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'; @@ -102,7 +101,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @observable private _regenInput = ''; @observable private _canInteract = true; @observable private _regenerateLoading = false; - @observable private _prevImgs: FireflyImageData[] = StrCast(this.Document.ai_firefly_history) ? JSON.parse(StrCast(this.Document.ai_firefly_history)) : []; // Add these observable properties to the ImageBox class @observable private _outpaintingInProgress = false; @@ -845,34 +843,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined; @observable private _fireflyRefStrength = 0; - componentAIViewHistory = () => ( - <div className="imageBox-aiView-history"> - <Button text="Clear History" type={Type.SEC} size={Size.XSMALL} /> - {this._prevImgs.map(img => ( - <div key={img.pathname}> - <img - className="imageBox-aiView-img" - src={ClientUtils.prepend(img.pathname.replace(extname(img.pathname), '_s' + extname(img.pathname)))} - onClick={() => { - this.dataDoc[this.fieldKey] = new ImageField(img.pathname); - this.dataDoc.ai_firefly_prompt = img.prompt; - this.dataDoc.ai_firefly_seed = img.seed; - }} - /> - <span>{img.prompt}</span> - </div> - ))} - </div> - ); - componentAIView = () => { - const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey])); return ( <div className="imageBox-aiView"> <div className="imageBox-aiView-regenerate"> - <span className="imageBox-aiView-firefly" style={{ color: SnappingManager.userColor }}> - Firefly: - </span> <input style={{ color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }} className="imageBox-aiView-input" @@ -886,57 +860,39 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { <Button text="Create" type={Type.TERT} + size={Size.XSMALL} 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 () => { + onClick={action(() => { this._regenerateLoading = true; - if (this._fireflyRefStrength) { - DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title))?.then(action(() => (this._regenerateLoading = false))); - } else { - SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput || StrCast(this.Document.title), true).then( - action(newImgs => { - 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: firstImg.prompt, seed: firstImg.seed, pathname: url }); - this.dataDoc.ai_firefly_history = JSON.stringify(this._prevImgs); - this.dataDoc.ai_firefly_prompt = firstImg.prompt; - this.dataDoc[this.fieldKey] = imgField; - this._regenerateLoading = false; - this._regenInput = ''; - } - }) - ); - } + DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title))?.then(action(() => (this._regenerateLoading = false))); })} /> </div> - </div> - <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> + <NumberDropdown + color={SnappingManager.userColor} + background={SnappingManager.userBackgroundColor} + numberDropdownType="slider" + showPlusMinus={false} + formLabel="similarity" + tooltip="structure similarity of created images to current image" + type={Type.PRIM} + width={75} + min={0} + max={100} + number={this._fireflyRefStrength} + size={Size.XXSMALL} + setNumber={undoable( + action(val => this._canInteract && (this._fireflyRefStrength = val as number)), + `${this.Document.title} button set from list` + )} + fillWidth + /> + </div> </div> </div> ); diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx index 85bd95d15..198b8e713 100644 --- a/src/client/views/nodes/imageEditor/ImageEditor.tsx +++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx @@ -281,11 +281,14 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc try { const canvasOriginalImg = ImageUtility.getCanvasImg(img); if (!canvasOriginalImg) return; - const canvasMask = ImageUtility.getCanvasMask(canvas, canvasOriginalImg); + const canvasMask = ImageUtility.getCanvasMask(canvas, canvas); if (!canvasMask) return; const maskBlob = await ImageUtility.canvasToBlob(canvasMask); const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg); const res = await ImageUtility.getEdit(imgBlob, maskBlob, input || 'Fill in the image in the same style', 2); + if ((res as any).status == 'error') { + alert((res as any).message); + } // create first image if (!newCollectionRef.current) { diff --git a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts index 1c6a38a24..d6093c6eb 100644 --- a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts +++ b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts @@ -75,7 +75,7 @@ export class ImageUtility { fd.append('mask', maskBlob, 'mask.png'); fd.append('prompt', prompt); fd.append('size', '1024x1024'); - fd.append('n', n ? JSON.stringify(n) : '1'); + fd.append('n', n ? n + '' : '1'); fd.append('response_format', 'b64_json'); try { @@ -268,14 +268,14 @@ export class ImageUtility { ctx.drawImage(img, xOffset, 0, width, height); // draw reflected image padding - this.drawHorizontalReflection(ctx, canvas, xOffset); + // this.drawHorizontalReflection(ctx, canvas, xOffset); } else { // vertical padding, y offset const yOffset = Math.floor((canvasSize - height) / 2); ctx.drawImage(img, 0, yOffset, width, height); // draw reflected image padding - this.drawVerticalReflection(ctx, canvas, yOffset); + // this.drawVerticalReflection(ctx, canvas, yOffset); } return canvas; }; |
