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 | |
parent | db1462bb2ea327a98ca239080e88944425aba768 (diff) |
merging w/ zach's branch
-rw-r--r-- | src/.DS_Store | bin | 10244 -> 10244 bytes | |||
-rw-r--r-- | src/client/documents/DocumentTypes.ts | 1 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 1 | ||||
-rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 12 | ||||
-rw-r--r-- | src/client/views/LightboxView.tsx | 13 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 50 | ||||
-rw-r--r-- | src/client/views/smartdraw/AnnotationPalette.tsx | 370 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 97 |
9 files changed, 360 insertions, 186 deletions
diff --git a/src/.DS_Store b/src/.DS_Store Binary files differindex 75cff7b55..d7a0cd9d4 100644 --- a/src/.DS_Store +++ b/src/.DS_Store diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 8f95068db..03ae2efb7 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -26,6 +26,7 @@ export enum DocumentType { FUNCPLOT = 'funcplot', // function plotter MAP = 'map', DATAVIZ = 'dataviz', + ANNOPALETTE = 'annopalette', LOADING = 'loading', SIMULATION = 'simulation', // physics simulation diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 24a5de42b..fa0cb920d 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -441,6 +441,7 @@ pie title Minerals in my tap water { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "person-chalkboard", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}}, { toolTip: "Tap or drag to create a view slide", title: "View Slide", icon: "address-card", dragFactory: doc.emptyViewSlide as Doc,clickFactory: DocCast(doc.emptyViewSlide),openFactoryLocation: OpenWhere.overlay,funcs: { hidden: "IsNoviceMode()"}}, { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true, funcs: { hidden: "IsNoviceMode()"} }, + { toolTip: "Tap or drag to create an annotation palette",title: "Annotation Palette", icon: "palette", dragFactory: doc.emptyAnnoPalette as Doc, clickFactory: DocCast(doc.emptyAnnoPalette)}, { toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '<ScriptingRepl />' as any, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script // { toolTip: "Toggle an UndoStack", title: "undostacker", icon: "calculator", clickFactory: "<UndoStack />" as any, openFactoryLocation: OpenWhere.overlay}, ].map(tuple => ( diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 177546fdc..eb0b00472 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -244,12 +244,14 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( } @observable _annoSaved: boolean = false; - @action - saveAnno = (targetDoc: Doc) => { - targetDoc.savedAsAnno = true; + + @undoBatch + saveAnno = action((targetDoc: Doc) => { + // targetDoc.savedAsAnno = true; this._annoSaved = true; - AnnotationPalette.Instance.saveAnno(this.view0, targetDoc); - }; + AnnotationPalette.Instance.addToPalette(targetDoc); + }); + @computed get saveAnnoButton() { const targetDoc = this.view0?.Document; diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index dcd5a61c7..4fcb7ec9c 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -24,6 +24,7 @@ import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { OverlayView } from './OverlayView'; import { AnnotationPalette } from './smartdraw/AnnotationPalette'; +import { DocData } from '../../fields/DocSymbols'; interface LightboxViewProps { PanelWidth: number; @@ -41,7 +42,14 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { * @param view * @returns true if a DocumentView is descendant of the lightbox view */ - public static Contains(view?:DocumentView) { return view && LightboxView.Instance?._docView && (view.containerViewPath?.() ?? []).concat(view).includes(LightboxView.Instance?._docView); } // prettier-ignore + public static Contains(view?: DocumentView) { + return true; + } + // return ( + // (view && LightboxView.Instance?._docView && (view.containerViewPath?.() ?? []).concat(view).includes(LightboxView.Instance?._docView)) || + // view?.Document === AnnotationPalette.Instance.FreeformCanvas || + // view?.Document.embedContainer === AnnotationPalette.Instance.DrawingCarousel + // ); } // prettier-ignore public static LightboxDoc = () => LightboxView.Instance?._doc; // eslint-disable-next-line no-use-before-define static Instance: LightboxView; @@ -210,6 +218,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { togglePalette = () => { this._showPalette = !this._showPalette; AnnotationPalette.Instance.displayPalette(this._showPalette); + if (this._showPalette === false) AnnotationPalette.Instance.resetPalette(true); }; togglePen = () => { Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; @@ -327,7 +336,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { )} <LightboxTourBtn lightboxDoc={this.lightboxDoc} navBtn={this.renderNavBtn} future={this.future} stepInto={this.stepInto} /> {toggleBtn('lightboxView-navBtn', 'toggle reading view', this._doc?._layout_fitWidth, 'book-open', 'book', this.toggleFitWidth)} - {toggleBtn('lightboxView-tabBtn', 'open document in a tab', false, 'file-download', '', this.downloadDoc)} + {toggleBtn('lightboxView-tabBtn', 'open document in a tab', false, 'file-export', '', this.downloadDoc)} {toggleBtn('lightboxView-paletteBtn', 'toggle annotation palette', this._showPalette === true, 'palette', '', this.togglePalette)} {toggleBtn('lightboxView-penBtn', 'toggle pen annotation', Doc.ActiveTool === InkTool.Pen, 'pen', '', this.togglePen)} {toggleBtn('lightboxView-exploreBtn', 'toggle navigate only mode', SnappingManager.ExploreMode, 'globe-americas', '', this.toggleExplore)} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 2e7cb1102..d9a8730e4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -342,6 +342,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faTerminal, fa.faToggleOn, fa.faFile, + fa.faFileExport, fa.faLocationArrow, fa.faSearch, fa.faFileDownload, @@ -485,6 +486,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faAlignJustify, fa.faCheckSquare, fa.faSquarePlus, + fa.faReply, fa.faListUl, fa.faWindowMinimize, fa.faWindowRestore, diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3cd1e99ef..d0f65866b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -57,6 +57,7 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler'; +import { AnnotationPalette } from '../../smartdraw/AnnotationPalette'; @observer class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> { @@ -662,18 +663,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection _width: B.width + inkWidth, _height: B.height + inkWidth, stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore - inkWidth, - ActiveInkColor(), - ActiveInkBezierApprox(), - ActiveFillColor(), - ActiveArrowStart(), - ActiveArrowEnd(), - ActiveDash(), - ActiveIsInkMask() - ); - }); - newStrokes && this.addDocument?.(newStrokes); - // setTimeout(() => this._eraserLock--); + inkWidth, + ActiveInkColor(), + ActiveInkBezierApprox(), + ActiveFillColor(), + ActiveArrowStart(), + ActiveArrowEnd(), + ActiveDash(), + ActiveIsInkMask() + ); + }); + newStrokes && this.addDocument?.(newStrokes); + // setTimeout(() => this._eraserLock--); + } } }); } @@ -1284,10 +1286,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @undoBatch createDrawing = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => { this._drawing = []; - this._drawingContainer = undefined; + const xf = this.screenToFreeformContentsXf; + // this._drawingContainer = undefined; strokeData.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 B = xf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale; const inkDoc = Docs.Create.InkDocument( stroke[0], @@ -1299,16 +1302,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore inkWidth, stroke[1], - undefined, - stroke[2] === 'none' ? undefined : stroke[2] + ActiveInkBezierApprox(), + stroke[2] === 'none' ? ActiveFillColor() : stroke[2], + ActiveArrowStart(), + ActiveArrowEnd(), + ActiveDash(), + ActiveIsInkMask() ); this._drawing.push(inkDoc); this.addDocument(inkDoc); }); - const collection = this._marqueeViewRef.current?.collection(undefined, true, this._drawing); + const collection = containerDoc || this._marqueeViewRef.current?.collection(undefined, true, this._drawing); if (collection) { const docData = collection[DocData]; - docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[0] || opts.text; + docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text; docData.drawingInput = opts.text; docData.drawingComplexity = opts.complexity; docData.drawingColored = opts.autoColor; @@ -1325,10 +1332,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const docData = DocCast(doc[DocData]); const children = DocListCast(docData.data); this._props.removeDocument?.(children); - this._props.removeDocument?.(doc); + // this._props.removeDocument?.(doc); } else { this._props.removeDocument?.(this._drawing); - if (this._drawingContainer) this._props.removeDocument?.(this._drawingContainer); + // if (this._drawingContainer) this._props.removeDocument?.(this._drawingContainer); } }; @@ -2038,8 +2045,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection optionItems.push({ description: 'Show Drawing Editor', event: action(() => { - this._showDrawingEditor = !this._showDrawingEditor; - this._showDrawingEditor ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, this.createDrawing, this.removeDrawing) : SmartDrawHandler.Instance.hideRegenerate(); + !SmartDrawHandler.Instance._showRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, this.createDrawing, this.removeDrawing) : SmartDrawHandler.Instance.hideRegenerate(); }), icon: 'pen-to-square', }); 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: '' }, +}); diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index 24046bb83..489f6f92b 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -17,6 +17,7 @@ import { DocData } from '../../../fields/DocSymbols'; import { DocumentView } from '../nodes/DocumentView'; import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; import './SmartDrawHandler.scss'; +import { unimplementedFunction } from '../../../Utils'; export interface DrawingOptions { text: string; @@ -39,15 +40,15 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { @observable private _userInput: string = ''; @observable private _showOptions: boolean = false; @observable private _showEditBox: boolean = false; - @observable private _showRegenerate: boolean = false; + @observable public _showRegenerate: boolean = false; @observable private _complexity: number = 5; @observable private _size: number = 200; @observable private _autoColor: boolean = true; @observable private _regenInput: string = ''; @observable private _canInteract: boolean = true; - private _addFunc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void = () => {}; - private _deleteFunc: (doc?: Doc) => void = () => {}; - private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 300, autoColor: true, x: 0, y: 0 }; + public _addFunc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void = () => {}; + public _deleteFunc: (doc?: Doc) => void = () => {}; + private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; private _lastResponse: string = ''; private _selectedDoc: Doc | undefined = undefined; @@ -119,51 +120,62 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { this._showOptions = false; this._userInput = ''; this._complexity = 5; - this._size = 300; + this._size = 350; this._autoColor = true; Doc.ActiveTool = InkTool.None; + this._lastInput = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; }; @action hideRegenerate = () => { - this._showRegenerate = false; - this._isLoading = false; - this._regenInput = ''; + if (!this._isLoading) { + this._showRegenerate = false; + this._isLoading = false; + this._regenInput = ''; + this._lastInput = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; + } }; @action handleKeyPress = async (event: React.KeyboardEvent) => { if (event.key === 'Enter') { - if (this._showRegenerate) { - this.regenerate(); - } else { - await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput); - this._userInput = ''; - } + await this.handleSendClick(); } }; - _errorOccurredOnce = false; @action - drawWithGPT = async (startPt: { X: number; Y: number }, input: string) => { - if (input === '') return; - this._lastInput = { text: input, complexity: this._complexity, size: this._size, autoColor: this._autoColor, x: startPt.X, y: startPt.Y }; + handleSendClick = async () => { this._isLoading = true; this._canInteract = false; - this._showOptions = false; + if (this._showRegenerate) { + await this.regenerate(); + this._regenInput = ''; + this._showEditBox = false; + } else { + this._showOptions = false; + await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor); + this.hideSmartDrawHandler(); + this._showRegenerate = true; + } + this._isLoading = false; + this._canInteract = true; + }; + + _errorOccurredOnce = false; + @action + drawWithGPT = async (startPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => { + if (input === '') return; + this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: startPt.X, y: startPt.Y }; + try { - const res = await gptAPICall(`"${input}", "${this._complexity}", "${this._size}"`, GPTCallType.DRAW, undefined, true); + const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true); if (!res) { console.error('GPT call failed'); return; } console.log(res); - const strokeData = await this.parseResponse(res, startPt, false); - this.hideSmartDrawHandler(); - this._showRegenerate = true; + const strokeData = await this.parseResponse(res, startPt, false, autoColor); this._errorOccurredOnce = false; - this._isLoading = false; - this._canInteract = true; return strokeData; } catch (err) { if (this._errorOccurredOnce) { @@ -171,7 +183,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { this._errorOccurredOnce = false; } else { this._errorOccurredOnce = true; - this.drawWithGPT(startPt, input); + this.drawWithGPT(startPt, input, complexity, size, autoColor); } } }; @@ -182,9 +194,11 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { }; @action - regenerate = async () => { - this._isLoading = true; - this._canInteract = false; + regenerate = async (lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string) => { + if (lastInput) this._lastInput = lastInput; + if (lastResponse) this._lastResponse = lastResponse; + if (regenInput) this._regenInput = regenInput; + try { let res; if (this._regenInput !== '') { @@ -199,21 +213,15 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { return; } console.log(res); - this.parseResponse(res, { X: this._lastInput.x, Y: this._lastInput.y }, true); + this.parseResponse(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); } catch (err) { console.error('GPT call failed', err); } - this._isLoading = false; - this._canInteract = true; - this._regenInput = ''; - this._showEditBox = false; }; @action - parseResponse = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean) => { + parseResponse = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => { const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g); - console.log(svg); - console.log('start point is', startPoint); if (svg) { this._lastResponse = svg[0]; const svgObject = await parse(svg[0]); @@ -225,12 +233,12 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { convertedBezier.map(point => { return { X: point.X + startPoint.X - this._size / 1.5, Y: point.Y + startPoint.Y - this._size / 2 }; }), - (regenerate ? this._lastInput.autoColor : this._autoColor) ? child.attributes.stroke : undefined, - (regenerate ? this._lastInput.autoColor : this._autoColor) ? child.attributes.fill : undefined, + (regenerate ? this._lastInput.autoColor : autoColor) ? child.attributes.stroke : undefined, + (regenerate ? this._lastInput.autoColor : autoColor) ? child.attributes.fill : undefined, ]); }); if (regenerate) { - this._deleteFunc(this._selectedDoc); + if (this._deleteFunc !== unimplementedFunction) this._deleteFunc(this._selectedDoc); this._addFunc(strokeData, this._lastInput, svg[0], this._selectedDoc); } else { this._addFunc(strokeData, this._lastInput, svg[0]); @@ -283,9 +291,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} iconPlacement="right" color={SettingsManager.userColor} - onClick={e => { - this.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._userInput); - }} + onClick={this.handleSendClick} /> </div> {this._showOptions && ( @@ -303,6 +309,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { }, }} defaultChecked={true} + value={this._autoColor} size="small" onChange={this.setAutoColor} /> @@ -390,7 +397,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { tooltip="Regenerate" icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />} color={SettingsManager.userColor} - onClick={this.regenerate} + onClick={this.handleSendClick} /> <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={this.edit} /> {this._showEditBox && ( @@ -418,7 +425,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { icon={this._isLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} iconPlacement="right" color={SettingsManager.userColor} - onClick={this.regenerate} + onClick={this.handleSendClick} /> </div> )} |