From 3e1ef3d0b5445065ab3f44ffc17bbb04efaa8c8a Mon Sep 17 00:00:00 2001
From: eleanor-park <eleanor_park@brown.edu>
Date: Tue, 23 Jul 2024 12:07:53 -0400
Subject: merging w/ zach's branch

---
 src/client/views/smartdraw/AnnotationPalette.tsx | 370 ++++++++++++++++-------
 1 file changed, 258 insertions(+), 112 deletions(-)

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

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<MarqueeView>();
     @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 (
-    //     <DocumentView
-    //         Document={doc}
-    //         addDocument={undefined}
-    //         addDocTab={DocumentViewInternal.addDocTabFunc}
-    //         pinToPres={DocumentView.PinDoc}
-    //         containerViewPath={returnEmptyDoclist}
-    //         styleProvider={DefaultStyleProvider}
-    //         removeDocument={returnFalse}
-    //         ScreenToLocalTransform={Transform.Identity}
-    //         PanelWidth={this.return170}
-    //         PanelHeight={this.return170}
-    //         renderDepth={0}
-    //         isContentActive={returnTrue}
-    //         focus={emptyFunction}
-    //         whenChildContentsActiveChanged={emptyFunction}
-    //         childFilters={returnEmptyFilter}
-    //         childFiltersByRanges={returnEmptyFilter}
-    //         searchFilterDocs={returnEmptyDoclist}
-    //     />
-    // );
+    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<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000));
+        }
+        return undefined;
+    }
+
+    @computed get drawingCreator() {
+        return (
+            <DocumentView
+                Document={this._freeFormCanvas}
+                addDocument={undefined}
+                addDocTab={DocumentViewInternal.addDocTabFunc}
+                pinToPres={DocumentView.PinDoc}
+                containerViewPath={returnEmptyDoclist}
+                styleProvider={DefaultStyleProvider}
+                removeDocument={returnFalse}
+                ScreenToLocalTransform={Transform.Identity}
+                PanelWidth={this.return170}
+                PanelHeight={this.return170}
+                renderDepth={1}
+                isContentActive={returnTrue}
+                focus={emptyFunction}
+                whenChildContentsActiveChanged={emptyFunction}
+                childFilters={returnEmptyFilter}
+                childFiltersByRanges={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+            />
+        );
+    }
 
     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}
                             />
                             <Button
                                 style={{ alignSelf: 'flex-end' }}
-                                // text="Send"
-                                icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
+                                tooltip={this._showRegenerate ? 'Regenerate' : 'Send'}
+                                icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : this._showRegenerate ? <FontAwesomeIcon icon={'rotate'} /> : <AiOutlineSend />}
                                 iconPlacement="right"
                                 color={SettingsManager.userColor}
                                 onClick={this.generateDrawing}
@@ -245,8 +359,9 @@ export class AnnotationPalette extends ObservableReactComponent<{}> {
                                         },
                                     }}
                                     defaultChecked={true}
+                                    value={this._opts.autoColor}
                                     size="small"
-                                    // onChange={this.setAutoColor}
+                                    onChange={() => this.setColor(!this._opts.autoColor)}
                                 />
                             </div>
                             <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '60px' }}>
@@ -271,7 +386,7 @@ export class AnnotationPalette extends ObservableReactComponent<{}> {
                                     max={10}
                                     step={1}
                                     size="small"
-                                    value={this._detail}
+                                    value={this._opts.complexity}
                                     onChange={(e, val) => {
                                         this.setDetail(val as number);
                                     }}
@@ -297,10 +412,10 @@ export class AnnotationPalette extends ObservableReactComponent<{}> {
                                     }}
                                     style={{ width: '80%' }}
                                     min={50}
-                                    max={700}
+                                    max={500}
                                     step={10}
                                     size="small"
-                                    value={this._size}
+                                    value={this._opts.size}
                                     onChange={(e, val) => {
                                         this.setSize(val as number);
                                     }}
@@ -308,9 +423,9 @@ export class AnnotationPalette extends ObservableReactComponent<{}> {
                                 />
                             </div>
                         </div>
-                        {this._drawing !== undefined && (
+                        <div style={{ display: 'none' }}>
                             <DocumentView
-                                Document={this._drawing}
+                                Document={this._freeFormCanvas}
                                 addDocument={undefined}
                                 addDocTab={DocumentViewInternal.addDocTabFunc}
                                 pinToPres={DocumentView.PinDoc}
@@ -320,7 +435,7 @@ export class AnnotationPalette extends ObservableReactComponent<{}> {
                                 ScreenToLocalTransform={Transform.Identity}
                                 PanelWidth={this.return170}
                                 PanelHeight={this.return170}
-                                renderDepth={0}
+                                renderDepth={1}
                                 isContentActive={returnTrue}
                                 focus={emptyFunction}
                                 whenChildContentsActiveChanged={emptyFunction}
@@ -328,10 +443,41 @@ export class AnnotationPalette extends ObservableReactComponent<{}> {
                                 childFiltersByRanges={returnEmptyFilter}
                                 searchFilterDocs={returnEmptyDoclist}
                             />
-                        )}
+                        </div>
+                        <DocumentView
+                            Document={this._drawingCarousel}
+                            addDocument={undefined}
+                            addDocTab={DocumentViewInternal.addDocTabFunc}
+                            pinToPres={DocumentView.PinDoc}
+                            containerViewPath={returnEmptyDoclist}
+                            styleProvider={DefaultStyleProvider}
+                            removeDocument={returnFalse}
+                            ScreenToLocalTransform={Transform.Identity}
+                            PanelWidth={this.return170}
+                            PanelHeight={this.return170}
+                            renderDepth={1}
+                            isContentActive={returnTrue}
+                            focus={emptyFunction}
+                            whenChildContentsActiveChanged={emptyFunction}
+                            childFilters={returnEmptyFilter}
+                            childFiltersByRanges={returnEmptyFilter}
+                            searchFilterDocs={returnEmptyDoclist}
+                        />
+                        <div style={{ width: '100%', display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
+                            <Button text="Back" tooltip="Back to All Annotations" icon={<FontAwesomeIcon icon="reply" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(true)} />
+                            <div style={{ display: 'flex', flexDirection: 'row' }}>
+                                <Button tooltip="Save" icon={<FontAwesomeIcon icon="file-arrow-down" />} color={SettingsManager.userColor} onClick={this.saveDrawing} />
+                                <Button tooltip="Reset" icon={<FontAwesomeIcon icon="rotate-left" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(false)} />
+                            </div>
+                        </div>
                     </>
                 )}
             </div>
         );
     }
 }
+
+Docs.Prototypes.TemplateMap.set(DocumentType.ANNOPALETTE, {
+    layout: { view: AnnotationPalette, dataField: 'data' },
+    options: { acl: '' },
+});
-- 
cgit v1.2.3-70-g09d2

