diff options
| author | bobzel <zzzman@gmail.com> | 2025-01-13 22:26:35 -0500 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2025-01-13 22:26:35 -0500 |
| commit | 12f5a435ee6476e2e07ded0c9cdd597c70ca8af0 (patch) | |
| tree | 258d3f999329bf94b5725fb5a04392aa007d6150 /src/client/views/nodes | |
| parent | c2312afe4ee969043ba3ce4cb9c275a48b0f1ee0 (diff) | |
changing ai for images and collections to work mostly the same way. fixes for document view/collection/images to keep things working when the ai editor view reduces the rendered document size. fixed so that freeform views overlaid on images/vieos/etc have the ui menu items of collections.
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/DocumentView.scss | 1 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 48 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.scss | 19 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 113 |
4 files changed, 111 insertions, 70 deletions
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index a3d47290a..8e7307d23 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -280,7 +280,6 @@ .documentView-editorView { width: 100%; - overflow-y: scroll; justify-items: center; background-color: rgb(223, 223, 223); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 640d27aa1..e37658ca5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -85,7 +85,7 @@ export interface DocumentViewProps extends FieldViewSharedProps { reactParent?: React.Component; // parent React component view (see CollectionFreeFormDocumentView) } @observer -export class DocumentViewInternal extends DocComponent<FieldViewProps & DocumentViewProps>() { +export class DocumentViewInternal extends DocComponent<FieldViewProps & DocumentViewProps & { showAIEditor: boolean }>() { // this makes mobx trace() statements more descriptive public get displayName() { return 'DocumentViewInternal(' + this.Document.title + ')'; } // prettier-ignore public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered. @@ -109,7 +109,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document private _mainCont = React.createRef<HTMLDivElement>(); private _titleRef = React.createRef<EditableView>(); private _dropDisposer?: DragManager.DragDropDisposer; - constructor(props: FieldViewProps & DocumentViewProps) { + constructor(props: FieldViewProps & DocumentViewProps & { showAIEditor: boolean }) { super(props); makeObservable(this); } @@ -122,12 +122,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document @observable _componentView: Opt<ViewBoxInterface<FieldViewProps>> = undefined; // needs to be accessed from DocumentView wrapper class @observable _animateScaleTime: Opt<number> = undefined; // milliseconds for animating between views. defaults to 300 if not uset @observable _animateScalingTo = 0; - @observable public _showAIEditor: boolean = false; - - @action - showAIEditor() { - this._showAIEditor = !this._showAIEditor; - } get _contentDiv() { return this._mainCont.current; } // prettier-ignore get _docView() { return this._props.DocumentView?.(); } // prettier-ignore @@ -689,7 +683,11 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document rootSelected = () => this._rootSelected; panelHeight = () => this._props.PanelHeight() - this.headerMargin; - screenToLocalContent = () => this._props.ScreenToLocalTransform().translate(0, -this.headerMargin); + screenToLocalContent = () => + this._props + .ScreenToLocalTransform() + .translate(0, -this.headerMargin) + .scale(this._props.showAIEditor ? (this._props.PanelHeight() || 1) / this.rph() : 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)); } // prettier-ignore setContentView = action((view: ViewBoxInterface<FieldViewProps>) => { this._componentView = view; }); // prettier-ignore @@ -715,9 +713,9 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document return this._props.styleProvider?.(doc, props, property); }; - @observable _aiWinHeight = 100; - rpw = () => this._props.PanelWidth() - this._aiWinHeight; - rph = () => this.panelHeight() - this._aiWinHeight; + @observable _aiWinHeight = 95; + rpw = () => (this.rph() * (this._props.NativeWidth?.() || 1)) / (this._props.NativeHeight?.() || 1); + rph = () => Math.max(10, this._props.PanelHeight() - this._aiWinHeight); @computed get viewBoxContents() { TraceMobx(); const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString; @@ -728,8 +726,8 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document className="documentView-contentsView" style={{ pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? (this._mounted ? 'all' : 'none'), - width: this._showAIEditor ? this.rpw() : undefined, - height: this._showAIEditor ? this.rph() : this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined, + width: this._props.showAIEditor ? this.rpw() : undefined, + height: this._props.showAIEditor ? this.rph() : this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined, }}> <DocumentContentsView {...this._props} @@ -737,8 +735,8 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document pointerEvents={this.contentPointerEvents} setContentViewBox={this.setContentView} childFilters={this.childFilters} - PanelHeight={this._showAIEditor ? this.rpw : this.panelHeight} - PanelWidth={this._showAIEditor ? this.rph : this._props.PanelWidth} + PanelWidth={this._props.showAIEditor ? this.rpw : this._props.PanelWidth} + PanelHeight={this._props.showAIEditor ? this.rph : this.panelHeight} setHeight={this.setHeight} isContentActive={this.isContentActive} ScreenToLocalTransform={this.screenToLocalContent} @@ -748,7 +746,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document hideClickBehaviors={BoolCast(this.Document.hideClickBehaviors)} /> </div> - {!this._showAIEditor ? null : ( + {!this._props.showAIEditor ? null : ( <> <div className="documentView-editorView-history" @@ -1168,10 +1166,13 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { @computed private get nativeScaling() { if (this.shouldNotScale) return 1; const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0; - if (this.layout_fitWidth || this._props.PanelHeight() / (this.effectiveNativeHeight || 1) > this._props.PanelWidth() / (this.effectiveNativeWidth || 1)) { - return Math.max(minTextScale, this._props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or layout_fitWidth + const ai = this._showAIEditor && this.nativeWidth === this.layoutDoc.width ? 95 : 0; + const effNW = Math.max(this.effectiveNativeWidth - ai, 1); + const effNH = Math.max(this.effectiveNativeHeight - ai, 1); + if (this.layout_fitWidth || (this._props.PanelHeight() - ai) / effNH > (this._props.PanelWidth() - ai) / effNW) { + return Math.max(minTextScale, (this._props.PanelWidth() - ai) / effNW); // width-limited or layout_fitWidth } - return Math.max(minTextScale, this._props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled + return Math.max(minTextScale, (this._props.PanelHeight() - ai) / effNH); // height-limited or unscaled } @computed private get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this._props.PanelWidth(); @@ -1231,7 +1232,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { } @computed get layout_fitWidth() { - return this._props.fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth; + return this._showAIEditor ? false : (this._props.fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth); } @computed get anchorViewDoc() { return this._props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.Document.link_anchor_2) : this._props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.Document.link_anchor_1) : undefined; @@ -1327,9 +1328,11 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { } }; + @observable public _showAIEditor: boolean = false; + @action public toggleAIEditor = () => { - this._docViewInternal && this._docViewInternal.showAIEditor(); + this._showAIEditor = !this._showAIEditor; }; public setTextHtmlOverlay = action((text: string | undefined, effect?: Doc) => { @@ -1516,6 +1519,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { }}> <DocumentViewInternal {...this._props} + showAIEditor={this._showAIEditor} reactParent={undefined} isHovering={this.isHovering} fieldKey={this.LayoutFieldKey} diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 1b6b3c85a..a50320f5e 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -40,6 +40,7 @@ max-height: 100%; pointer-events: inherit; background: transparent; + z-index: -10000; img { height: auto; @@ -159,6 +160,7 @@ .imageBox-aiView { text-align: center; font-weight: bold; + width: 100%; .imageBox-aiView-subtitle { position: relative; @@ -168,20 +170,29 @@ .imageBox-aiView-regenerate-container, .imageBox-aiView-options-container { font-weight: normal; - margin: 5px; display: flex; } .imageBox-aiView-regenerate, .imageBox-aiView-options { display: flex; - flex-direction: row; - justify-content: center; + align-items: center; flex-direction: row; gap: 5px; + width: 100%; } + .imageBox-aiView-strength { + text-align: center; + align-items: center; + display: flex; + width: 125px; + } + .imageBox-aiView-slider { + width: 50px; + margin-left: 5px; + } .imageBox-aiView-input { - width: 50%; + width: 100%; } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9838af4d0..9223fc180 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@mui/material'; +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'; @@ -44,6 +44,7 @@ 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'; export class ImageEditorData { // eslint-disable-next-line no-use-before-define @@ -98,7 +99,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @observable private _regenInput = ''; @observable private _canInteract = true; @observable private _regenerateLoading = false; - @observable private _prevImgs: FireflyImageData[] = []; + @observable private _prevImgs: FireflyImageData[] = StrCast(this.Document.ai_firefly_history) ? JSON.parse(StrCast(this.Document.ai_firefly_history)) : []; constructor(props: FieldViewProps) { super(props); @@ -386,8 +387,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { : UpdateIcon( this.layoutDoc[Id] + '_icon_' + new Date().getTime(), contentDiv, - usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width), - usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height), + usePanelDimensions || true ? this._props.PanelWidth() : NumCast(this.layoutDoc._width), + usePanelDimensions || true ? this._props.PanelHeight() : NumCast(this.layoutDoc._height), this._props.PanelWidth(), this._props.PanelHeight(), 0, @@ -543,65 +544,91 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { <div key={img.pathname}> <img className="imageBox-aiView-img" - src={img.href} + 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; }} /> - <text>{img.prompt}</text> + <span>{img.prompt}</span> </div> ))} </div> ); }; + @observable private _fireflyRefStrength = 0; componentAIView = () => { const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey])); - const showRegenerate = this.Document[DocData].ai; return ( <div className="imageBox-aiView"> - {showRegenerate && ( - <div className="imageBox-aiView-regenerate-container"> - <div className="imageBox-aiView-regenerate"> - <input - className="imageBox-aiView-input" - aria-label="Edit instructions input" - type="text" - value={this._regenInput} - onChange={action(e => this._canInteract && (this._regenInput = e.target.value))} - placeholder="Prompt (Optional)" - /> - <Button - text="Regenerate Image" - type={Type.SEC} - // 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; - 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._prevImgs.unshift({ prompt: newImgs[0].prompt, seed: newImgs[0].seed, href: this.paths.lastElement(), pathname: url }); - this.dataDoc.ai_firefly_history = `${this._prevImgs}`; - this.dataDoc[this.fieldKey] = imgField; - this._regenerateLoading = false; - this._regenInput = ''; - } - }); - })} + <div className="imageBox-aiView-regenerate-container"> + <div className="imageBox-aiView-regenerate"> + Firefly: + <input + className="imageBox-aiView-input" + aria-label="Edit instructions input" + type="text" + value={this._regenInput} + onChange={action(e => this._canInteract && (this._regenInput = e.target.value))} + placeholder={this._regenInput || StrCast(this.Document.title)} + /> + <div className="imageBox-aiView-strength"> + <span style={{ width: 60 }}>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" /> - <Button text="Get Variations" type={Type.SEC} iconPlacement="right" /> </div> + <Button + text="Create" + type={Type.SEC} + // 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))?.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 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.dataDoc.ai_firefly_history = JSON.stringify(this._prevImgs); + this.dataDoc.ai_firefly_prompt = newImgs[0].prompt; + this.dataDoc[this.fieldKey] = imgField; + this._regenerateLoading = false; + this._regenInput = ''; + } + }) + ); + })} + /> </div> - )} + </div> <div className="imageBox-aiView-options-container"> - {showRegenerate && <text className="imageBox-aiView-subtitle"> More Options: </text>} + <span className="imageBox-aiView-subtitle"> More Options: </span> <div className="imageBox-aiView-options"> <Button type={Type.TERT} |
