From 7e4d793eaa7e5b6b564355a11fa02a5611645f20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 21 Mar 2025 18:58:21 -0400
Subject: trying to improve how data / layout / root and templtae docs are
 accessed.

---
 src/client/views/smartdraw/DrawingFillHandler.tsx | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

(limited to 'src/client/views/smartdraw/DrawingFillHandler.tsx')

diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index c672bc718..f1d5f2cfe 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -1,6 +1,5 @@
 import { imageUrlToBase64 } from '../../../ClientUtils';
 import { Doc, StrListCast } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
 import { List } from '../../../fields/List';
 import { DocCast, ImageCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
@@ -15,8 +14,7 @@ import { AspectRatioLimits, FireflyDimensionsMap, FireflyImageDimensions, Firefl
 const DashDropboxId = '2m86iveqdr9vzsa';
 export class DrawingFillHandler {
     static drawingToImage = async (drawing: Doc, strength: number, user_prompt: string, styleDoc?: Doc) => {
-        const docData = drawing[DocData];
-        const tags = StrListCast(docData.tags).map(tag => tag.slice(1));
+        const tags = StrListCast(drawing.$tags).map(tag => tag.slice(1));
         const styles = tags.filter(tag => FireflyStylePresets.has(tag));
         const styleDocs = !Doc.Links(drawing).length
             ? styleDoc && !tags.length
@@ -47,7 +45,7 @@ export class DrawingFillHandler {
                         Networking.PostToServer('/queryFireflyImageFromStructure', { prompt: `${newPrompt}`, width: dims.width, height: dims.height, structureUrl, strength, presets: styles, styleUrl })
                             .then(res => {
                                 const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { _width: 400, _height: 400 });
-                                drawing[DocData].ai_firefly_generatedDocs = genratedDocs;
+                                drawing.$ai_firefly_generatedDocs = genratedDocs;
                                 (res as Upload.ImageInformation[]).map(info =>
                                     Doc.AddDocToList(
                                         genratedDocs,
-- 
cgit v1.2.3-70-g09d2


From 902780aa40b0234aeeeb3aca25af9fa553b3553c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Mar 2025 13:44:33 -0400
Subject: fixed tag label height.  fixed dropbox authorization for firefly.

---
 src/client/views/TagsView.tsx                     |  2 +-
 src/client/views/nodes/DocumentView.tsx           |  9 ++++++---
 src/client/views/smartdraw/DrawingFillHandler.tsx | 13 ++++++++-----
 3 files changed, 15 insertions(+), 9 deletions(-)

(limited to 'src/client/views/smartdraw/DrawingFillHandler.tsx')

diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index 7d2a6be5e..f1bcaac2c 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -357,7 +357,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                     <div className="tagsView-list">
                         {this._props.Views.length === 1 && !this.View.showTags ? null : ( //
                             <IconButton
-                                style={{ width: '8px' }}
+                                style={{ width: '8px', height: this._props.Views.lastElement().TagBtnHeight }}
                                 tooltip="Close Menu"
                                 onPointerDown={e =>
                                     setupMoveUpEvents(this, e, returnFalse, emptyFunction, upEv => {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4ea2c66f3..eae3454ba 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -719,7 +719,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
 
     @observable _aiWinHeight = 88;
 
-    private _tagsBtnHeight = 22;
+    TagsBtnHeight = 22;
     @computed get currentScale() {
         const viewXfScale = this._props.DocumentView!().screenToLocalScale();
         const x = NumCast(this.Document._height) / viewXfScale / 80;
@@ -735,11 +735,11 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
     /**
      * 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._tagsBtnHeight * this.viewScaling, 0.25 * Math.min(NumCast(this.Document._width), NumCast(this.Document._height))); } // prettier-ignore
+    @computed get maxWidgetSize() { return Math.min(this.TagsBtnHeight * this.viewScaling, 0.25 * Math.min(NumCast(this.Document._width), NumCast(this.Document._height))); } // 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.max(this.maxWidgetSize / this._tagsBtnHeight, 1) * Math.min(1, this.viewScaling); } // prettier-ignore
+    @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);
@@ -1271,6 +1271,9 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     public get ContentDiv()     { return this._docViewInternal?._contentDiv; } // prettier-ignore
     public get ComponentView()  { return this._docViewInternal?._componentView; } // prettier-ignore
     public get allLinks()       { return this._docViewInternal?._allLinks ?? []; } // prettier-ignore
+    public get TagBtnHeight() {
+        return this._docViewInternal?.TagsBtnHeight;
+    }
 
     get LayoutFieldKey() {
         return Doc.LayoutFieldKey(this.Document, this._props.LayoutTemplateString);
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index f1d5f2cfe..34cc4f0e1 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -1,7 +1,7 @@
 import { imageUrlToBase64 } from '../../../ClientUtils';
 import { Doc, StrListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
-import { DocCast, ImageCast } from '../../../fields/Types';
+import { DocCast, ImageCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { Upload } from '../../../server/SharedMediaTypes';
 import { gptDescribeImage } from '../../apis/gpt/GPT';
@@ -44,7 +44,12 @@ export class DrawingFillHandler {
                     .then((prompt, newPrompt = user_prompt || prompt) =>
                         Networking.PostToServer('/queryFireflyImageFromStructure', { prompt: `${newPrompt}`, width: dims.width, height: dims.height, structureUrl, strength, presets: styles, styleUrl })
                             .then(res => {
-                                const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { _width: 400, _height: 400 });
+                                const error = ('error' in res && (res.error as string)) || '';
+                                if (error.includes('Dropbox') && confirm('Create image failed. Try authorizing DropBox?\r\n' + error.replace(/^[^"]*/, ''))) {
+                                    window.open(`https://www.dropbox.com/oauth2/authorize?client_id=${DashDropboxId}&response_type=code&token_access_type=offline&redirect_uri=http://localhost:1050/refreshDropbox`, '_blank')?.focus();
+                                    return;
+                                }
+                                const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 });
                                 drawing.$ai_firefly_generatedDocs = genratedDocs;
                                 (res as Upload.ImageInformation[]).map(info =>
                                     Doc.AddDocToList(
@@ -69,9 +74,7 @@ export class DrawingFillHandler {
                                 if (!DocumentView.getFirstDocumentView(genratedDocs)) DocumentViewInternal.addDocTabFunc(genratedDocs, OpenWhere.addRight);
                             })
                             .catch(e => {
-                                if (e.toString().includes('Dropbox') && confirm('Create image failed. Try authorizing DropBox?\r\n' + e.toString().replace(/^[^"]*/, ''))) {
-                                    window.open(`https://www.dropbox.com/oauth2/authorize?client_id=${DashDropboxId}&response_type=code&token_access_type=offline&redirect_uri=http://localhost:1050/refreshDropbox`, '_blank')?.focus();
-                                } else alert(e.toString());
+                                alert(e.toString());
                             })
                     ); // prettier-ignore:q
             }
-- 
cgit v1.2.3-70-g09d2


From 163b0d9d54d9477792e1b7cdc64bbcb5d2897b4f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Mar 2025 14:22:14 -0400
Subject: fixed drawingfill regeneration when dropping an image on another to
 honor the style of the dropped image first, then the linked images, then the
 source image.

---
 src/client/views/nodes/ImageBox.tsx               | 32 ++++++++++++-----------
 src/client/views/smartdraw/DrawingFillHandler.tsx | 24 +++++++++--------
 2 files changed, 30 insertions(+), 26 deletions(-)

(limited to 'src/client/views/smartdraw/DrawingFillHandler.tsx')

diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c737623f5..d89fe0bd4 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -217,7 +217,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     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)));
+                    DrawingFillHandler.drawingToImage(this.Document, 90, 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];
@@ -505,20 +505,22 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     }
     @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>
+            <Tooltip title={'click to show AI generations.  Drop an image on to create a new generation'}>
+                <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>
+            </Tooltip>
         );
     }
 
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index 34cc4f0e1..328bf3487 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -16,17 +16,19 @@ export class DrawingFillHandler {
     static drawingToImage = async (drawing: Doc, strength: number, user_prompt: string, styleDoc?: Doc) => {
         const tags = StrListCast(drawing.$tags).map(tag => tag.slice(1));
         const styles = tags.filter(tag => FireflyStylePresets.has(tag));
-        const styleDocs = !Doc.Links(drawing).length
-            ? styleDoc && !tags.length
-                ? [styleDoc]
-                : []
-            : Doc.Links(drawing)
-                  .map(link => Doc.getOppositeAnchor(link, drawing))
-                  .map(anchor => anchor && DocCast(anchor.embedContainer));
-        const styleUrl = await DocumentView.GetDocImage(styleDocs.filter(doc => doc?.data instanceof ImageField).lastElement())?.then(styleImg => {
-            const hrefParts = ImageCast(styleImg).url.href.split('.');
-            return `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`;
-        });
+        const styleDocs = [drawing].concat(
+            ...Doc.Links(drawing)
+                .map(link => Doc.getOppositeAnchor(link, drawing))
+                .map(anchor => DocCast(anchor?.annotationOn, anchor))
+                .map(anchor => anchor!),
+            ...(styleDoc ? [styleDoc] : [])
+        );
+        const styleUrl = tags.length
+            ? undefined
+            : await DocumentView.GetDocImage(styleDocs.filter(doc => doc?.data instanceof ImageField).lastElement())?.then(styleImg => {
+                  const hrefParts = ImageCast(styleImg).url.href.split('.');
+                  return `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`;
+              });
         return DocumentView.GetDocImage(drawing)?.then(imageField => {
             if (imageField) {
                 const aspectRatio = (drawing.width as number) / (drawing.height as number);
-- 
cgit v1.2.3-70-g09d2


From 960357bce915d1b8246d2291fd82fbebcf6cd769 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 31 Mar 2025 23:16:24 -0400
Subject: fixed zooming on image boxes with fades.  fixed style ref for image
 regen to use self las, after links.

---
 src/client/views/nodes/ImageBox.tsx               | 4 ++--
 src/client/views/smartdraw/DrawingFillHandler.tsx | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

(limited to 'src/client/views/smartdraw/DrawingFillHandler.tsx')

diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 030473be4..dc4b79983 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -650,7 +650,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                             onClick={action(async () => {
                                 this._regenerateLoading = true;
                                 if (this._fireflyRefStrength) {
-                                    DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title), this.Document)?.then(action(() => (this._regenerateLoading = false)));
+                                    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 => {
@@ -772,7 +772,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 <CollectionFreeFormView
                     ref={this._ffref}
                     {...this._props}
-                    Document={doc}
+                    Document={this.Document}
                     setContentViewBox={emptyFunction}
                     NativeWidth={returnZero}
                     NativeHeight={returnZero}
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index 328bf3487..d8ef8308d 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -17,6 +17,7 @@ export class DrawingFillHandler {
         const tags = StrListCast(drawing.$tags).map(tag => tag.slice(1));
         const styles = tags.filter(tag => FireflyStylePresets.has(tag));
         const styleDocs = [drawing].concat(
+            drawing,
             ...Doc.Links(drawing)
                 .map(link => Doc.getOppositeAnchor(link, drawing))
                 .map(anchor => DocCast(anchor?.annotationOn, anchor))
-- 
cgit v1.2.3-70-g09d2


From ede7aaa19d903a7e55fc60d9bc213988ea602c97 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 3 Apr 2025 14:15:15 -0400
Subject: fixed image box button sizing. made drawing ai regen a little faster
 by not creating icons when there are no annotqations.

---
 src/client/apis/gpt/GPT.ts                        |  5 +-
 src/client/views/nodes/DocumentView.tsx           |  2 +-
 src/client/views/nodes/ImageBox.scss              |  7 +--
 src/client/views/nodes/ImageBox.tsx               | 23 ++++---
 src/client/views/smartdraw/DrawingFillHandler.tsx | 75 +++++++++++------------
 src/server/ApiManagers/FireflyManager.ts          |  1 -
 src/server/ApiManagers/UploadManager.ts           |  7 +--
 src/server/DashUploadUtils.ts                     | 14 ++---
 8 files changed, 62 insertions(+), 72 deletions(-)

(limited to 'src/client/views/smartdraw/DrawingFillHandler.tsx')

diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 29b6ab989..140aebfe0 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -1,5 +1,6 @@
 import { ChatCompletionMessageParam, Image } from 'openai/resources';
 import { openai } from './setup';
+import { imageUrlToBase64 } from '../../../ClientUtils';
 
 export enum GPTDocCommand {
     AssignTags = 1,
@@ -310,7 +311,9 @@ const gptHandwriting = async (src: string): Promise<string> => {
     }
 };
 
-const gptDescribeImage = async (image: string): Promise<string> => {
+const gptDescribeImage = async (userPrompt: string, url: string): Promise<string> => {
+    if (userPrompt) return userPrompt;
+    const image = imageUrlToBase64(url);
     try {
         const response = await openai.chat.completions.create({
             model: 'gpt-4o',
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 070a13103..37f888ddd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -702,7 +702,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
      */
     @computed get viewScaling() { return 1 / this.currentScale; } // 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.
+     * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its nominal pixel size.
      */
     @computed get maxWidgetSize() { return Math.min(this.TagsBtnHeight * this.viewScaling, 0.25 * Math.min(NumCast(this.Document._width), NumCast(this.Document._height))); } // prettier-ignore
     /**
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 36abf707c..4a6e8eb49 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -142,11 +142,8 @@
     }
 }
 .imageBox-regenerateDropTarget {
-    right: 30;
-    border-radius: 50%;
-    svg {
-        border-radius: 50%;
-    }
+    right: 35;
+    transform-origin: 70px 35px;
 }
 
 .imageBox-fader img {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 75c298dc6..2b9a78596 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -45,7 +45,6 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { FocusViewOptions } from './FocusViewOptions';
 import './ImageBox.scss';
 import { OpenWhere } from './OpenWhere';
-import { RichTextField } from '../../../fields/RichTextField';
 import { ComputedField } from '../../../fields/ScriptField';
 
 const DefaultPath = '/assets/unknown-file-icon-hi.png';
@@ -218,9 +217,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     this._regenerateLoading = true;
                     const drag = de.complete.docDragData.draggedDocuments.lastElement();
                     const dragField = drag[Doc.LayoutFieldKey(drag)];
+                    const descText = RTFCast(dragField)?.Text || StrCast(dragField) || RTFCast(drag.text)?.Text || StrCast(drag.text) || StrCast(this.Document.title);
                     const oldPrompt = StrCast(this.Document.ai_firefly_prompt, StrCast(this.Document.title));
                     const newPrompt = (text: string) => (oldPrompt ? `${oldPrompt} ~~~ ${text}` : text);
-                    DrawingFillHandler.drawingToImage(this.Document, 90, newPrompt(dragField instanceof RichTextField ? dragField.Text : ''), drag)?.then(action(() => (this._regenerateLoading = false)));
+                    DrawingFillHandler.drawingToImage(this.Document, 90, newPrompt(descText), drag)?.then(action(() => (this._regenerateLoading = false)));
                     added = false;
                 } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
                     const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
@@ -401,13 +401,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     // updateIcon = () => new Promise<void>(res => res());
-    updateIcon = (/* usePanelDimensions?: boolean */) => {
-        const contentDiv = this._mainCont;
-        return !contentDiv
+    updateIcon = (/* usePanelDimensions?: boolean */) =>
+        !this._mainCont || !DocListCast(this.dataDoc[this.annotationKey]).length
             ? new Promise<void>(res => res())
             : UpdateIcon(
                   this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
-                  contentDiv,
+                  this._mainCont,
                   this._props.PanelWidth(), // usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
                   this._props.PanelHeight(), // usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
                   this._props.PanelWidth(),
@@ -422,7 +421,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                       this.dataDoc.icon_nativeHeight = nativeHeight;
                   }
               );
-    };
 
     choosePath = (url: URL) => {
         if (!url?.href) return '';
@@ -453,15 +451,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     /**
      * 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
+    @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
+    @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth, 0.2 * this._props.PanelWidth())*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 uiBtnScaling() { return Math.min(1/(this._props.NativeDimScaling?.()??1), this.maxWidgetSize / this._sideBtnWidth); } // prettier-ignore
 
     @computed get overlayImageIcon() {
         const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`];
@@ -518,10 +516,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         transform: `scale(${this.uiBtnScaling})`,
                         width: this._sideBtnWidth,
                         height: this._sideBtnWidth,
-                        background: 'transparent',
+                        background: 'black',
+                        color: 'white',
                         // color: SettingsManager.userBackgroundColor,
                     }}>
-                    {this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width="100%" height="100%" /> : <FontAwesomeIcon icon="portrait" color={SettingsManager.userColor} size="lg" />}
+                    {this._regenerateLoading ? <ReactLoading type="spin" width="100%" height="100%" /> : <FontAwesomeIcon icon="portrait" size="lg" />}
                 </div>
             </Tooltip>
         );
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index d8ef8308d..a91ec23b8 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -1,4 +1,3 @@
-import { imageUrlToBase64 } from '../../../ClientUtils';
 import { Doc, StrListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { DocCast, ImageCast, StrCast } from '../../../fields/Types';
@@ -42,44 +41,42 @@ export class DrawingFillHandler {
                 const { href } = ImageCast(imageField).url;
                 const hrefParts = href.split('.');
                 const structureUrl = `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`;
-                return imageUrlToBase64(structureUrl)
-                    .then(gptDescribeImage)
-                    .then((prompt, newPrompt = user_prompt || prompt) =>
-                        Networking.PostToServer('/queryFireflyImageFromStructure', { prompt: `${newPrompt}`, width: dims.width, height: dims.height, structureUrl, strength, presets: styles, styleUrl })
-                            .then(res => {
-                                const error = ('error' in res && (res.error as string)) || '';
-                                if (error.includes('Dropbox') && confirm('Create image failed. Try authorizing DropBox?\r\n' + error.replace(/^[^"]*/, ''))) {
-                                    window.open(`https://www.dropbox.com/oauth2/authorize?client_id=${DashDropboxId}&response_type=code&token_access_type=offline&redirect_uri=http://localhost:1050/refreshDropbox`, '_blank')?.focus();
-                                    return;
-                                }
-                                const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 });
-                                drawing.$ai_firefly_generatedDocs = genratedDocs;
-                                (res as Upload.ImageInformation[]).map(info =>
-                                    Doc.AddDocToList(
-                                        genratedDocs,
-                                        undefined,
-                                        Docs.Create.ImageDocument(info.accessPaths.agnostic.client, {
-                                            ai: 'firefly',
-                                            tags: new List<string>(['@ai']),
-                                            title: newPrompt,
-                                            _data_usePath: 'alternate:hover',
-                                            data_alternates: new List<Doc>([drawing]),
-                                            ai_firefly_prompt: newPrompt,
-                                            _width: 500,
-                                            data_nativeWidth: info.nativeWidth,
-                                            data_nativeHeight: info.nativeHeight,
-                                        }),
-                                        undefined,
-                                        undefined,
-                                        true
-                                    )
-                                );
-                                if (!DocumentView.getFirstDocumentView(genratedDocs)) DocumentViewInternal.addDocTabFunc(genratedDocs, OpenWhere.addRight);
-                            })
-                            .catch(e => {
-                                alert(e.toString());
-                            })
-                    ); // prettier-ignore:q
+                return gptDescribeImage(user_prompt, structureUrl).then(newPrompt =>
+                    Networking.PostToServer('/queryFireflyImageFromStructure', { prompt: `${newPrompt}`, width: dims.width, height: dims.height, structureUrl, strength, presets: styles, styleUrl })
+                        .then(res => {
+                            const error = ('error' in res && (res.error as string)) || '';
+                            if (error.includes('Dropbox') && confirm('Create image failed. Try authorizing DropBox?\r\n' + error.replace(/^[^"]*/, ''))) {
+                                window.open(`https://www.dropbox.com/oauth2/authorize?client_id=${DashDropboxId}&response_type=code&token_access_type=offline&redirect_uri=http://localhost:1050/refreshDropbox`, '_blank')?.focus();
+                                return;
+                            }
+                            const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 });
+                            drawing.$ai_firefly_generatedDocs = genratedDocs;
+                            (res as Upload.ImageInformation[]).map(info =>
+                                Doc.AddDocToList(
+                                    genratedDocs,
+                                    undefined,
+                                    Docs.Create.ImageDocument(info.accessPaths.agnostic.client, {
+                                        ai: 'firefly',
+                                        tags: new List<string>(['@ai']),
+                                        title: newPrompt,
+                                        _data_usePath: 'alternate:hover',
+                                        data_alternates: new List<Doc>([drawing]),
+                                        ai_firefly_prompt: newPrompt,
+                                        _width: 500,
+                                        data_nativeWidth: info.nativeWidth,
+                                        data_nativeHeight: info.nativeHeight,
+                                    }),
+                                    undefined,
+                                    undefined,
+                                    true
+                                )
+                            );
+                            if (!DocumentView.getFirstDocumentView(genratedDocs)) DocumentViewInternal.addDocTabFunc(genratedDocs, OpenWhere.addRight);
+                        })
+                        .catch(e => {
+                            alert(e.toString());
+                        })
+                ); // prettier-ignore:q
             }
         });
     };
diff --git a/src/server/ApiManagers/FireflyManager.ts b/src/server/ApiManagers/FireflyManager.ts
index e75ede9df..07428798c 100644
--- a/src/server/ApiManagers/FireflyManager.ts
+++ b/src/server/ApiManagers/FireflyManager.ts
@@ -117,7 +117,6 @@ export default class FireflyManager extends ApiManager {
     generateImage = (prompt: string = 'a realistic illustration of a cat coding', width: number = 2048, height: number = 2048, seed?: number) => {
         let body = `{  "prompt": "${prompt}", "size": { "width": ${width}, "height": ${height}} }`;
         if (seed) {
-            console.log('RECEIVED SEED', seed);
             body = `{  "prompt": "${prompt}", "size": { "width": ${width}, "height": ${height}}, "seeds": [${seed}]}`;
         }
         const fetched = this.getBearerToken().then(response =>
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index c9d5df547..7c55e4a42 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -126,11 +126,8 @@ export default class UploadManager extends ApiManager {
             secureHandler: async ({ req, res }) => {
                 const { sources } = req.body;
                 if (Array.isArray(sources)) {
-                    const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
-                    res.send(results);
-                    return;
-                }
-                res.send();
+                    res.send(await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source))));
+                } else res.send();
             },
         });
 
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index a2747257a..ed109d8f7 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -450,14 +450,12 @@ export namespace DashUploadUtils {
      * 3) the size of the image, in bytes (4432130)
      * 4) the content type of the image, i.e. image/(jpeg | png | ...)
      */
-    export const UploadImage = async (source: string, filename?: string, prefix: string = ''): Promise<Upload.ImageInformation | Error> => {
-        const result = await InspectImage(source);
-        if (result instanceof Error) {
-            return { name: result.name, message: result.message };
-        }
-        const outputFile = filename || result.filename || '';
-        return UploadInspectedImage(result, outputFile, prefix, isLocal().exec(source) || source.startsWith('data:') ? true : false);
-    };
+    export const UploadImage = (source: string, filename?: string, prefix: string = ''): Promise<Upload.ImageInformation | Error> =>
+        InspectImage(source).then(async result =>
+            result instanceof Error
+                ? ({ name: result.name, message: result.message } as Error) //
+                : UploadInspectedImage(result, filename || result.filename || '', prefix, isLocal().exec(source) || source.startsWith('data:') ? true : false)
+        );
 
     type md5 = 'md5';
     type falsetype = false;
-- 
cgit v1.2.3-70-g09d2


From 49c571cb3f8db0732ac9b461890af2c09722c71b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 16 Apr 2025 19:53:26 -0400
Subject: fixed authorizing dropbox.

---
 src/client/Network.ts                             |   4 +-
 src/client/util/request-image-size.ts             |  38 +++--
 src/client/views/smartdraw/DrawingFillHandler.tsx |   8 +-
 src/server/ApiManagers/FireflyManager.ts          | 164 ++++++++++------------
 src/server/DashUploadUtils.ts                     |   7 +-
 5 files changed, 101 insertions(+), 120 deletions(-)

(limited to 'src/client/views/smartdraw/DrawingFillHandler.tsx')

diff --git a/src/client/Network.ts b/src/client/Network.ts
index a2ecf1bea..b11dcb379 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -21,10 +21,10 @@ export namespace Networking {
                 'Content-Type': 'application/json',
             },
             body: body ? JSON.stringify(body) : undefined,
-        }).then(async response => {
+        }).then(response => {
             if (response.ok) return response.json() as object;
 
-            return await response.text().then(text => ({ error: '' + response.status + ':' + response.statusText + '-' + text }));
+            return response.text().then(text => ({ error: '' + response.status + ':' + response.statusText + '-' + text }));
         });
     }
 
diff --git a/src/client/util/request-image-size.ts b/src/client/util/request-image-size.ts
index 32ab23618..798390c7d 100644
--- a/src/client/util/request-image-size.ts
+++ b/src/client/util/request-image-size.ts
@@ -33,27 +33,25 @@ export function requestImageSize(url: string): Promise<ISizeCalculationResult> {
 
             res.on('data', chunk => {
                 buffer = Buffer.concat([buffer, chunk]);
-            });
-
-            res.on('error', reject);
-
-            res.on('end', () => {
-                try {
-                    size = imageSize(buffer);
-                    if (size) {
-                        resolve(size);
-                        req.abort();
+            })
+                .on('error', reject)
+                .on('end', () => {
+                    try {
+                        size = imageSize(buffer);
+                        if (size) {
+                            resolve(size);
+                            req.abort();
+                        }
+                    } catch (err) {
+                        /* empty */
+                        console.log('Error: ', err);
+                    }
+                    if (!size) {
+                        reject(new Error('Image has no size'));
+                        return;
                     }
-                } catch (err) {
-                    /* empty */
-                    console.log('Error: ', err);
-                }
-                if (!size) {
-                    reject(new Error('Image has no size'));
-                    return;
-                }
-                resolve(size);
-            });
+                    resolve(size);
+                });
         });
 
         req.on('error', reject);
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index a91ec23b8..b0945fd83 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -26,11 +26,12 @@ export class DrawingFillHandler {
         const styleUrl = tags.length
             ? undefined
             : await DocumentView.GetDocImage(styleDocs.filter(doc => doc?.data instanceof ImageField).lastElement())?.then(styleImg => {
-                  const hrefParts = ImageCast(styleImg).url.href.split('.');
-                  return `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`;
+                  const hrefParts = ImageCast(styleImg)?.url.href.split('.');
+                  return !hrefParts ? undefined : `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`;
               });
         return DocumentView.GetDocImage(drawing)?.then(imageField => {
-            if (imageField) {
+            const href = ImageCast(imageField)?.url.href;
+            if (href) {
                 const aspectRatio = (drawing.width as number) / (drawing.height as number);
                 const dims = (() => {
                     if (aspectRatio > AspectRatioLimits[FireflyImageDimensions.Widescreen]) return FireflyDimensionsMap[FireflyImageDimensions.Widescreen];
@@ -38,7 +39,6 @@ export class DrawingFillHandler {
                     if (aspectRatio < AspectRatioLimits[FireflyImageDimensions.Portrait]) return FireflyDimensionsMap[FireflyImageDimensions.Portrait];
                     return FireflyDimensionsMap[FireflyImageDimensions.Square];
                 })();
-                const { href } = ImageCast(imageField).url;
                 const hrefParts = href.split('.');
                 const structureUrl = `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`;
                 return gptDescribeImage(user_prompt, structureUrl).then(newPrompt =>
diff --git a/src/server/ApiManagers/FireflyManager.ts b/src/server/ApiManagers/FireflyManager.ts
index 07428798c..d22142d7d 100644
--- a/src/server/ApiManagers/FireflyManager.ts
+++ b/src/server/ApiManagers/FireflyManager.ts
@@ -70,49 +70,42 @@ export default class FireflyManager extends ApiManager {
         );
 
     uploadImageToDropbox = (fileUrl: string, user: DashUserModel | undefined, dbx = new Dropbox({ accessToken: user?.dropboxToken || '' })) =>
-        new Promise<string | Error>((res, rej) =>
+        new Promise<string>((resolve, reject) => {
             fs.readFile(path.join(filesDirectory, `${Directory.images}/${path.basename(fileUrl)}`), undefined, (err, contents) => {
                 if (err) {
-                    console.log('Error: ', err);
-                    rej();
-                } else {
-                    dbx.filesUpload({ path: `/Apps/browndash/${path.basename(fileUrl)}`, contents })
-                        .then(response => {
-                            dbx.filesGetTemporaryLink({ path: response.result.path_display ?? '' })
-                                .then(link => res(link.result.link))
-                                .catch(e => res(new Error(e.toString())));
-                        })
-                        .catch(e => {
-                            if (user?.dropboxRefresh) {
-                                console.log('*********** try refresh dropbox for: ' + user.email + ' ***********');
-                                this.refreshDropboxToken(user).then(token => {
-                                    if (!token) {
-                                        console.log('Dropbox error: cannot refresh token');
-                                        res(new Error(e.toString()));
-                                    } else {
-                                        const dbxNew = new Dropbox({ accessToken: user.dropboxToken || '' });
-                                        dbxNew
-                                            .filesUpload({ path: `/Apps/browndash/${path.basename(fileUrl)}`, contents })
-                                            .then(response => {
-                                                dbxNew
-                                                    .filesGetTemporaryLink({ path: response.result.path_display ?? '' })
-                                                    .then(link => res(link.result.link))
-                                                    .catch(linkErr => res(new Error(linkErr.toString())));
-                                            })
-                                            .catch(uploadErr => {
-                                                console.log('Dropbox error:', uploadErr);
-                                                res(new Error(uploadErr.toString()));
-                                            });
-                                    }
-                                });
-                            } else {
-                                console.log('Dropbox error:', e);
-                                res(new Error(e.toString()));
-                            }
-                        });
+                    return reject(new Error('Error reading file:' + err.message));
                 }
-            })
-        );
+
+                const uploadToDropbox = (dropboxClient: Dropbox) =>
+                    dropboxClient
+                        .filesUpload({ path: `/Apps/browndash/${path.basename(fileUrl)}`, contents })
+                        .then(response =>
+                            dropboxClient
+                                .filesGetTemporaryLink({ path: response.result.path_display ?? '' })
+                                .then(link => resolve(link.result.link))
+                                .catch(linkErr => reject(new Error('Failed to get temporary link: ' + linkErr.message)))
+                        )
+                        .catch(uploadErr => reject(new Error('Failed to upload file to Dropbox: ' + uploadErr.message)));
+
+                uploadToDropbox(dbx).catch(e => {
+                    if (user?.dropboxRefresh) {
+                        console.log('Attempting to refresh Dropbox token for user:', user.email);
+                        this.refreshDropboxToken(user)
+                            .then(token => {
+                                if (!token) {
+                                    return reject(new Error('Failed to refresh Dropbox token.' + user.email));
+                                }
+
+                                const dbxNew = new Dropbox({ accessToken: token });
+                                uploadToDropbox(dbxNew).catch(finalErr => reject(new Error('Failed to refresh Dropbox token:' + finalErr.message)));
+                            })
+                            .catch(refreshErr => reject(new Error('Failed to refresh Dropbox token: ' + refreshErr.message)));
+                    } else {
+                        reject(new Error('Dropbox error: ' + e.message));
+                    }
+                });
+            });
+        });
 
     generateImage = (prompt: string = 'a realistic illustration of a cat coding', width: number = 2048, height: number = 2048, seed?: number) => {
         let body = `{  "prompt": "${prompt}", "size": { "width": ${width}, "height": ${height}} }`;
@@ -289,43 +282,36 @@ export default class FireflyManager extends ApiManager {
         register({
             method: Method.POST,
             subscription: '/queryFireflyImageFromStructure',
-            secureHandler: ({ req, res }) =>
-                new Promise<void>(resolver => {
-                    (req.body.styleUrl ? this.uploadImageToDropbox(req.body.styleUrl, req.user as DashUserModel) : Promise.resolve(undefined))
-                        .then(styleUrl => {
-                            if (styleUrl instanceof Error) {
-                                _invalid(res, styleUrl.message);
-                                throw new Error('Error uploading images to dropbox');
-                            }
-                            this.uploadImageToDropbox(req.body.structureUrl, req.user as DashUserModel)
-                                .then(dropboxStructureUrl => {
-                                    if (dropboxStructureUrl instanceof Error) {
-                                        _invalid(res, dropboxStructureUrl.message);
-                                        throw new Error('Error uploading images to dropbox');
-                                    }
-                                    return { styleUrl, structureUrl: dropboxStructureUrl };
-                                })
-                                .then(uploads =>
-                                    this.generateImageFromStructure(req.body.prompt, req.body.width, req.body.height, uploads.structureUrl, req.body.strength, req.body.presets, uploads.styleUrl)
-                                        .then(images => {
-                                            Promise.all((images ?? [new Error('no images were generated')]).map(fire => (fire instanceof Error ? fire : DashUploadUtils.UploadImage(fire.url))))
-                                                .then(dashImages => {
-                                                    if (dashImages.every(img => img instanceof Error)) _invalid(res, dashImages[0]!.message);
-                                                    else _success(res, JSON.stringify(dashImages.filter(img => !(img instanceof Error))));
-                                                })
-                                                .then(resolver);
-                                        })
-                                        .catch(e => {
-                                            _invalid(res, e.message);
-                                            resolver();
-                                        })
-                                );
-                        })
-                        .catch(() => {
-                            /* do nothing */
-                            resolver();
-                        });
-                }),
+            secureHandler: ({ req, res }) => 
+                new Promise<void>(resolver =>
+                    (req.body.styleUrl 
+                        ? this.uploadImageToDropbox(req.body.styleUrl, req.user as DashUserModel) 
+                        : Promise.resolve(undefined)
+                    )
+                    .then(styleUrl => 
+                         this.uploadImageToDropbox(req.body.structureUrl, req.user as DashUserModel)
+                            .then(dropboxStructureUrl => 
+                                 ({ styleUrl, structureUrl: dropboxStructureUrl })
+                            )
+                            
+                    )
+                    .then(uploads =>
+                        this.generateImageFromStructure(
+                            req.body.prompt, req.body.width, req.body.height, uploads.structureUrl, req.body.strength, req.body.presets, uploads.styleUrl
+                        ).then(images =>
+                            Promise.all((images ?? [new Error('no images were generated')]).map(fire => (fire instanceof Error ? fire : DashUploadUtils.UploadImage(fire.url))))
+                            .then(dashImages =>
+                               (dashImages.every(img => img instanceof Error))
+                                ? _invalid(res, dashImages[0]!.message)
+                                : _success(res, JSON.stringify(dashImages.filter(img => !(img instanceof Error))))
+                            )
+                        )
+                    )
+                    .catch(e => {
+                        _invalid(res, e.message);
+                        resolver();
+                    })
+            ), // prettier-ignore
         });
         register({
             method: Method.POST,
@@ -357,18 +343,18 @@ export default class FireflyManager extends ApiManager {
             method: Method.POST,
             subscription: '/expandImage',
             secureHandler: ({ req, res }) =>
-                this.uploadImageToDropbox(req.body.file, req.user as DashUserModel).then(uploadUrl =>
-                    uploadUrl instanceof Error
-                        ? _invalid(res, uploadUrl.message)
-                        : this.expandImage(uploadUrl, req.body.prompt).then(text => {
-                              if (text.error_code) _error(res, text.message);
-                              else
-                                  DashUploadUtils.UploadImage(text.outputs[0].image.url).then(info => {
-                                      if (info instanceof Error) _invalid(res, info.message);
-                                      else _success(res, info);
-                                  });
-                          })
-                ),
+                this.uploadImageToDropbox(req.body.file, req.user as DashUserModel)
+                    .then(uploadUrl =>
+                        this.expandImage(uploadUrl, req.body.prompt).then(text => {
+                            if (text.error_code) _error(res, text.message);
+                            else
+                                DashUploadUtils.UploadImage(text.outputs[0].image.url).then(info => {
+                                    if (info instanceof Error) _invalid(res, info.message);
+                                    else _success(res, info);
+                                });
+                        })
+                    )
+                    .catch(e => _invalid(res, e.message)),
         });
 
         // construct this url and send user to it.  It will allow them to authorize their dropbox account and will send the resulting token to our endpoint /refreshDropbox
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index ed109d8f7..f76371b0d 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -221,9 +221,7 @@ export namespace DashUploadUtils {
     const parseExifData = async (source: string) => {
         const image = await request.get(source, { encoding: null });
         const { /* data, */ error } = await new Promise<{ data: ExifData; error: string | undefined }>(resolve => {
-            new ExifImage({ image }, (exifError, data) => {
-                resolve({ data, error: exifError?.message });
-            });
+            new ExifImage({ image }, (exifError, data) => resolve({ data, error: exifError?.message }));
         });
         return error ? { data: undefined, error } : { data: await exifr.parse(image), error };
     };
@@ -295,7 +293,7 @@ export namespace DashUploadUtils {
 
         try {
             // Compute the native width and height ofthe image with an npm module
-            const { width: nativeWidth, height: nativeHeight } = await requestImageSize(resolvedUrl);
+            const { width: nativeWidth, height: nativeHeight } = await requestImageSize(resolvedUrl).catch(() => ({ width: 0, height: 0 }));
             // Bundle up the information into an object
             return {
                 source,
@@ -307,7 +305,6 @@ export namespace DashUploadUtils {
                 ...results,
             };
         } catch (e: unknown) {
-            console.log(e);
             return new Error(e ? e.toString?.() : 'unkown error');
         }
     };
-- 
cgit v1.2.3-70-g09d2


From 0d758430301d934a465ea090abf41f4a596a146c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 23 Apr 2025 15:29:26 -0400
Subject: fixed outpainting to get authorization for dropbox

---
 src/client/documents/Documents.ts                 | 63 ++++++-----------------
 src/client/views/nodes/ImageBox.tsx               | 55 ++++++++++----------
 src/client/views/smartdraw/DrawingFillHandler.tsx |  6 ++-
 3 files changed, 49 insertions(+), 75 deletions(-)

(limited to 'src/client/views/smartdraw/DrawingFillHandler.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2a2f9d342..e694419a4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -526,18 +526,17 @@ export class DocumentOptions {
     ai_firefly_seed?: number;
     ai_firefly_prompt?: string;
 
-      /**
-   * JSON‐stringified slot configuration for ScrapbookBox
-   */
-  scrapbookConfig?: string;
-
-  /**
-   * The list of embedded Doc instances in each Scrapbook slot
-   */
-  scrapbookContents?: List<Doc>;
-  
-    _outpaintingMetadata?: STRt = new StrInfo('serialized JSON metadata needed for image outpainting', false);
+    /**
+     * JSON‐stringified slot configuration for ScrapbookBox
+     */
+    scrapbookConfig?: string;
 
+    /**
+     * The list of embedded Doc instances in each Scrapbook slot
+     */
+    scrapbookContents?: List<Doc>;
+
+    _outpaintingMetadata?: STRt = new StrInfo('serialized JSON metadata needed for image outpainting', false);
 }
 
 export const DocOptions = new DocumentOptions();
@@ -587,31 +586,6 @@ export namespace Docs {
                     options: { acl: '' },
                 },
             ],
-
-            // AARAV ADD //
-            [
-                DocumentType.JOURNAL,
-                {
-                    layout: { view: EmptyBox, dataField: 'text' },
-                    options: {
-                        title: 'Daily Journal',
-                        acl_Guest: SharingPermissions.View,
-                    },
-                },
-            ],
-            // AARAV ADD //
-
-            [
-                DocumentType.SCRAPBOOK,
-                {
-                    layout: { view: EmptyBox, dataField: 'text' },
-                    options: {
-                        title: 'Scrapbook',
-                        acl_Guest: SharingPermissions.View,
-                    },
-                    
-                },
-            ],
         ]);
 
         const suffix = 'Proto';
@@ -944,18 +918,17 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
         }
 
-        export function ScrapbookDocument(
-            items: Doc[] = [],
-            options: DocumentOptions = {},
-            fieldKey: string = 'items'
-        ) {
+        export function ScrapbookDocument(items: Doc[] = [], options: DocumentOptions = {}, fieldKey: string = 'items') {
             return InstanceFromProto(
                 Prototypes.get(DocumentType.SCRAPBOOK),
                 new List<Doc>(items),
                 {
-                    title: options.title
-                        ?? new Date().toLocaleDateString(undefined, {
-                            year: 'numeric', month: 'short', day: 'numeric'
+                    title:
+                        options.title ??
+                        new Date().toLocaleDateString(undefined, {
+                            year: 'numeric',
+                            month: 'short',
+                            day: 'numeric',
                         }),
                     ...options,
                 },
@@ -1006,8 +979,6 @@ export namespace Docs {
 
         // AARAV ADD //
 
-     
-
         export function LinkDocument(source: Doc, target: Doc, options: DocumentOptions = {}, id?: string) {
             const linkDoc = InstanceFromProto(
                 Prototypes.get(DocumentType.LINK),
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 1fcabaf21..9c6b8e99c 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -140,7 +140,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     componentDidMount() {
-        super.componentDidMount?.();
         this._disposers.sizer = reaction(
             () => ({
                 forceFull: this._props.renderDepth < 1 || this.layoutDoc._showFullRes,
@@ -426,38 +425,40 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 newDimensions: { width: newWidth, height: newHeight },
             });
 
-            const batch = UndoManager.StartBatch('outpaint image');
-            if (response && typeof response === 'object' && 'url' in response && typeof response.url === 'string') {
-                console.log('Received outpainted image:', response.url);
-
-                if (!this.dataDoc[this.fieldKey + '_alternates']) {
-                    this.dataDoc[this.fieldKey + '_alternates'] = new List<Doc>();
-                }
+            const error = ('error' in response && (response.error as string)) || '';
+            if (error.includes('Dropbox') && confirm('Outpaint image failed. Try authorizing DropBox?\r\n' + error.replace(/^[^"]*/, ''))) {
+                DrawingFillHandler.authorizeDropbox();
+            } else {
+                const batch = UndoManager.StartBatch('outpaint image');
+                if (response && typeof response === 'object' && 'url' in response && typeof response.url === 'string') {
+                    if (!this.dataDoc[this.fieldKey + '_alternates']) {
+                        this.dataDoc[this.fieldKey + '_alternates'] = new List<Doc>();
+                    }
 
-                const originalDoc = Docs.Create.ImageDocument(field.url.href, {
-                    title: `Original: ${this.Document.title}`,
-                    _nativeWidth: Doc.NativeWidth(this.dataDoc),
-                    _nativeHeight: Doc.NativeHeight(this.dataDoc),
-                });
+                    const originalDoc = Docs.Create.ImageDocument(field.url.href, {
+                        title: `Original: ${this.Document.title}`,
+                        _nativeWidth: Doc.NativeWidth(this.dataDoc),
+                        _nativeHeight: Doc.NativeHeight(this.dataDoc),
+                    });
 
-                Doc.AddDocToList(this.dataDoc, this.fieldKey + '_alternates', originalDoc);
+                    Doc.AddDocToList(this.dataDoc, this.fieldKey + '_alternates', originalDoc);
 
-                // Replace with new outpainted image
-                this.dataDoc[this.fieldKey] = new ImageField(response.url);
+                    // Replace with new outpainted image
+                    this.dataDoc[this.fieldKey] = new ImageField(response.url);
 
-                Doc.SetNativeWidth(this.dataDoc, newWidth);
-                Doc.SetNativeHeight(this.dataDoc, newHeight);
+                    Doc.SetNativeWidth(this.dataDoc, newWidth);
+                    Doc.SetNativeHeight(this.dataDoc, newHeight);
 
-                this.Document.$ai = true;
-                this.Document.$ai_outpainted = true;
-                this.Document.$ai_outpaint_prompt = customPrompt;
-            } else {
-                console.error('Unexpected API response:', response);
-                this.Document._width = origWidth;
-                this.Document._height = origHeight;
-                alert('Failed to receive a valid image URL from server.');
+                    this.Document.$ai = true;
+                    this.Document.$ai_outpainted = true;
+                    this.Document.$ai_outpaint_prompt = customPrompt;
+                } else {
+                    this.Document._width = origWidth;
+                    this.Document._height = origHeight;
+                    alert('Failed to receive a valid image URL from server.');
+                }
+                batch.end();
             }
-            batch.end();
 
             this._mainCont?.removeChild(loadingOverlay);
         } catch (error) {
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index b0945fd83..2c69284db 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -12,6 +12,9 @@ import { AspectRatioLimits, FireflyDimensionsMap, FireflyImageDimensions, Firefl
 
 const DashDropboxId = '2m86iveqdr9vzsa';
 export class DrawingFillHandler {
+    static authorizeDropbox = () => {
+        window.open(`https://www.dropbox.com/oauth2/authorize?client_id=${DashDropboxId}&response_type=code&token_access_type=offline&redirect_uri=http://localhost:1050/refreshDropbox`, '_blank')?.focus();
+    };
     static drawingToImage = async (drawing: Doc, strength: number, user_prompt: string, styleDoc?: Doc) => {
         const tags = StrListCast(drawing.$tags).map(tag => tag.slice(1));
         const styles = tags.filter(tag => FireflyStylePresets.has(tag));
@@ -46,8 +49,7 @@ export class DrawingFillHandler {
                         .then(res => {
                             const error = ('error' in res && (res.error as string)) || '';
                             if (error.includes('Dropbox') && confirm('Create image failed. Try authorizing DropBox?\r\n' + error.replace(/^[^"]*/, ''))) {
-                                window.open(`https://www.dropbox.com/oauth2/authorize?client_id=${DashDropboxId}&response_type=code&token_access_type=offline&redirect_uri=http://localhost:1050/refreshDropbox`, '_blank')?.focus();
-                                return;
+                                return DrawingFillHandler.authorizeDropbox();
                             }
                             const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 });
                             drawing.$ai_firefly_generatedDocs = genratedDocs;
-- 
cgit v1.2.3-70-g09d2


From 4b8c4c9277cca69ea3341ca6ddf4c7f5befc78a0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 7 May 2025 15:46:06 -0400
Subject: cleaned up ai metadata on generated drawings.

---
 src/client/documents/Documents.ts                  |  5 +-
 src/client/util/DocumentManager.ts                 |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 54 ++-----------------
 src/client/views/nodes/ImageBox.tsx                |  6 +--
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |  4 +-
 src/client/views/smartdraw/DrawingFillHandler.tsx  |  6 +--
 src/client/views/smartdraw/SmartDrawHandler.scss   |  1 +
 src/client/views/smartdraw/SmartDrawHandler.tsx    | 63 +++++++++++-----------
 src/client/views/smartdraw/StickerPalette.tsx      | 12 ++---
 9 files changed, 57 insertions(+), 96 deletions(-)

(limited to 'src/client/views/smartdraw/DrawingFillHandler.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index bbe872a91..9b6acef7b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -519,8 +519,9 @@ export class DocumentOptions {
     card_sort_isDesc?: BOOLt = new BoolInfo('whether the cards are sorted ascending or descending');
 
     ai?: string; // to mark items as ai generated
-    ai_firefly_seed?: number;
-    ai_firefly_prompt?: string;
+    ai_prompt_seed?: NUMt = new NumInfo('seed to GAI engine to make results deterministic');
+    ai_prompt?: STRt = new StrInfo('input prompt to GAI engine');
+    ai_generatedDocs?: List<Doc>; // list of documents generated by GAI engine
 
     /**
      * JSON‐stringified slot configuration for ScrapbookBox
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 3bae2881e..fc8f22cf3 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -349,7 +349,7 @@ export class DocumentManager {
             // bcz: should this delay be an options parameter?
             setTimeout(() => {
                 Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect);
-                const zoomableText = StrCast(targetDoc.text_html, StrCast(targetDoc.ai_firefly_prompt));
+                const zoomableText = StrCast(targetDoc.text_html, StrCast(targetDoc.ai_prompt));
                 if (options.zoomTextSelections && Doc.IsUnhighlightTimerSet() && contextView && zoomableText) {
                     // if the docView is a text anchor, the contextView is the PDF/Web/Text doc
                     contextView.setTextHtmlOverlay(zoomableText, options.effect);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 05958e6b9..bb3c59eae 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1259,15 +1259,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     /**
      * Adds the created drawing to the freeform canvas and sets the metadata.
      */
-    addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => {
-        doc.$title = opts.text;
-        doc.$width = opts.size;
-        doc.$ai_drawing_input = opts.text;
-        doc.$ai_drawing_complexity = opts.complexity;
-        doc.$ai_drawing_colored = opts.autoColor;
-        doc.$ai_drawing_size = opts.size;
-        doc.$ai_drawing_data = gptRes;
-        doc.$ai = 'gpt';
+    addDrawing = (doc: Doc, opts: DrawingOptions, x?: number, y?: number) => {
+        doc.$ai_prompt = opts.text;
         this._drawingContainer = doc;
         if (x !== undefined && y !== undefined) {
             [doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y);
@@ -1977,7 +1970,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 event: action(() => {
                     SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
                     SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
-                    !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10) : SmartDrawHandler.Instance.hideRegenerate();
+                    !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, NumCast(this.layoutDoc[this.scaleFieldKey])) : SmartDrawHandler.Instance.hideRegenerate();
                 }),
                 icon: 'pen-to-square',
             });
@@ -2182,9 +2175,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                             placeholder={this._drawingFillInput || StrCast(this.Document.title) || 'Describe image'}
                             type="text"
                             value={this._drawingFillInput}
-                            onChange={action(e => {
-                                this._drawingFillInput = e.target.value;
-                            })}
+                            onChange={action(e => (this._drawingFillInput = e.target.value))}
                         />
                         <div className="collectionFreeFormView-aiView-strength">
                             <span className="collectionFreeFormView-aiView-similarity">Similarity</span>
@@ -2213,11 +2204,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                                 onClick={undoable(
                                     action(() => {
                                         this._drawingFillLoading = true;
-                                        DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then(
-                                            action(() => {
-                                                this._drawingFillLoading = false;
-                                            })
-                                        );
+                                        DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then(action(() => (this._drawingFillLoading = false)));
                                     }),
                                     'create image'
                                 )}
@@ -2225,37 +2212,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         </div>
                     </div>
                 </div>
-                <div className="collectionfreeformview-aiView-regenerate-container">
-                    <span className="collectionfreeformview-aiView-subtitle">Regenerate</span>
-                    <div className="collectionfreeformview-aiView-regenerate">
-                        <input
-                            className="collectionfreeformview-aiView-input"
-                            aria-label="Edit instructions input"
-                            type="text"
-                            value={this._regenInput}
-                            onChange={action(e => {
-                                this._regenInput = e.target.value;
-                            })}
-                            placeholder="..under development.."
-                        />
-                        <div className="collectionFreeFormView-aiView-regenBtn">
-                            <Button
-                                text="Regenerate"
-                                type={Type.SEC}
-                                icon={this._regenLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
-                                iconPlacement="right"
-                                // onClick={action(async () => {
-                                //     this._regenLoading = true;
-                                //     SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
-                                //     SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
-                                //     SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
-                                //     await SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true);
-                                //     this._regenLoading = false;
-                                // })}
-                            />
-                        </div>
-                    </div>
-                </div>
             </div>
         );
     };
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index bf6915570..b648f2adb 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -225,7 +225,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     const drag = de.complete.docDragData.draggedDocuments.lastElement();
                     const dragField = drag[Doc.LayoutDataKey(drag)];
                     const descText = RTFCast(dragField)?.Text || StrCast(dragField) || RTFCast(drag.text)?.Text || StrCast(drag.text) || StrCast(this.Document.title);
-                    const oldPrompt = StrCast(this.Document.ai_firefly_prompt, StrCast(this.Document.title));
+                    const oldPrompt = StrCast(this.Document.ai_prompt, StrCast(this.Document.title));
                     const newPrompt = (text: string) => (oldPrompt ? `${oldPrompt} ~~~ ${text}` : text);
                     DrawingFillHandler.drawingToImage(this.Document, 90, newPrompt(descText), drag)?.then(action(() => (this._regenerateLoading = false)));
                     added = false;
@@ -735,9 +735,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 <div
                     className="imageBox-regenerateDropTarget"
                     ref={this._regenerateIconRef}
-                    onClick={() => DocCast(this.Document.ai_firefly_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs)!, { openLocation: OpenWhere.addRight })}
+                    onClick={() => DocCast(this.Document.ai_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_generatedDocs)!, { openLocation: OpenWhere.addRight })}
                     style={{
-                        display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_firefly_generatedDocs)) || this._regenerateLoading ? 'block' : 'none',
+                        display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_generatedDocs)) || this._regenerateLoading ? 'block' : 'none',
                         transform: `scale(${this.uiBtnScaling})`,
                         width: this._sideBtnWidth,
                         height: this._sideBtnWidth,
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 9fbae5c90..568e48edf 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -211,7 +211,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
         const selView = DocumentView.Selected().lastElement();
         const selDoc = selView?.Document;
         if (selDoc && (selView._props.renderDepth > 1 || selDoc[Doc.LayoutDataKey(selDoc)] instanceof ImageField)) {
-            const oldPrompt = StrCast(selDoc.ai_firefly_prompt, StrCast(selDoc.title));
+            const oldPrompt = StrCast(selDoc.ai_prompt, StrCast(selDoc.title));
             const newPrompt = oldPrompt ? `${oldPrompt} ~~~ ${imgDesc}` : imgDesc;
             return DrawingFillHandler.drawingToImage(selDoc, 100, newPrompt, selDoc)
                 .then(action(() => (this._userPrompt = '')))
@@ -767,7 +767,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                     //prettier-ignore
                     switch (this._mode) {
                         case GPTPopupMode.USER_PROMPT:   return this.promptBox("ASK", this._userPrompt, this.setUserPrompt, 'Ask GPT to sort, tag, define, or filter your documents for you!');
-                        case GPTPopupMode.FIREFLY:       return this.promptBox("CREATE", this._userPrompt, this.setUserPrompt, StrCast(DocumentView.Selected().lastElement()?.Document.ai_firefly_prompt, 'Ask Firefly to generate images'));
+                        case GPTPopupMode.FIREFLY:       return this.promptBox("CREATE", this._userPrompt, this.setUserPrompt, StrCast(DocumentView.Selected().lastElement()?.Document.ai_prompt, 'Ask Firefly to generate images'));
                         case GPTPopupMode.QUIZ_RESPONSE: return this.promptBox("QUIZ", this._quizAnswer, this.setQuizAnswer, 'Describe/answer the selected document!');
                         case GPTPopupMode.GPT_MENU:      return this.menuBox();
                         case GPTPopupMode.SUMMARY:       return this.summaryBox();
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index 2c69284db..f773957e7 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -51,19 +51,19 @@ export class DrawingFillHandler {
                             if (error.includes('Dropbox') && confirm('Create image failed. Try authorizing DropBox?\r\n' + error.replace(/^[^"]*/, ''))) {
                                 return DrawingFillHandler.authorizeDropbox();
                             }
-                            const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 });
-                            drawing.$ai_firefly_generatedDocs = genratedDocs;
+                            const genratedDocs = DocCast(drawing.ai_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 });
+                            drawing.$ai_generatedDocs = genratedDocs;
                             (res as Upload.ImageInformation[]).map(info =>
                                 Doc.AddDocToList(
                                     genratedDocs,
                                     undefined,
                                     Docs.Create.ImageDocument(info.accessPaths.agnostic.client, {
                                         ai: 'firefly',
+                                        ai_prompt: newPrompt,
                                         tags: new List<string>(['@ai']),
                                         title: newPrompt,
                                         _data_usePath: 'alternate:hover',
                                         data_alternates: new List<Doc>([drawing]),
-                                        ai_firefly_prompt: newPrompt,
                                         _width: 500,
                                         data_nativeWidth: info.nativeWidth,
                                         data_nativeHeight: info.nativeHeight,
diff --git a/src/client/views/smartdraw/SmartDrawHandler.scss b/src/client/views/smartdraw/SmartDrawHandler.scss
index cca7d77c7..e80f1122b 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.scss
+++ b/src/client/views/smartdraw/SmartDrawHandler.scss
@@ -73,5 +73,6 @@
     .edit-box {
         display: flex;
         flex-direction: row;
+        color: black;
     }
 }
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index a6e221f3e..3976ec39e 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -61,7 +61,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
     static Instance: SmartDrawHandler;
 
     private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 };
-    private _lastResponse: string = '';
     private _selectedDocs: Doc[] = [];
 
     @observable private _display: boolean = false;
@@ -97,7 +96,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
         CollectionFreeForm, FormattedTextBox, StickerPalette) to define how a drawing document should be added
         or removed in their respective locations (to the freeform canvas, to the sticker palette's preview, etc.)
     */
-    public AddDrawing: (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => void = unimplementedFunction;
+    public AddDrawing: (doc: Doc, opts: DrawingOptions, x?: number, y?: number) => void = unimplementedFunction;
     public RemoveDrawing: (useLastContainer: boolean, doc?: Doc) => void = unimplementedFunction;
     /**
      * This creates the ink document that represents a drawing, so it goes through the strokes that make up the drawing,
@@ -105,7 +104,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
      * classes to customize the way the drawing docs get created. For example, the freeform canvas has a different way of
      * defining document bounds, so CreateDrawingDoc is redefined when that class calls gpt draw functions.
      */
-    public static CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions) => {
+    public static CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => {
         const drawing: Doc[] = [];
         strokeList.forEach((stroke: [InkData, string, string]) => {
             const bounds = InkField.getBounds(stroke[0]);
@@ -130,7 +129,14 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
             drawing.push(inkDoc);
         });
 
-        return MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 });
+        const drawn = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 });
+
+        drawn.$ai_drawing = true;
+        drawn.$ai_drawing_complexity = opts.complexity;
+        drawn.$ai_drawing_colored = opts.autoColor;
+        drawn.$ai_drawing_size = opts.size;
+        drawn.$ai_drawing_data = gptRes;
+        return drawn;
     };
 
     @action
@@ -146,15 +152,16 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
      * the regenerate popup show by user command.
      */
     @action
-    displayRegenerate = (x: number, y: number) => {
+    displayRegenerate = (x: number, y: number, scale: number) => {
         this._selectedDocs = [DocumentView.SelectedDocs()?.lastElement()];
         [this._pageX, this._pageY] = [x, y];
+        this._scale = scale;
         this._display = false;
         this.ShowRegenerate = true;
         this._showEditBox = false;
         const docData = this._selectedDocs[0];
-        this._lastResponse = StrCast(docData.$drawingData);
-        this._lastInput = { text: StrCast(docData.$ai_drawing_input), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY };
+        this._regenInput = StrCast(docData.$ai_prompt, StrCast(docData.title));
+        this._lastInput = { text: StrCast(docData.$ai_prompt), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY };
     };
 
     /**
@@ -168,9 +175,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
             this._isLoading = false;
             this._showOptions = false;
             this._userInput = '';
-            this._complexity = 5;
-            this._size = 350;
-            this._autoColor = true;
             Doc.ActiveTool = InkTool.None;
         }
     };
@@ -184,7 +188,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
             this.ShowRegenerate = false;
             this._isLoading = false;
             this._regenInput = '';
-            this._lastInput = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 };
         }
     };
 
@@ -208,7 +211,9 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
         this._isLoading = true;
         this._canInteract = false;
         if (this.ShowRegenerate) {
-            await this.regenerate(this._selectedDocs, undefined, undefined, this._regenInput).then(action(() => (this._showEditBox = false)));
+            this._lastInput.x = X;
+            this._lastInput.y = Y;
+            await this.regenerate(this._selectedDocs).then(action(() => (this._showEditBox = false)));
         } else {
             this._showOptions = false;
             try {
@@ -234,13 +239,15 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
      */
     drawWithGPT = async (screenPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => {
         if (input) {
-            this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: screenPt.X, y: screenPt.Y };
+            this._lastInput = { text: input, complexity, size, autoColor, x: screenPt.X, y: screenPt.Y };
             const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true);
             if (res) {
                 const strokeData = await this.parseSvg(res, { X: 0, Y: 0 }, false, autoColor);
                 const drawingDoc = strokeData && SmartDrawHandler.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
-                drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res, screenPt.X, screenPt.Y);
-                drawingDoc && this._selectedDocs.push(drawingDoc);
+                if (drawingDoc) {
+                    this.AddDrawing(drawingDoc, this._lastInput, screenPt.X, screenPt.Y);
+                    this._selectedDocs.push(drawingDoc);
+                }
                 return strokeData;
             } else {
                 console.error('GPT call failed');
@@ -255,7 +262,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
     createImageWithFirefly = (input: string, seed?: number): Promise<FireflyImageData | Doc | undefined> => {
         this._lastInput.text = input;
         return SmartDrawHandler.CreateWithFirefly(input, this._imgDims, seed).then(doc => {
-            doc instanceof Doc && this.AddDrawing(doc, this._lastInput, input, this._pageX, this._pageY);
+            doc instanceof Doc && this.AddDrawing(doc, this._lastInput, this._pageX, this._pageY);
             return doc;
         });
     }; /**
@@ -301,8 +308,8 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
                     _width: Math.min(400, dims.width),
                     _height: (Math.min(400, dims.width) * dims.height) / dims.width,
                     ai: 'firefly',
-                    ai_firefly_seed: +(newseed ?? 0),
-                    ai_firefly_prompt: input,
+                    ai_prompt_seed: +(newseed ?? 0),
+                    ai_prompt: input,
                 });
             })
             .catch(e => {
@@ -316,33 +323,30 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
      * @param doc the drawing Docs to regenerate
      */
     @action
-    regenerate = (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => {
-        if (lastInput) this._lastInput = lastInput;
-        if (lastResponse) this._lastResponse = lastResponse;
+    regenerate = (drawingDocs: Doc[], regenInput?: string, changeInPlace?: boolean) => {
         if (regenInput) this._regenInput = regenInput;
         return Promise.all(
             drawingDocs.map(async doc => {
                 switch (doc.type) {
                     case DocumentType.IMG: {
                         const func = changeInPlace ? this.recreateImageWithFirefly : this.createImageWithFirefly;
-                        const newPrompt = doc.ai_firefly_prompt ? `${doc.ai_firefly_prompt} ~~~ ${this._regenInput}` : this._regenInput;
-                        return this._regenInput ? func(newPrompt, NumCast(doc?.ai_firefly_seed)) : func(this._lastInput.text || StrCast(doc.ai_firefly_prompt));
+                        const newPrompt = doc.ai_prompt && doc.ai_prompt !== this._regenInput ? `${doc.ai_prompt} ~~~ ${this._regenInput}` : this._regenInput;
+                        return this._regenInput ? func(newPrompt, NumCast(doc?.ai_prompt_seed)) : func(this._lastInput.text || StrCast(doc.ai_prompt));
                     }
                     case DocumentType.COL: {
                         try {
                             const res = await (async () => {
                                 if (this._regenInput) {
-                                    const prompt = `This is your previously generated svg code: ${this._lastResponse} for the user input "${this._lastInput.text}". Please regenerate it with the provided specifications.`;
+                                    const prompt = `This is your previously generated svg code: ${doc.$ai_drawing_data} for the user input "${doc.ai_prompt}". Please regenerate it with the provided specifications.`;
                                     this._lastInput.text = `${this._lastInput.text} ~~~ ${this._regenInput}`;
                                     return gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true);
                                 }
-                                return gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true);
+                                return gptAPICall(`"${doc.$ai_prompt}", "${doc.$ai_drawing_complexity}", "${doc.$ai_drawing_size}"`, GPTCallType.DRAW, undefined, true);
                             })();
                             if (res) {
-                                const strokeData = await this.parseSvg(res, { X: this._lastInput.x ?? 0, Y: this._lastInput.y ?? 0 }, true, lastInput?.autoColor || this._autoColor);
-                                this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc);
+                                const strokeData = await this.parseSvg(res, { X: 0, Y: 0 }, true, this._autoColor);
                                 const drawingDoc = strokeData && SmartDrawHandler.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
-                                drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+                                drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, this._lastInput.x, this._lastInput.y);
                             } else {
                                 console.error('GPT call failed');
                             }
@@ -363,7 +367,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
         const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
 
         if (svg) {
-            this._lastResponse = svg[0];
             const svgObject = await parse(svg[0]);
             console.log(res, svgObject);
             const svgStrokes: INode[] = svgObject.children;
@@ -664,7 +667,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
             <div className="regenerate-box">
                 <IconButton
                     tooltip="Regenerate"
-                    icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
+                    icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
                     color={SettingsManager.userColor}
                     onClick={() => this.handleSendClick(this._pageX, this._pageY)}
                 />
diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx
index 0e234e966..6ef3d26ad 100644
--- a/src/client/views/smartdraw/StickerPalette.tsx
+++ b/src/client/views/smartdraw/StickerPalette.tsx
@@ -9,7 +9,7 @@ import ReactLoading from 'react-loading';
 import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
 import { emptyFunction, numberRange } from '../../../Utils';
 import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc';
-import { ImageCast, NumCast } from '../../../fields/Types';
+import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
@@ -147,9 +147,9 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
         SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
         this._canInteract = false;
         Promise.all(
-            numberRange(3).map(i => {
+            numberRange(3).map(() => {
                 return this._showRegenerate
-                    ? SmartDrawHandler.Instance.regenerate(prevDrawings, this._opts, this._gptRes[i], this._userInput)
+                    ? SmartDrawHandler.Instance.regenerate(prevDrawings, this._userInput)
                     : SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity || 0, this._opts.size || 0, !!this._opts.autoColor);
             })
         ).then(() => {
@@ -161,8 +161,8 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
     });
 
     @action
-    addDrawing = (drawing: Doc, opts: DrawingOptions, gptRes: string) => {
-        this._gptRes.push(gptRes);
+    addDrawing = (drawing: Doc) => {
+        this._gptRes.push(StrCast(drawing.$ai_drawing_data));
         drawing.$freeform_fitContentsToBox = true;
         Doc.AddDocToList(this._props.Doc, 'data', drawing);
     };
@@ -176,7 +176,7 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
         const cIndex = NumCast(this._props.Doc.carousel_index);
         const focusedDrawing = DocListCast(this._props.Doc.data)[cIndex];
         focusedDrawing.$title = this._opts.text?.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text;
-        focusedDrawing.$ai_drawing_input = this._opts.text;
+        focusedDrawing.$ai_prompt = this._opts.text;
         focusedDrawing.$ai_drawing_complexity = this._opts.complexity;
         focusedDrawing.$ai_drawing_colored = this._opts.autoColor;
         focusedDrawing.$ai_drawing_size = this._opts.size;
-- 
cgit v1.2.3-70-g09d2

