aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/smartdraw/AnnotationPalette.tsx
diff options
context:
space:
mode:
authoreleanor-park <eleanor_park@brown.edu>2024-07-23 12:07:53 -0400
committereleanor-park <eleanor_park@brown.edu>2024-07-23 12:07:53 -0400
commit3e1ef3d0b5445065ab3f44ffc17bbb04efaa8c8a (patch)
tree54060710007bb52986e86efba0162ffef9142229 /src/client/views/smartdraw/AnnotationPalette.tsx
parentdb1462bb2ea327a98ca239080e88944425aba768 (diff)
merging w/ zach's branch
Diffstat (limited to 'src/client/views/smartdraw/AnnotationPalette.tsx')
-rw-r--r--src/client/views/smartdraw/AnnotationPalette.tsx370
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: '' },
+});