diff options
| author | eleanor-park <eleanor_park@brown.edu> | 2024-07-23 12:07:53 -0400 |
|---|---|---|
| committer | eleanor-park <eleanor_park@brown.edu> | 2024-07-23 12:07:53 -0400 |
| commit | 3e1ef3d0b5445065ab3f44ffc17bbb04efaa8c8a (patch) | |
| tree | 54060710007bb52986e86efba0162ffef9142229 /src/client/views/smartdraw/AnnotationPalette.tsx | |
| parent | db1462bb2ea327a98ca239080e88944425aba768 (diff) | |
merging w/ zach's branch
Diffstat (limited to 'src/client/views/smartdraw/AnnotationPalette.tsx')
| -rw-r--r-- | src/client/views/smartdraw/AnnotationPalette.tsx | 370 |
1 files changed, 258 insertions, 112 deletions
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,40 +51,96 @@ 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; }; @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: '' }, +}); |
