From 7e4d793eaa7e5b6b564355a11fa02a5611645f20 Mon Sep 17 00:00:00 2001 From: bobzel 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 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 {
{this._props.Views.length === 1 && !this.View.showTags ? null : ( // 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 (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() { 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 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() { 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() { } @computed get regenerateImageIcon() { return ( -
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 ? : } -
+ +
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 ? : } +
+
); } 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 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() { 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() { 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 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 => { } }; -const gptDescribeImage = async (image: string): Promise => { +const gptDescribeImage = async (userPrompt: string, url: string): Promise => { + 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() { 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() { }; // updateIcon = () => new Promise(res => res()); - updateIcon = (/* usePanelDimensions?: boolean */) => { - const contentDiv = this._mainCont; - return !contentDiv + updateIcon = (/* usePanelDimensions?: boolean */) => + !this._mainCont || !DocListCast(this.dataDoc[this.annotationKey]).length ? new Promise(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() { this.dataDoc.icon_nativeHeight = nativeHeight; } ); - }; choosePath = (url: URL) => { if (!url?.href) return ''; @@ -453,15 +451,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { /** * 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() { transform: `scale(${this.uiBtnScaling})`, width: this._sideBtnWidth, height: this._sideBtnWidth, - background: 'transparent', + background: 'black', + color: 'white', // color: SettingsManager.userBackgroundColor, }}> - {this._regenerateLoading ? : } + {this._regenerateLoading ? : }
); 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(['@ai']), - title: newPrompt, - _data_usePath: 'alternate:hover', - data_alternates: new List([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(['@ai']), + title: newPrompt, + _data_usePath: 'alternate:hover', + data_alternates: new List([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 => { - 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 => + 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 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 { 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((res, rej) => + new Promise((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(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(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 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; - - _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; + + _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(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() { }; componentDidMount() { - super.componentDidMount?.(); this._disposers.sizer = reaction( () => ({ forceFull: this._props.renderDepth < 1 || this.layoutDoc._showFullRes, @@ -426,38 +425,40 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { 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(); - } + 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(); + } - 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 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; // 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 { - 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 { 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 { - this._drawingFillInput = e.target.value; - })} + onChange={action(e => (this._drawingFillInput = e.target.value))} />
Similarity @@ -2213,11 +2204,7 @@ export class CollectionFreeFormView extends CollectionSubView { 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
-
- Regenerate -
- { - this._regenInput = e.target.value; - })} - placeholder="..under development.." - /> -
-
-
-
); }; 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() { 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() {
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 { 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 { //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(['@ai']), title: newPrompt, _data_usePath: 'alternate:hover', data_alternates: new List([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 { 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 { 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 { * 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 { 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 { * 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 { 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 { 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 { 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 { */ 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 { createImageWithFirefly = (input: string, seed?: number): Promise => { 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 { _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 { * @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 { const svg = res.match(/]*>([\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 {
: } + icon={this._isLoading ? : } 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 { + 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 { - 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