From 8da4acee81701b2b779713b1ff4cc4d60e80f437 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 8 Jan 2025 10:29:50 -0500 Subject: code cleanup --- src/client/views/nodes/ImageBox.tsx | 10 +- src/client/views/smartdraw/SmartDrawHandler.tsx | 366 ++++++++++---------- src/client/views/smartdraw/StickerPalette.tsx | 434 ++++++++++++------------ 3 files changed, 404 insertions(+), 406 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index a1fa9a283..ba0959d73 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -7,8 +7,8 @@ import { observer } from 'mobx-react'; import { extname } from 'path'; import * as React from 'react'; import ReactLoading from 'react-loading'; -import { ClientUtils, DashColor, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils'; -import { Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc'; +import { ClientUtils, DashColor, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; @@ -34,7 +34,7 @@ import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; import { StickerPalette } from '../smartdraw/StickerPalette'; import { StyleProp } from '../StyleProp'; -import { DocumentView, DocumentViewInternal } from './DocumentView'; +import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; @@ -44,7 +44,6 @@ import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler'; import { Button } from 'browndash-components'; import { SettingsManager } from '../../util/SettingsManager'; import { AiOutlineSend } from 'react-icons/ai'; -import { returnEmptyDocViewList } from '../StyleProvider'; import { FireflyImageData } from '../smartdraw/FireflyConstants'; export class ImageEditorData { @@ -541,6 +540,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
{this._prevImgs.map(img => ( { @@ -583,7 +583,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { onClick={undoable( action(async () => { this._regenerateLoading = true; - SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true).then(newImgs => { + await SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true).then(newImgs => { if (newImgs[0]) { const url = newImgs[0].pathname; const imgField = new ImageField(url); diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index 5ebe2e358..58db091bc 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -26,6 +26,7 @@ import './SmartDrawHandler.scss'; import { Networking } from '../../Network'; import { OpenWhere } from '../nodes/OpenWhere'; import { FireflyDimensionsMap, FireflyImageDimensions, FireflyImageData } from './FireflyConstants'; +import { DocumentType } from '../../documents/DocumentTypes'; export interface DrawingOptions { text: string; @@ -272,19 +273,19 @@ export class SmartDrawHandler extends ObservableReactComponent { const dims = FireflyDimensionsMap[this._imgDims]; return Networking.PostToServer('/queryFireflyImage', { prompt: input, width: dims.width, height: dims.height, seed: seed }).then(img => { if (!changeInPlace) { - const seed = img.accessPaths.agnostic.client.match(/\/(\d+)upload/)[1]; + const aiseed = img.accessPaths.agnostic.client.match(/\/(\d+)upload/)[1]; const imgDoc: Doc = Docs.Create.ImageDocument(img.accessPaths.agnostic.client, { title: input.match(/^(.*?)~~~.*$/)?.[1] || input, nativeWidth: dims.width, nativeHeight: dims.height, ai: 'firefly', - ai_firefly_seed: seed, + ai_firefly_seed: aiseed, ai_firefly_prompt: input, }); DocumentViewInternal.addDocTabFunc(imgDoc, OpenWhere.addRight); this._selectedDocs.push(imgDoc); } - return { prompt: input, seed: seed, pathname: img.accessPaths.agnostic.client }; + return { prompt: input, seed, pathname: img.accessPaths.agnostic.client }; }); }; @@ -293,44 +294,43 @@ export class SmartDrawHandler extends ObservableReactComponent { * @param doc the drawing Docs to regenerate */ @action - regenerate = async (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => { + regenerate = (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => { if (lastInput) this._lastInput = lastInput; if (lastResponse) this._lastResponse = lastResponse; if (regenInput) this._regenInput = regenInput; - return await Promise.all( + return Promise.all( drawingDocs.map(async doc => { - const docData = doc[DocData]; - if (docData.type == 'image') { - const seed: number = docData?.ai_firefly_seed as number; - if (this._regenInput !== '') { - // if (this._selectedDoc) { - const newPrompt = `${docData.ai_firefly_prompt} ~~~ ${this._regenInput}`; - return this.createImageWithFirefly(newPrompt, seed, changeInPlace); - // } - } else { - return this.createImageWithFirefly(this._lastInput.text || StrCast(docData.ai_firefly_prompt), undefined, changeInPlace); - } - } - if (docData.type == 'collection') { - try { - let res; - if (this._regenInput !== '') { - const prompt: string = `This is your previously generated svg code: ${this._lastResponse} for the user input "${this._lastInput.text}". Please regenerate it with the provided specifications.`; - res = await gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true); - this._lastInput.text = `${this._lastInput.text} ~~~ ${this._regenInput}`; - } else { - res = await gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true); + switch (doc.type) { + case DocumentType.IMG: + if (this._regenInput) { + // if (this._selectedDoc) { + const newPrompt = `${doc.ai_firefly_prompt} ~~~ ${this._regenInput}`; + return this.createImageWithFirefly(newPrompt, NumCast(doc?.ai_firefly_seed), changeInPlace); + // } } - if (!res) { - console.error('GPT call failed'); - return; + return this.createImageWithFirefly(this._lastInput.text || StrCast(doc.ai_firefly_prompt), undefined, changeInPlace); + case DocumentType.COL: { + try { + let res; + 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.`; + res = await gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true); + this._lastInput.text = `${this._lastInput.text} ~~~ ${this._regenInput}`; + } else { + res = await gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true); + } + if (res) { + const strokeData = await this.parseSvg(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); + this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc); + const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes); + drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res); + } else { + console.error('GPT call failed'); + } + } catch (err) { + console.error('Error regenerating drawing', err); } - const strokeData = await this.parseSvg(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); - this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc); - const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes); - drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res); - } catch (err) { - console.error('Error regenerating drawing', err); + break; } } }) @@ -340,7 +340,6 @@ export class SmartDrawHandler extends ObservableReactComponent { /** * Parses the svg code that GPT returns into Bezier curves, with coordinates and colors. */ - @action parseSvg = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => { const svg = res.match(/]*>([\s\S]*?)<\/svg>/g); if (svg) { @@ -401,7 +400,108 @@ export class SmartDrawHandler extends ObservableReactComponent { }); }, 'color strokes'); - renderDisplay() { + renderGenerateOutputOptions = () => ( +
+
+ Generate Ink + this._canInteract && (this._generateDrawing = !this._generateDrawing)} + /> +
+
+ Generate Image + this._canInteract && (this._generateImage = !this._generateImage)} + /> +
+
+ ); + + renderGenerateDrawing = () => ( +
+ Drawing Options +
+
+ Auto color + this._canInteract && (this._autoColor = !this._autoColor))} + /> +
+
+ Complexity + this._canInteract && (this._complexity = val as number))} + valueLabelDisplay="auto" + /> +
+
+ Size (in pixels) + this._canInteract && (this._size = val as number))} + valueLabelDisplay="auto" + /> +
+
+
+ ); + + renderGenerateImage = () => ( +
+ Image Options +
+ + {Object.values(FireflyImageDimensions).map(dim => ( + } onChange={() => this._canInteract && (this._imgDims = dim)} label={dim} /> + ))} + +
+
+ ); + + renderDisplay = () => { return (
{
{this._showOptions && (
-
-
- Generate Ink - this._canInteract && (this._generateDrawing = !this._generateDrawing)} - /> -
-
- Generate Image - this._canInteract && (this._generateImage = !this._generateImage)} - /> -
-
- {this._generateDrawing && ( -
- Drawing Options -
-
- Auto color - this._canInteract && (this._autoColor = !this._autoColor))} - /> -
-
- Complexity - this._canInteract && (this._complexity = val as number))} - valueLabelDisplay="auto" - /> -
-
- Size (in pixels) - this._canInteract && (this._size = val as number))} - valueLabelDisplay="auto" - /> -
-
-
- )} - {this._generateImage && ( -
- Image Options -
- - } onChange={() => this._canInteract && (this._imgDims = FireflyImageDimensions.Square)} label="Square" /> - } onChange={() => this._canInteract && (this._imgDims = FireflyImageDimensions.Landscape)} label="Landscape" /> - } onChange={() => this._canInteract && (this._imgDims = FireflyImageDimensions.Portrait)} label="Portrait" /> - } onChange={() => this._canInteract && (this._imgDims = FireflyImageDimensions.Widescreen)} label="Widescreen" /> - -
-
- )} + {this.renderGenerateOutputOptions()} + {this._generateDrawing ? this.renderGenerateDrawing() : null} + {this._generateImage ? this.renderGenerateImage() : null}
)} ); - } + }; - renderRegenerate() { - return ( -
-
- : } - color={SettingsManager.userColor} - onClick={this.handleSendClick} - /> - } color={SettingsManager.userColor} onClick={action(() => (this._showEditBox = !this._showEditBox))} /> - {this._showEditBox && ( -
- this._canInteract && (this._regenInput = e.target.value))} - onKeyDown={this.handleKeyPress} - placeholder="Edit instructions" - /> -
- )} -
+ renderRegenerateEditBox = () => ( +
+ this._canInteract && (this._regenInput = e.target.value))} + onKeyDown={this.handleKeyPress} + placeholder="Edit instructions" + /> +
+ ); + + renderRegenerate = () => ( +
+
+ : } + color={SettingsManager.userColor} + onClick={this.handleSendClick} + /> + } color={SettingsManager.userColor} onClick={action(() => (this._showEditBox = !this._showEditBox))} /> + {this._showEditBox ? this.renderRegenerateEditBox() : null}
- ); - } +
+ ); render() { - return this._display ? this.renderDisplay() : this.ShowRegenerate ? this.renderRegenerate() : null; + return this._display + ? this.renderDisplay() // + : this.ShowRegenerate + ? this.renderRegenerate() + : null; } } diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx index d23763eb9..e3345f547 100644 --- a/src/client/views/smartdraw/StickerPalette.tsx +++ b/src/client/views/smartdraw/StickerPalette.tsx @@ -7,7 +7,7 @@ import * as React from 'react'; import { AiOutlineSend } from 'react-icons/ai'; import ReactLoading from 'react-loading'; import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; -import { emptyFunction } from '../../../Utils'; +import { emptyFunction, numberRange } from '../../../Utils'; import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { ImageCast, NumCast } from '../../../fields/Types'; @@ -29,6 +29,11 @@ interface StickerPaletteProps { Document: Doc; } +enum StickerPaletteMode { + create, + view, +} + /** * The StickerPalette can be toggled in the lightbox view of a document. The goal of the palette * is to offer an easy way for users to create stickers and drag and drop them onto a document. @@ -41,7 +46,34 @@ interface StickerPaletteProps { */ @observer export class StickerPalette extends ObservableReactComponent { - @observable private _paletteMode: 'create' | 'view' = 'view'; + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(StickerPalette, fieldKey); + } + /** + * Adds a doc to the sticker palette. Gets a snapshot of the document to use as a preview in the palette. When this + * preview is dragged onto a parent document, a copy of that document is added as a sticker. + */ + public static addToPalette = async (doc: Doc) => { + if (!doc.savedAsSticker) { + const docView = DocumentView.getDocumentView(doc); + await docView?.ComponentView?.updateIcon?.(true); + const { clone } = await Doc.MakeClone(doc); + clone.title = doc.title; + const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href; + Doc.AddDocToList(Doc.MyStickers, 'data', makeUserTemplateButtonOrImage(clone, image)); + doc.savedAsSticker = true; + } + }; + + public static getIcon(group: Doc) { + const docView = DocumentView.getDocumentView(group); + docView?.ComponentView?.updateIcon?.(true); + return !docView ? undefined : new Promise(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); + } + + private _gptRes: string[] = []; + + @observable private _paletteMode = StickerPaletteMode.view; @observable private _userInput: string = ''; @observable private _isLoading: boolean = false; @observable private _canInteract: boolean = true; @@ -49,7 +81,6 @@ export class StickerPalette extends ObservableReactComponent { - return (this._docView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docView)) || (this._docCarouselView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docCarouselView)); - }; + Contains = (view: DocumentView) => + (this._docView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docView)) || // + (this._docCarouselView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docCarouselView)); return170 = () => 170; - @action - handleKeyPress = async (event: React.KeyboardEvent) => { + handleKeyPress = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { - await this.generateDrawings(); + this.generateDrawings(); } }; - @action - setPaletteMode = (mode: 'create' | 'view') => { + setPaletteMode = action((mode: StickerPaletteMode) => { this._paletteMode = mode; - }; + }); - @action - setUserInput = (input: string) => { + setUserInput = action((input: string) => { if (!this._isLoading) this._userInput = input; - }; + }); - @action - setDetail = (detail: number) => { + setDetail = action((detail: number) => { if (this._canInteract) this._opts.complexity = detail; - }; + }); - @action - setColor = (autoColor: boolean) => { + setColor = action((autoColor: boolean) => { if (this._canInteract) this._opts.autoColor = autoColor; - }; + }); - @action - setSize = (size: number) => { + setSize = action((size: number) => { if (this._canInteract) this._opts.size = size; - }; + }); - @action - resetPalette = (changePaletteMode: boolean) => { - if (changePaletteMode) this.setPaletteMode('view'); + resetPalette = action((changePaletteMode: boolean) => { + if (changePaletteMode) this.setPaletteMode(StickerPaletteMode.view); this.setUserInput(''); this.setDetail(5); this.setColor(true); @@ -114,55 +134,31 @@ export class StickerPalette extends ObservableReactComponent { - if (!doc.savedAsSticker) { - const docView = DocumentView.getDocumentView(doc); - await docView?.ComponentView?.updateIcon?.(true); - const { clone } = await Doc.MakeClone(doc); - clone.title = doc.title; - const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href; - Doc.AddDocToList(Doc.MyStickers, 'data', makeUserTemplateButtonOrImage(clone, image)); - doc.savedAsSticker = true; - } - }; - - public static getIcon(group: Doc) { - const docView = DocumentView.getDocumentView(group); - if (docView) { - docView.ComponentView?.updateIcon?.(true); - return new Promise(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); - } - return undefined; - } + }); /** * Calls the draw with AI functions in SmartDrawHandler to allow users to generate drawings straight from * the sticker palette. */ @undoBatch - generateDrawings = action(async () => { + generateDrawings = action(() => { this._isLoading = true; const prevDrawings = DocListCast(this._props.Document[DocData].data); this._props.Document[DocData].data = undefined; SmartDrawHandler.Instance.AddDrawing = this.addDrawing; this._canInteract = false; - await Promise.all( - Array.from({ length: 3 }).map((_, i) => { + Promise.all( + numberRange(3).map(i => { return this._showRegenerate ? SmartDrawHandler.Instance.regenerate(prevDrawings, this._opts, this._gptRes[i], this._userInput) : SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity, this._opts.size, this._opts.autoColor); }) - ); - this._opts.text !== '' ? (this._opts.text = `${this._opts.text} ~~~ ${this._userInput}`) : (this._opts.text = this._userInput); - this._userInput = ''; - this._isLoading = false; - this._showRegenerate = true; + ).then(() => { + this._opts.text !== '' ? (this._opts.text = `${this._opts.text} ~~~ ${this._userInput}`) : (this._opts.text = this._userInput); + this._userInput = ''; + this._isLoading = false; + this._showRegenerate = true; + }); }); @action @@ -177,7 +173,7 @@ export class StickerPalette extends ObservableReactComponent { + saveDrawing = () => { const cIndex = NumCast(this._props.Document.carousel_index); const focusedDrawing = DocListCast(this._props.Document.data)[cIndex]; const docData = focusedDrawing[DocData]; @@ -191,168 +187,160 @@ export class StickerPalette extends ObservableReactComponent this.resetPalette(true)); + }; + + renderCreateInput = () => ( +
+ this.setUserInput(e.target.value)} + placeholder={this._showRegenerate ? '(Optional) Enter edits' : 'Enter item to draw'} + onKeyDown={this.handleKeyPress} + /> +
+ ); + renderCreateOptions = () => ( +
+
+ Color + this.setColor(!this._opts.autoColor)} + /> +
+
+ Detail + typeof val === 'number' && this.setDetail(val)} + valueLabelDisplay="auto" + /> +
+
+ Size + typeof val === 'number' && this.setSize(val)} + valueLabelDisplay="auto" + /> +
+
+ ); + renderDoc = (doc: Doc, refFunc: (r: DocumentView) => void) => { + return ( + + ); }; + renderPaletteCreate = () => ( + <> + {this.renderCreateInput()} + {this.renderCreateOptions()} + {this.renderDoc(this._props.Document, (r: DocumentView) => { + this._docCarouselView = r; + })} +
+
+
+ + ); + renderPaletteView = () => ( + <> + {this.renderDoc(Doc.MyStickers, (r: DocumentView) => { + this._docView = r; + })} +