aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/apis/gpt/GPT.ts1
-rw-r--r--src/client/util/CurrentUserUtils.ts2
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/SmartDraw.tsx0
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx44
-rw-r--r--src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx154
-rw-r--r--src/fields/InkField.ts1
7 files changed, 195 insertions, 9 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 3a5e49731..5afb345a0 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -12,6 +12,7 @@ enum GPTCallType {
DESCRIBE = 'describe',
MERMAID = 'mermaid',
DATA = 'data',
+ DRAW = 'draw',
}
type GPTCallOpts = {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4e379219f..3250f10a8 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -745,7 +745,7 @@ pie title Minerals in my tap water
{ title: "Labels", toolTip: "Lab els", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: "labels", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, },
{ title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1},
{ title: "Ink", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'} },
- { title: "Smart Draw", toolTip: "Draw with GPT", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: "smartDraw", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}},
+ { title: "Smart Draw", toolTip: "Draw with GPT", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: "smartdraw", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}},
];
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8430db883..f7e1617fc 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -55,6 +55,7 @@ import { TabDocView } from './collections/TabDocView';
import './collections/TreeView.scss';
import { CollectionFreeFormView } from './collections/collectionFreeForm';
import { ImageLabelHandler } from './collections/collectionFreeForm/ImageLabelHandler';
+import { SmartDrawHandler } from './collections/collectionFreeForm/SmartDrawHandler';
import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
import { CollectionLinearView } from './collections/collectionLinear';
import { LinkMenu } from './linking/LinkMenu';
@@ -1090,6 +1091,7 @@ export class MainView extends ObservableReactComponent<{}> {
<TaskCompletionBox />
<ContextMenu />
<ImageLabelHandler />
+ <SmartDrawHandler />
<AnchorMenu />
<MapAnchorMenu />
<DirectionsAnchorMenu />
diff --git a/src/client/views/SmartDraw.tsx b/src/client/views/SmartDraw.tsx
deleted file mode 100644
index e69de29bb..000000000
--- a/src/client/views/SmartDraw.tsx
+++ /dev/null
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 <></>;
+ }
+ }
+}
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 32abf0076..123d32301 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -17,6 +17,7 @@ export enum InkTool {
Stamp = 'stamp',
Write = 'write',
PresentationPin = 'presentationpin',
+ SmartDraw = 'smartdraw',
}
export type Segment = Array<Bezier>;