diff options
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 226 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 33 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 2 |
3 files changed, 168 insertions, 93 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9af698ec7..af82724ef 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,5 +1,5 @@ import { Bezier } from 'bezier-js'; -import { Colors } from 'browndash-components'; +import { Button, Colors, Type } from 'browndash-components'; import { Property } from 'csstype'; import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -67,6 +67,8 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors'; import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; +import ReactLoading from 'react-loading'; +import { SettingsManager } from '../../../util/SettingsManager'; @observer class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> { @@ -2186,84 +2188,160 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection </div> ); } - render() { - TraceMobx(); + + @observable private _regenInput = ''; + @observable private _canInteract = true; + @observable private _regenerateLoading = false; + + componentAIViewHistory = () => { return ( - <div - className="collectionfreeformview-container" - id={this._paintedId} - ref={r => { - this.createDashEventsTarget(r); - this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); - this._oldWheel = r; - // prevent wheel events from passivly propagating up through containers - r?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); - r?.addEventListener('mouseleave', this.onMouseLeave); - r?.addEventListener('mouseenter', this.onMouseEnter); - }} - onWheel={this.onPointerWheel} - onClick={this.onClick} - onPointerDown={this.onPointerDown} - onPointerMove={this.onCursorMove} - onDrop={this.onExternalDrop} - onDragOver={e => e.preventDefault()} - onContextMenu={this.onContextMenu} - style={{ - pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : this._props.pointerEvents?.(), - textAlign: this.isAnnotationOverlay ? 'initial' : undefined, - transform: `scale(${this.nativeDimScaling})`, - width: `${100 / this.nativeDimScaling}%`, - height: this._props.getScrollHeight?.() ?? `${100 / this.nativeDimScaling}%`, - }}> - {Doc.ActiveTool === InkTool.Eraser && Doc.ActiveEraser === InkEraserTool.Radius && this._showEraserCircle && ( - <div - onPointerMove={this.onCursorMove} - style={{ - position: 'fixed', - left: this._eraserX, - top: this._eraserY, - width: (ActiveEraserWidth() + 5) * 2, - height: (ActiveEraserWidth() + 5) * 2, - borderRadius: '50%', - border: '1px solid gray', - transform: 'translate(-50%, -50%)', + <div className="imageBox-aiView-history"> + {/* {this._prevImgs.map(img => ( + <img + className="imageBox-aiView-img" + src={img.href} + onClick={() => { + this.dataDoc[this.fieldKey] = new ImageField(img.pathname); + this.dataDoc.ai_firefly_prompt = img.prompt; + this.dataDoc.ai_firefly_seed = img.seed; }} /> - )} - {this.paintFunc ? ( - <FormattedTextBox {...this.props} /> // need this so that any live dashfieldviews will update the underlying text that the code eval reads - ) : this._lightboxDoc ? ( - <div style={{ padding: 15, width: '100%', height: '100%' }}> - <DocumentView - {...this._props} - Document={this._lightboxDoc} - containerViewPath={this.DocumentView?.().docViewPath} - TemplateDataDocument={undefined} - PanelWidth={this.lightboxPanelWidth} - PanelHeight={this.lightboxPanelHeight} - NativeWidth={returnZero} - NativeHeight={returnZero} - onClickScript={this.onChildClickHandler} - onKey={this.onKeyDown} - onDoubleClickScript={this.onChildDoubleClickHandler} - childFilters={this.childDocFilters} - childFiltersByRanges={this.childDocRangeFilters} - searchFilterDocs={this.searchFilterDocs} - isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this.isContentActive} - isContentActive={this._props.childContentsActive ?? emptyFunction} - addDocTab={this.addDocTab} - ScreenToLocalTransform={this.lightboxScreenToLocal} - fitContentsToBox={undefined} - focus={this.focus} - /> + ))} */} + </div> + ); + }; + + componentAIView = () => { + const showRegenerate = this.Document[DocData].ai; + return ( + <div className="collectionfreeformview-aiView"> + Edit Image with AI + {showRegenerate && ( + <div className="collectionfreeformview-aiView-regenerate-container"> + {/* <text className="collectionfreeformview-aiView-subtitle">Regenerate AI Image</text> + <div className="collectionfreeformview-aiView-regenerate"> + <input + className="collectionfreeformview-aiView-input" + aria-label="Edit instructions input" + // className="smartdraw-input" + type="text" + value={this._regenInput} + onChange={action(e => this._canInteract && (this._regenInput = e.target.value))} + // onKeyDown={this.handleKeyPress} + placeholder="Prompt (Optional)" + /> + <Button + text="Regenerate" + type={Type.SEC} + icon={this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} + iconPlacement="right" + onClick={action(async () => { + this._regenerateLoading = true; + SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true).then(newDrawings => { + if (newDrawings[0]) { + } + }); + })} + /> + <Button + // style={{ alignSelf: 'flex-end' }} + text="Get Variations" + type={Type.SEC} + // icon={this._isLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} + iconPlacement="right" + // onClick={this.handleSendClick} + /> + </div> */} </div> - ) : ( - <> - {this._firstRender ? this.placeholder : this.marqueeView} - {this._props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />} - {!this.GroupChildDrag ? null : <div className="collectionFreeForm-groupDropper" />} - </> )} + <div className="collectionfreeformview-aiView-options-container"> + {showRegenerate && <text className="collectionfreeformview-aiView-subtitle"> Turn Drawing to Image </text>} + <div className="collectionfreeformview-aiView-options"></div> + </div> + </div> + ); + }; + + render() { + TraceMobx(); + return ( + <div> + <div + className="collectionfreeformview-container" + id={this._paintedId} + ref={r => { + this.createDashEventsTarget(r); + this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); + this._oldWheel = r; + // prevent wheel events from passivly propagating up through containers + r?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); + r?.addEventListener('mouseleave', this.onMouseLeave); + r?.addEventListener('mouseenter', this.onMouseEnter); + }} + onWheel={this.onPointerWheel} + onClick={this.onClick} + onPointerDown={this.onPointerDown} + onPointerMove={this.onCursorMove} + onDrop={this.onExternalDrop} + onDragOver={e => e.preventDefault()} + onContextMenu={this.onContextMenu} + style={{ + pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : this._props.pointerEvents?.(), + textAlign: this.isAnnotationOverlay ? 'initial' : undefined, + transform: `scale(${this.nativeDimScaling})`, + width: `${100 / this.nativeDimScaling}%`, + height: this._props.getScrollHeight?.() ?? `${100 / this.nativeDimScaling}%`, + }}> + {Doc.ActiveTool === InkTool.Eraser && Doc.ActiveEraser === InkEraserTool.Radius && this._showEraserCircle && ( + <div + onPointerMove={this.onCursorMove} + style={{ + position: 'fixed', + left: this._eraserX, + top: this._eraserY, + width: (ActiveEraserWidth() + 5) * 2, + height: (ActiveEraserWidth() + 5) * 2, + borderRadius: '50%', + border: '1px solid gray', + transform: 'translate(-50%, -50%)', + }} + /> + )} + {this.paintFunc ? ( + <FormattedTextBox {...this.props} /> // need this so that any live dashfieldviews will update the underlying text that the code eval reads + ) : this._lightboxDoc ? ( + <div style={{ padding: 15, width: '100%', height: '100%' }}> + <DocumentView + {...this._props} + Document={this._lightboxDoc} + containerViewPath={this.DocumentView?.().docViewPath} + TemplateDataDocument={undefined} + PanelWidth={this.lightboxPanelWidth} + PanelHeight={this.lightboxPanelHeight} + NativeWidth={returnZero} + NativeHeight={returnZero} + onClickScript={this.onChildClickHandler} + onKey={this.onKeyDown} + onDoubleClickScript={this.onChildDoubleClickHandler} + childFilters={this.childDocFilters} + childFiltersByRanges={this.childDocRangeFilters} + searchFilterDocs={this.searchFilterDocs} + isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this.isContentActive} + isContentActive={this._props.childContentsActive ?? emptyFunction} + addDocTab={this.addDocTab} + ScreenToLocalTransform={this.lightboxScreenToLocal} + fitContentsToBox={undefined} + focus={this.focus} + /> + </div> + ) : ( + <> + {this._firstRender ? this.placeholder : this.marqueeView} + {this._props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />} + {!this.GroupChildDrag ? null : <div className="collectionFreeForm-groupDropper" />} + </> + )} + </div> </div> ); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index a1fa9a283..6b9e170c8 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -580,24 +580,21 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { // style={{ alignSelf: 'flex-end' }} icon={this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} iconPlacement="right" - onClick={undoable( - action(async () => { - this._regenerateLoading = true; - SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true).then(newImgs => { - if (newImgs[0]) { - const url = newImgs[0].pathname; - const imgField = new ImageField(url); - this._prevImgs.length === 0 && - this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: NumCast(this.dataDoc.ai_firefly_seed), href: this.paths.lastElement(), pathname: field.url.pathname }); - this.dataDoc[this.fieldKey] = imgField; - this._prevImgs.unshift({ prompt: newImgs[0].prompt, seed: newImgs[0].seed, href: this.paths.lastElement(), pathname: url }); - this._regenerateLoading = false; - this._regenInput = ''; - } - }); - }), - 'regenerate image' - )} + onClick={action(async () => { + this._regenerateLoading = true; + SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true).then(newImgs => { + if (newImgs[0]) { + const url = newImgs[0].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.dataDoc[this.fieldKey] = imgField; + this._prevImgs.unshift({ prompt: newImgs[0].prompt, seed: newImgs[0].seed, href: this.paths.lastElement(), pathname: url }); + this._regenerateLoading = false; + this._regenInput = ''; + } + }); + })} /> <Button // style={{ alignSelf: 'flex-end' }} diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index 5ebe2e358..af5fd727f 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -271,8 +271,8 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { this._lastInput.text = input; const dims = FireflyDimensionsMap[this._imgDims]; return Networking.PostToServer('/queryFireflyImage', { prompt: input, width: dims.width, height: dims.height, seed: seed }).then(img => { + const seed = img.accessPaths.agnostic.client.match(/\/(\d+)upload/)[1]; if (!changeInPlace) { - const seed = img.accessPaths.agnostic.client.match(/\/(\d+)upload/)[1]; const imgDoc: Doc = Docs.Create.ImageDocument(img.accessPaths.agnostic.client, { title: input.match(/^(.*?)~~~.*$/)?.[1] || input, nativeWidth: dims.width, |