From d0cbb43ec3fa19f76a570f5e0038bfc72c9f37b9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 7 May 2025 11:21:53 -0400 Subject: fixed dont_center on Docs in gridview. fixed ai view screentolocal in docView, and made ai view more streamlined. got rid of ai history sidebar in images. fixed imageeditor to use a mask that doesn't show the original conctents. fixed gptpopup scrolling, turning off of spinner, and improved ability to filter Docs --- src/client/views/ViewBoxInterface.ts | 1 - .../collectionGrid/CollectionGridView.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 14 +- src/client/views/nodes/DocumentView.scss | 3 +- src/client/views/nodes/DocumentView.tsx | 59 ++++---- src/client/views/nodes/IconTagBox.tsx | 24 ++-- src/client/views/nodes/ImageBox.scss | 2 +- src/client/views/nodes/ImageBox.tsx | 96 ++++--------- src/client/views/nodes/imageEditor/ImageEditor.tsx | 5 +- .../imageEditor/imageEditorUtils/ImageHandler.ts | 6 +- src/client/views/pdf/GPTPopup/GPTPopup.scss | 7 +- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 149 +++++++++++---------- src/client/views/smartdraw/SmartDrawHandler.tsx | 2 +- 13 files changed, 168 insertions(+), 202 deletions(-) (limited to 'src') diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts index 0ddac8914..d8dab8e89 100644 --- a/src/client/views/ViewBoxInterface.ts +++ b/src/client/views/ViewBoxInterface.ts @@ -64,5 +64,4 @@ export abstract class ViewBoxInterface

extends ObservableReactComponent boolean; // KeyValueBox's don't want to register their views isUnstyledView?: () => boolean; // SchemaView and KeyValue are unstyled -- not titles, no opacity, no animations componentAIView?: () => JSX.Element; - componentAIViewHistory?: () => JSX.Element; } diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 3320eacb6..b837b3a86 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -206,7 +206,7 @@ export class CollectionGridView extends CollectionSubView() { setContentViewBox={emptyFunction} whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} onClickScript={this.onChildClickHandler} - dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'} + dontCenter={StrCast(this.layoutDoc.layout_dontCenter, StrCast(childLayout.layout_dontCenter)) as 'x' | 'y' | 'xy'} showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)} /> ); 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 { - 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 { - 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 } @@ -171,7 +171,7 @@ export class CollectionFreeFormDocumentView extends DocComponent }) { 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(findexed); }); @@ -180,7 +180,7 @@ export class CollectionFreeFormDocumentView extends DocComponent }) { 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(findexed); }); @@ -204,15 +204,15 @@ export class CollectionFreeFormDocumentView extends DocComponent { 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 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) => (this._componentView = view)); @@ -678,7 +675,7 @@ export class DocumentViewInternal extends DocComponent (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 ( - <> -

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} -
-
this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}> -
- {this._componentView?.componentAIView?.() ?? null} - {this._props.DocumentView?.() ? : null} -
- +
this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}> +
+ {this._componentView?.componentAIView?.() ?? null} + {this._props.DocumentView?.() ? : null} +
); } @computed get tagsOverlay() { @@ -780,7 +765,6 @@ export class DocumentViewInternal extends DocComponent() { 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,9 +1482,13 @@ export class DocumentView extends DocComponent() { 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) */ @@ -1508,7 +1496,8 @@ export class DocumentView extends DocComponent() { 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 { const tag = StrCast(key.toolType); const color = dv._props.styleProvider?.(dv.layoutDoc, dv.ComponentView?._props, StyleProp.FontColor) as string; return ( - } - size={Size.XSMALL} - type={Type.PRIM} - onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))} - color={color} - /> +
+ {' '} + {/* tooltips require the wrapped item to be an element ref */} + } + size={Size.XSMALL} + type={Type.PRIM} + onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))} + color={color} + /> +
); }; 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() { @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() { @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined; @observable private _fireflyRefStrength = 0; - componentAIViewHistory = () => ( -
-
- ); - componentAIView = () => { - const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey])); return (
- - Firefly: - () {
-
-
- - Similarity - - this._canInteract && (this._fireflyRefStrength = val as number))} - valueLabelDisplay="auto" - /> +
+ this._canInteract && (this._fireflyRefStrength = val as number)), + `${this.Document.title} button set from list` + )} + fillWidth + /> +
); 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; }; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss index bb43291ee..f6fa45221 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.scss +++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss @@ -7,6 +7,11 @@ $highlightedText: #82e0ff; $inputHeight: 60px; $headingHeight: 32px; +.gptPopup-sortBox { + display: block; + max-height: calc(100% - 45px); // leave room for input +} + .gptPopup-summary-box { position: fixed; padding-left: 10px; @@ -87,6 +92,7 @@ $headingHeight: 32px; } .btns-wrapper-gpt { height: 100%; + width: 100%; display: flex; justify-content: center; align-items: center; @@ -97,7 +103,6 @@ $headingHeight: 32px; flex-direction: column; width: 100%; height: 100%; - overflow-y: auto; padding-right: 5px; } diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index c45d8e052..9fbae5c90 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -93,7 +93,7 @@ export class GPTPopup extends ObservableReactComponent { this.onGptResponse = (sortResult: string, questionType: GPTDocCommand, args?: string) => this.processGptResponse(selDoc, this._textToDocMap, sortResult, questionType, args); this.onQuizRandom = () => this.randomlyChooseDoc(selDoc.Document, hasChildDocs()); this._documentDescriptions = Promise.all(hasChildDocs().map(doc => - Doc.getDescription(doc).then(text => this._textToDocMap.set(text.trim(), doc) && `${DescriptionSeperator}${text}${DescriptionSeperator}`) + Doc.getDescription(doc).then(text => this._textToDocMap.set(text.replace(/\n/g, ' ').trim(), doc) && `${DescriptionSeperator}${text}${DescriptionSeperator}`) )).then(docDescriptions => docDescriptions.join()); // prettier-ignore } }, @@ -406,73 +406,80 @@ export class GPTPopup extends ObservableReactComponent { scrollToBottom = () => setTimeout(() => this._messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' }), 50); gptMenu = () => ( -
-
); callGpt = action((mode: GPTPopupMode) => { this.setGptProcessing(true); + const reset = action(() => { + this.setGptProcessing(false); + this._userPrompt = ''; + this._quizAnswer = ''; + }); switch (mode) { case GPTPopupMode.FIREFLY: this._fireflyArray.push(this._userPrompt); - return this.generateFireflyImage(this._userPrompt).then(action(() => (this._userPrompt = ''))); + return this.generateFireflyImage(this._userPrompt).then(reset); case GPTPopupMode.USER_PROMPT: this._conversationArray.push(this._userPrompt); - return this.generateUserPromptResponse(this._userPrompt).then(action(() => (this._userPrompt = ''))); + return this.generateUserPromptResponse(this._userPrompt).then(reset); case GPTPopupMode.QUIZ_RESPONSE: this._conversationArray.push(this._quizAnswer); - return this.generateQuizAnswerAnalysis(DocumentView.SelectedDocs().lastElement(), this._quizAnswer).then(action(() => (this._quizAnswer = ''))); + return this.generateQuizAnswerAnalysis(DocumentView.SelectedDocs().lastElement(), this._quizAnswer).then(reset); } }); @@ -490,18 +497,20 @@ export class GPTPopup extends ObservableReactComponent { }; gptUserInput = () => ( -
-
-
- {(this._mode === GPTPopupMode.FIREFLY ? this._fireflyArray : this._conversationArray).map((message, index) => ( -
- {message} -
- ))} - {this._gptProcessing &&
...
} -
+
+
+
+
+ {(this._mode === GPTPopupMode.FIREFLY ? this._fireflyArray : this._conversationArray).map((message, index) => ( +
+ {message} +
+ ))} + {this._gptProcessing &&
...
} +
-
+
+
); @@ -520,7 +529,7 @@ export class GPTPopup extends ObservableReactComponent { onChange={e => onChange(e.target.value)} onKeyDown={e => this.handleKeyPress(e, this._mode)} type="text" - style={{ color: SnappingManager.userColor }} + style={{ color: 'black' }} placeholder={placeholder} />