diff options
| author | eleanor-park <eleanor_park@brown.edu> | 2024-06-06 11:31:52 -0400 |
|---|---|---|
| committer | eleanor-park <eleanor_park@brown.edu> | 2024-06-06 11:31:52 -0400 |
| commit | 2f5757ffaebaec9d459404fec266295abeebd2b0 (patch) | |
| tree | 8334b3bc8e569db8a4825ac2aa4490aa633c8236 /src/client/views/collections/collectionFreeForm | |
| parent | baf156f58856004279223b2e1f858c5ff7e88686 (diff) | |
created input box for gpt draw
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 44 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx | 154 |
2 files changed, 190 insertions, 8 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c6db8290d..194c99c3d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -54,6 +54,8 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors'; import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; +import { SmartDrawHandler } from './SmartDrawHandler'; +import { ImageLabelHandler } from './ImageLabelHandler'; @observer class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> { @@ -496,30 +498,33 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection if (!this.Document.isGroup) { // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag // prettier-ignore + const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); switch (Doc.ActiveTool) { - case InkTool.Highlighter: break; - case InkTool.Write: break; - case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views + case InkTool.Highlighter: + break; + case InkTool.Write: + break; + case InkTool.Pen: + break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views case InkTool.StrokeEraser: case InkTool.SegmentEraser: this._batch = UndoManager.StartBatch('collectionErase'); this._eraserPts.length = 0; - const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction, hit !== -1, false); break; case InkTool.RadiusEraser: this._batch = UndoManager.StartBatch('collectionErase'); this._eraserPts.length = 0; - // const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); - // setupMoveUpEvents(this, e, this.onRadiusEraserMove, this.onEraserUp, emptyFunction, hit !== -1, false); + setupMoveUpEvents(this, e, this.onRadiusEraserMove, this.onEraserUp, emptyFunction, hit !== -1, false); break; + case InkTool.SmartDraw: + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, this.createDrawing, hit !== -1, false); case InkTool.None: if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) { - const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false); } break; - default: + default: } } } @@ -1226,6 +1231,29 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; @action + createDrawing = (e: PointerEvent, doubleTap?: boolean) => { + SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY); + if (SmartDrawHandler.Instance.coords) { + // const coords: InkData = SmartDrawHandler.Instance.coords; + // const inkField = new InkField(coords); + // const points = coords.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]); + // const bounds = InkField.getBounds(points); + // const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); + // const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale; + // return Docs.Create.InkDocument( + // points, + // { 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 + // ); + } + }; + + @action zoom = (pointX: number, pointY: number, deltaY: number): void => { if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return; let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05; diff --git a/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx new file mode 100644 index 000000000..fc88b5cc6 --- /dev/null +++ b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx @@ -0,0 +1,154 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, makeObservable, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import React from 'react'; +import { SettingsManager } from '../../../util/SettingsManager'; +import { ObservableReactComponent } from '../../ObservableReactComponent'; +import { Button, IconButton } from 'browndash-components'; +import ReactLoading from 'react-loading'; +import { AiOutlineSend } from 'react-icons/ai'; +import { MarqueeOptionsMenu } from './MarqueeOptionsMenu'; +import './ImageLabelHandler.scss'; +import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT'; +import { InkingStroke } from '../../InkingStroke'; +import { InkData } from '../../../../fields/InkField'; + +@observer +export class SmartDrawHandler extends ObservableReactComponent<{}> { + static Instance: SmartDrawHandler; + + @observable private _display: boolean = false; + @observable private _pageX: number = 0; + @observable private _pageY: number = 0; + @observable private _yRelativeToTop: boolean = true; + @observable private _isLoading: boolean = false; + @observable private _userInput: string = ''; + @observable public coords: InkData | undefined = undefined; + + constructor(props: any) { + super(props); + makeObservable(this); + SmartDrawHandler.Instance = this; + } + + @action + setIsLoading = (isLoading: boolean) => { + this._isLoading = isLoading; + }; + + @action + setUserInput = (input: string) => { + this._userInput = input; + }; + + @action + displaySmartDrawHandler = (x: number, y: number) => { + this._pageX = x; + this._pageY = y; + this._display = true; + }; + + @action + hideLabelhandler = () => { + this._display = false; + }; + + @action + drawWithGPT = async (startPoint: {X: number, Y: number}, input: string) => { + this.setIsLoading(true); + try { + const res = await gptAPICall(input, GPTCallType.DRAW); + if (!res) { + console.error('GPT call failed'); + return; + } + console.log("GPT response:", res); + // controlPts: {X: number, Y: number}[] = [] + // code to extract list of coords from the string + // this.coords = controlPts.map(pt: {X: number, Y: number } => {pt.X + startPoint.X, pt.Y + startPoint.Y}); + + + } catch (err) { + console.error('GPT call failed'); + } + + this.setIsLoading(false); + this.setUserInput(''); + }; + + render() { + if (this._display) { + return ( + <div + id="label-handler" + className="contextMenu-cont" + style={{ + display: this._display ? '' : 'none', + left: this._pageX, + ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }), + background: SettingsManager.userBackgroundColor, + color: SettingsManager.userColor, + }}> + <div> + <IconButton tooltip={'Cancel'} onPointerDown={this.hideLabelhandler} icon={<FontAwesomeIcon icon="xmark" />} color={MarqueeOptionsMenu.Instance.userColor} style={{ width: '19px' }} /> + <input + aria-label="label-input" + id="new-label" + type="text" + style={{ color: 'black' }} + value={this._userInput} + onChange={e => { + this.setUserInput(e.target.value); + }} + placeholder="Enter item to draw" + /> + <Button + style={{ alignSelf: 'flex-end' }} + text="Send" + icon={this._isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />} + iconPlacement="right" + color={MarqueeOptionsMenu.Instance.userColor} + onClick={e => { + this.drawWithGPT({X: e.clientX, Y: e.clientY}, this._userInput); + }} + /> + {/* <IconButton + tooltip={'Generate Drawing'} + onPointerDown={() => { + const input = document.getElementById('new-label') as HTMLInputElement; + const newLabel = input.value; + // this.addLabel(newLabel); + // this._currentLabel = ''; + input.value = ''; + }} + icon={<FontAwesomeIcon icon="plus" />} + color={MarqueeOptionsMenu.Instance.userColor} + style={{ width: '19px' }} + /> + <IconButton tooltip={'Group Images'} icon={<FontAwesomeIcon icon="object-group" />} color={MarqueeOptionsMenu.Instance.userColor} style={{ width: '19px' }} /> */} + </div> + <div> + {/* {this._labelGroups.map(group => { + return ( + <div> + <p>{group}</p> + <IconButton + tooltip={'Remove Label'} + onPointerDown={() => { + this.removeLabel(group); + }} + icon={'x'} + color={MarqueeOptionsMenu.Instance.userColor} + style={{ width: '19px' }} + /> + </div> + ); + })} */} + </div> + </div> + ); + } else { + return <></>; + } + } +} |
