From 3e1ef3d0b5445065ab3f44ffc17bbb04efaa8c8a Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Tue, 23 Jul 2024 12:07:53 -0400 Subject: merging w/ zach's branch --- src/client/views/smartdraw/AnnotationPalette.tsx | 370 ++++++++++++++++------- src/client/views/smartdraw/SmartDrawHandler.tsx | 97 +++--- 2 files changed, 310 insertions(+), 157 deletions(-) (limited to 'src/client/views/smartdraw') diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx index 10e88e91e..c8ce9e653 100644 --- a/src/client/views/smartdraw/AnnotationPalette.tsx +++ b/src/client/views/smartdraw/AnnotationPalette.tsx @@ -1,49 +1,49 @@ +import { faLaptopHouse } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { flexibleCompare } from '@fullcalendar/core/internal'; import { Slider, Switch } from '@mui/material'; import { Button, IconButton } from 'browndash-components'; -import { data } from 'jquery'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { AiOutlineSend } from 'react-icons/ai'; import ReactLoading from 'react-loading'; import { returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero } from '../../../ClientUtils'; -import { ActiveInkWidth, Doc, StrListCast } from '../../../fields/Doc'; +import { ActiveInkWidth, Doc, DocListCast, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { InkData, InkField } from '../../../fields/InkField'; -import { BoolCast, DocCast } from '../../../fields/Types'; -import { emptyFunction } from '../../../Utils'; +import { BoolCast, DocCast, ImageCast, NumCast } from '../../../fields/Types'; +import { emptyFunction, unimplementedFunction } from '../../../Utils'; import { Docs } from '../../documents/Documents'; -import { CollectionViewType } from '../../documents/DocumentTypes'; -import { DragManager } from '../../util/DragManager'; -import { convertDropDataToButtons, makeUserTemplateButton } from '../../util/DropConverter'; +import { makeUserTemplateButton } from '../../util/DropConverter'; import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; -import { MarqueeOptionsMenu, MarqueeView } from '../collections/collectionFreeForm'; -import { CollectionGridView } from '../collections/collectionGrid'; -import { CollectionStackingView } from '../collections/CollectionStackingView'; -import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; -import { FieldViewProps } from '../nodes/FieldView'; +import { undoable, undoBatch } from '../../util/UndoManager'; +import { CollectionFreeFormView, MarqueeOptionsMenu, MarqueeView } from '../collections/collectionFreeForm'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveIsInkMask, DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; +import { FieldView } from '../nodes/FieldView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DefaultStyleProvider } from '../StyleProvider'; import './AnnotationPalette.scss'; -import { SmartDrawHandler } from './SmartDrawHandler'; +import { DrawingOptions, SmartDrawHandler } from './SmartDrawHandler'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { ImageField } from '../../../fields/URLField'; +import { CollectionCarousel3DView } from '../collections/CollectionCarousel3DView'; @observer export class AnnotationPalette extends ObservableReactComponent<{}> { static Instance: AnnotationPalette; - @observable private _savedDrawings: Doc[] = []; - // @observable private _marqueeViewRef = React.createRef(); @observable private _display: boolean = false; @observable private _paletteMode: 'create' | 'view' = 'view'; @observable private _userInput: string = ''; - @observable private _showDrawing: boolean = false; - @observable private _drawing: Doc | undefined = undefined; @observable private _isLoading: boolean = false; - @observable private _detail: number = 5; - @observable private _size: number = 350; @observable private _canInteract: boolean = true; + @observable private _showRegenerate: boolean = false; + @observable private _freeFormCanvas = Docs.Create.FreeformDocument([], {}); + @observable private _drawingCarousel = Docs.Create.CarouselDocument([], {}); + @observable private _drawings: Doc[] = []; + private _drawing: Doc[] = []; + @observable private _opts: DrawingOptions = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 }; + private _gptRes: string[] = []; constructor(props: any) { super(props); @@ -51,8 +51,40 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { AnnotationPalette.Instance = this; } + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(AnnotationPalette, fieldKey); + } + + public get FreeformCanvas() { + return this._freeFormCanvas; + } + + public get DrawingCarousel() { + return this._drawingCarousel; + } + + // componentDidUpdate(prevProps: Readonly<{}>) { + // const docView = DocumentView.getDocumentView(this._freeFormCanvas); + // const componentView = docView?.ComponentView as CollectionFreeFormView; + // if (componentView) { + // componentView.fitContentOnce(); + // } + // this._freeFormCanvas._freeform_fitContentsToBox = false; + // } + return170 = () => 170; + @action + handleKeyPress = async (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + // if (this._showRegenerate) { + // this.regenerate(); + // } else { + await this.generateDrawing(); + // } + } + }; + @action setPaletteMode = (mode: 'create' | 'view') => { this._paletteMode = mode; @@ -60,31 +92,55 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { @action setUserInput = (input: string) => { - this._userInput = input; + if (!this._isLoading) this._userInput = input; }; @action setDetail = (detail: number) => { - this._detail = detail; + if (this._canInteract) this._opts.complexity = detail; + }; + + @action + setColor = (autoColor: boolean) => { + if (this._canInteract) this._opts.autoColor = autoColor; }; @action setSize = (size: number) => { - this._size = size; + if (this._canInteract) this._opts.size = size; }; - saveAnno = async (docView: DocumentView | undefined, doc: Doc) => { - const dragData = new DragManager.DocumentDragData([doc]); - // convertDropDataToButtons(dragData); - const clone = await Doc.MakeClone(doc); - clone.clone.title = doc.title; - const templateBtn = makeUserTemplateButton(clone.clone); + @action + resetPalette = (changePaletteMode: boolean) => { + if (changePaletteMode) this.setPaletteMode('view'); + this.setUserInput(''); + this.setDetail(5); + this.setColor(true); + this.setSize(200); + this._freeFormCanvas = Docs.Create.FreeformDocument([], {}); + this._drawingCarousel = Docs.Create.CarouselDocument([], {}); + this._showRegenerate = false; + this._canInteract = true; + this._drawing = []; + this._drawings = []; + this._opts = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 }; + this._gptRes = []; + }; - // const cloneData: Doc = DocCast(clone.clone[DocData]); - // cloneData.dragFactory = doc; - Doc.AddDocToList(Doc.MyAnnos, 'data', templateBtn); - // const collection = this._marqueeViewRef.current?.collection(undefined, false, this._savedDrawings); - // if (docView) docView.ComponentView?.removeDocument?.(doc); + addToPalette = async (doc: Doc) => { + if (!doc.savedAsAnno) { + const clone = await Doc.MakeClone(doc); + clone.clone.title = doc.title; + const image = await this.getIcon(doc); + if (image) { + const imageDoc = Docs.Create.ImageDocument(image); + Doc.AddDocToList(Doc.MyAnnos, 'data', imageDoc); + } + doc.savedAsAnno = true; + // const templateBtn = makeUserTemplateButton(clone.clone); + // Doc.AddDocToList(Doc.MyAnnos, 'data', templateBtn); + // this.resetPalette(true); + } }; @action @@ -92,78 +148,136 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { this._display = display; }; - @action - generateDrawing = async () => { + @undoBatch + generateDrawing = action(async () => { this._isLoading = true; - try { - const drawingRes = await SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput); - const opts = drawingRes?.lastInput; - const drawing: Doc[] = []; - drawingRes?.data.forEach((stroke: [InkData, string, string]) => { - const bounds = InkField.getBounds(stroke[0]); - // const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); - const inkWidth = ActiveInkWidth(); - const inkDoc = Docs.Create.InkDocument( - stroke[0], - { title: 'stroke', - // x: B.x - inkWidth / 2, - // y: B.y - inkWidth / 2, - // _width: B.width + inkWidth, - // _height: B.height + inkWidth, - stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore - inkWidth, - stroke[1], - undefined, - stroke[2] === 'none' ? undefined : stroke[2] - ); - drawing.push(inkDoc); - }); - const collection = MarqueeOptionsMenu.Instance.createCollection(undefined, true, drawing); - if (collection) { - const docData = collection[DocData]; - docData.title = opts?.text; - docData.drawingInput = opts?.text; - docData.drawingComplexity = opts?.complexity; - docData.drawingColored = opts?.autoColor; - docData.drawingSize = opts?.size; - docData.drawingData = drawingRes?.lastRes; - this._drawing = collection; + this._drawings = []; + this._drawing = []; + for (var i = 0; i < 3; i++) { + try { + SmartDrawHandler.Instance._addFunc = this.createDrawing; + this._canInteract = false; + if (this._showRegenerate) { + SmartDrawHandler.Instance._deleteFunc = unimplementedFunction; + await SmartDrawHandler.Instance.regenerate(this._opts, this._gptRes[i], this._userInput); + } else { + await SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity, this._opts.size, this._opts.autoColor); + } + } catch (e) { + console.log('Error generating drawing'); } - this._showDrawing = true; - } catch (e) { - console.log('Error generating drawing'); } + 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 + createDrawing = (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => { + this._opts = opts; + this._gptRes.push(gptRes); + this._drawing = []; + // const childDocs = DocListCast(this._drawing1[DocData].data); + strokeList.forEach((stroke: [InkData, string, string]) => { + const bounds = InkField.getBounds(stroke[0]); + const inkWidth = ActiveInkWidth(); + const inkDoc = Docs.Create.InkDocument( + stroke[0], + { title: 'stroke', + x: bounds.left - inkWidth / 2, + y: bounds.top - inkWidth / 2, + _width: bounds.width + inkWidth, + _height: bounds.height + inkWidth, + stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore + inkWidth, + stroke[1], + ActiveInkBezierApprox(), + stroke[2] === 'none' ? ActiveFillColor() : stroke[2], + ActiveArrowStart(), + ActiveArrowEnd(), + ActiveDash(), + ActiveIsInkMask() + ); + this._drawing.push(inkDoc); + // childDocs.push(inkDoc); + }); + + const cv = DocumentView.getDocumentView(this._freeFormCanvas)?.ComponentView as CollectionFreeFormView; + const collection = cv._marqueeViewRef.current?.collection(undefined, true, this._drawing); + if (collection) { + this._drawings.push(collection); + cv.fitContentOnce(); + } + this._drawingCarousel = Docs.Create.CarouselDocument(this._drawings, { childLayoutFitWidth: true, _layout_fitWidth: true, _freeform_fitContentsToBox: true }); + this._freeFormCanvas = Docs.Create.FreeformDocument(this._drawing, { _freeform_fitContentsToBox: true }); }; - // @computed get drawingCreator() { - // return ( - // MarqueeOptionsMenu.Instance.createCollection(undefined, true, this._drawing); - // ); - // } - // return Docs.Create.FreeformDocument([], {}); - // Docs.Create. - // return ( - // - // ); + saveDrawing = async () => { + // const cv = DocumentView.getDocumentView(this._freeFormCanvas)?.ComponentView as CollectionFreeFormView; + // if (cv) { + // const collection = cv._marqueeViewRef.current?.collection(undefined, true, this._drawing); + const cIndex: number = this._drawingCarousel.carousel_index as number; + const focusedDrawing = this._drawings[cIndex]; + + const docData = focusedDrawing[DocData]; + docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; + docData.drawingInput = this._opts.text; + docData.drawingComplexity = this._opts.complexity; + docData.drawingColored = this._opts.autoColor; + docData.drawingSize = this._opts.size; + docData.drawingData = this._gptRes[cIndex]; + docData.width = this._opts.size; + // const image = await this.getIcon(collection); + await this.addToPalette(focusedDrawing); + + // if (collection) { + // const docData = collection[DocData]; + // docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; + // docData.drawingInput = this._opts.text; + // docData.drawingComplexity = this._opts.complexity; + // docData.drawingColored = this._opts.autoColor; + // docData.drawingSize = this._opts.size; + // docData.drawingData = this._gptRes; + // docData.width = this._opts.size; + // // const image = await this.getIcon(collection); + // await this.addToPalette(collection); + // } + // } + }; + + async getIcon(group: Doc) { + const docView = DocumentView.getDocumentView(group); + if (docView) { + docView.ComponentView?.updateIcon?.(); + return new Promise(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); + } + return undefined; + } + + @computed get drawingCreator() { + return ( + + ); + } render() { return !this._display ? null : ( @@ -220,13 +334,13 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { onChange={e => { this.setUserInput(e.target.value); }} - placeholder="Enter item to draw" - // onKeyDown={this.handleKeyPress} + placeholder={this._showRegenerate ? '(Optional) Enter edits' : 'Enter item to draw'} + onKeyDown={this.handleKeyPress} />