aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/MainView.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx14
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss14
-rw-r--r--src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx261
4 files changed, 208 insertions, 82 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cbc337860..67b875ecb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -442,6 +442,7 @@ export class MainView extends ObservableReactComponent<{}> {
fa.faEyeDropper,
fa.faPaintRoller,
fa.faBars,
+ fa.faBarsStaggered,
fa.faBrush,
fa.faShapes,
fa.faEllipsisH,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 93b63ac4c..b8257ff31 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1268,23 +1268,25 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@undoBatch
- createInkStrokes = (strokeList: InkData[], alpha?: number) => {
- console.log(strokeList.length);
- strokeList.forEach(inkData => {
+ createInkStrokes = (strokeData: [InkData, string, string][]) => {
+ strokeData.forEach((stroke: [InkData, string, string]) => {
// const points: InkData = FitCurve(inkData, 20) as InkData;
// const allPts = GenerateControlPoints(inkData, alpha);
- const bounds = InkField.getBounds(inkData);
+ const bounds = InkField.getBounds(stroke[0]);
const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
const inkDoc = Docs.Create.InkDocument(
- inkData,
+ 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
+ inkWidth,
+ stroke[1],
+ undefined,
+ stroke[2] === 'none' ? undefined : stroke[2]
);
this.addDocument(inkDoc);
});
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
index e7413bf8e..9b8727e1a 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
@@ -42,3 +42,17 @@
}
}
}
+
+.complexity-slider {
+ width: 50%; /* Full-width */
+ height: 25px; /* Specified height */
+ background: #d3d3d3; /* Grey background */
+ outline: none; /* Remove outline */
+ opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */
+ -webkit-transition: 0.2s; /* 0.2 seconds transition on hover */
+ transition: opacity 0.2s;
+
+ :hover {
+ opacity: 1; /* Fully shown on mouse-over */
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx
index 956a8d7e9..edb814172 100644
--- a/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx
+++ b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx
@@ -4,16 +4,20 @@ 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 { Button, IconButton, Size } 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 { InkData } from '../../../../fields/InkField';
-import { ButtonType } from '../../nodes/FontIconBox/FontIconBox';
import { SVGToBezier } from '../../../util/bezierFit';
-const { parse, stringify } = require('svgson');
+const { parse } = require('svgson');
+import { Slider, Switch } from '@mui/material';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { Flex } from '@adobe/react-spectrum';
+import { Row } from 'react-aria-components';
+import { UndoManager } from '../../../util/UndoManager';
+import e from 'cors';
@observer
export class SmartDrawHandler extends ObservableReactComponent<{}> {
@@ -25,11 +29,15 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
@observable private _yRelativeToTop: boolean = true;
@observable private _isLoading: boolean = false;
@observable private _userInput: string = '';
- @observable private _drawingTypeToolTip = 'Create Geometric Drawing';
- @observable private _drawingTypeIcon: 'star' | 'splotch' = 'star';
- @observable private _alpha: number | undefined = undefined; // number between 0 and 1 that determines how rounded a drawing will be
- // @observable public strokes: InkData[] = [];
- private _addToDocFunc: (strokeList: InkData[], alpha?: number) => void = () => {};
+ @observable private _showOptions: boolean = false;
+ @observable private _menuIcon: string = 'caret-right';
+ @observable private _complexity: number = 5;
+ @observable private _size: number = 300;
+ @observable private _autoColor: boolean = true;
+ @observable private _showRegenerate: boolean = false;
+ private _addToDocFunc: (strokeList: [InkData, string, string][]) => void = () => {};
+ private _lastX: number = 0;
+ private _lastY: number = 0;
constructor(props: any) {
super(props);
@@ -38,96 +46,81 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
}
@action
- setIsLoading = (isLoading: boolean) => {
- this._isLoading = isLoading;
- };
-
- @action
setUserInput = (input: string) => {
this._userInput = input;
};
@action
- displaySmartDrawHandler = (x: number, y: number, addToDoc: (strokeList: InkData[], alpha?: number) => void) => {
+ displaySmartDrawHandler = (x: number, y: number, addToDoc: (strokeData: [InkData, string, string][]) => void) => {
this._pageX = x;
this._pageY = y;
this._display = true;
this._addToDocFunc = addToDoc;
};
- @action
hideSmartDrawHandler = () => {
+ this._showRegenerate = false;
this._display = false;
+ this._isLoading = false;
+ this._showOptions = false;
+ this._menuIcon = 'caret-right';
+ };
+
+ hideRegenerate = () => {
+ this._showRegenerate = false;
+ this._userInput = '';
+ this._complexity = 5;
+ this._size = 300;
+ this._autoColor = true;
+ this._isLoading = false;
+ };
+
+ toggleMenu = () => {
+ this._showOptions = !this._showOptions;
+ this._menuIcon === 'caret-right' ? (this._menuIcon = 'caret-down') : (this._menuIcon = 'caret-right');
};
@action
- drawWithGPT = async (startPoint: { X: number; Y: number }, input: string) => {
- console.log('start point is', startPoint);
- this.setIsLoading(true);
+ drawWithGPT = async (e: React.MouseEvent<Element, MouseEvent>, startPoint: { X: number; Y: number }, input: string, regenerate: boolean = false) => {
+ if (this._userInput === '') return;
+ e.stopPropagation();
+ this._lastX = startPoint.X;
+ this._lastY = startPoint.Y;
+ this._isLoading = true;
+ this._showOptions = false;
try {
- const res = await gptAPICall(input, GPTCallType.DRAW);
+ const res = await gptAPICall(`"${input}", "${this._complexity}", "${this._size}"`, GPTCallType.DRAW);
if (!res) {
console.error('GPT call failed');
return;
}
- console.log('GPT response:', res);
const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
- console.log('svg', svg);
if (svg) {
const svgObject = await parse(svg[0]);
- console.log('svg object', svgObject);
const svgStrokes: any = svgObject.children;
- const beziers: InkData[] = [];
- svgStrokes.forEach((stroke: any) => {
- const convertedBezier: InkData = SVGToBezier(stroke.name, stroke.attributes);
- beziers.push(
+ const strokeData: [InkData, string, string][] = [];
+ svgStrokes.forEach((child: any) => {
+ const convertedBezier: InkData = SVGToBezier(child.name, child.attributes);
+ strokeData.push([
convertedBezier.map(point => {
- return { X: point.X + startPoint.X, Y: point.Y + startPoint.Y };
- })
- );
+ return { X: point.X + startPoint.X - this._size / 1.5, Y: point.Y + startPoint.Y - this._size / 2 };
+ }),
+ this._autoColor ? child.attributes.stroke : undefined,
+ this._autoColor ? child.attributes.fill : undefined,
+ ]);
});
- this._addToDocFunc(beziers);
+ if (regenerate) UndoManager.Undo();
+ this._addToDocFunc(strokeData);
}
-
- // const strokes = res.trim().split(/\s*(?=\s*M)/); // prettier-ignore
- // const parsedSegments: InkData[] = [];
- // console.log('strokes', strokes);
- // strokes.forEach(stroke => {
- // stroke = stroke.replace(/C\s*\((\d+,\d+)\)\s*\((\d+,\d+)\)\s*\((\d+,\d+)\)/g, (c, p1, p2, p3) => {
- // return `C (${p1}) (${p2}) (${p3}) (${p3})`;
- // });
- // const coordStrings = stroke.match(/(\d+,\d+)/g);
- // const coords: InkData = [];
- // if (coordStrings) {
- // coordStrings.forEach(coord => {
- // const xy = coord.split(',');
- // coords.push({ X: parseInt(xy[0]), Y: parseInt(xy[1]) });
- // });
- // coords.pop();
- // parsedSegments.push(coords);
- // }
- // console.log('coords', coords);
- // });
- // this._addToDocFunc(parsedSegments);
} catch (err) {
console.error('GPT call failed', err);
}
-
- this.setIsLoading(false);
- this.setUserInput('');
this.hideSmartDrawHandler();
+ this._showRegenerate = true;
};
- changeDrawingType = () => {
- if (this._drawingTypeIcon === 'star') {
- this._drawingTypeIcon = 'splotch';
- this._drawingTypeToolTip = 'Create Rounded Drawing';
- this._alpha = 0.2;
- } else {
- this._drawingTypeIcon = 'star';
- this._drawingTypeToolTip = 'Create Geometric Drawing';
- this._alpha = 0;
- }
+ regenerate = (e: React.MouseEvent<Element, MouseEvent>) => {
+ this.drawWithGPT(e, { X: this._lastX, Y: this._lastY }, `Regenerate the item "${this._userInput}"`, true);
};
render() {
@@ -144,7 +137,16 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
color: SettingsManager.userColor,
}}>
<div>
- <IconButton tooltip={'Cancel'} onClick={this.hideSmartDrawHandler} icon={<FontAwesomeIcon icon="xmark" />} color={MarqueeOptionsMenu.Instance.userColor} style={{ width: '19px' }} />
+ <IconButton
+ tooltip={'Cancel'}
+ onClick={() => {
+ this.hideSmartDrawHandler();
+ this.hideRegenerate();
+ }}
+ icon={<FontAwesomeIcon icon="xmark" />}
+ color={SettingsManager.userColor}
+ style={{ width: '19px' }}
+ />
<input
aria-label="label-input"
id="new-label"
@@ -156,24 +158,131 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
}}
placeholder="Enter item to draw"
/>
- <IconButton tooltip={this._drawingTypeToolTip} icon={<FontAwesomeIcon icon={this._drawingTypeIcon} />} color={MarqueeOptionsMenu.Instance.userColor} style={{ width: '14px' }} onClick={this.changeDrawingType} />
- {/* <IconButton
- tooltip="Create Geometric Drawing"
- icon={<FontAwesomeIcon icon="star" />}
- color={MarqueeOptionsMenu.Instance.userColor}
+ <IconButton
+ tooltip="Advanced Options"
+ icon={<FontAwesomeIcon icon={this._showOptions ? 'caret-down' : 'caret-right'} />}
+ color={SettingsManager.userColor}
style={{ width: '14px' }}
onClick={() => {
- this._alpha = 0;
+ this._showOptions = !this._showOptions;
}}
- /> */}
+ />
<Button
style={{ alignSelf: 'flex-end' }}
text="Send"
- icon={this._isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />}
+ icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
iconPlacement="right"
- color={MarqueeOptionsMenu.Instance.userColor}
+ color={SettingsManager.userColor}
+ onClick={e => {
+ this.drawWithGPT(e, { X: e.clientX, Y: e.clientY }, this._userInput);
+ }}
+ />
+ </div>
+ {this._showOptions && (
+ <>
+ <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-around' }}>
+ <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '30%' }}>
+ Auto color
+ <Switch
+ sx={{
+ '& .MuiSwitch-switchBase.Mui-checked': {
+ color: SettingsManager.userColor,
+ },
+ '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': {
+ backgroundColor: SettingsManager.userVariantColor,
+ },
+ }}
+ defaultChecked={true}
+ size="small"
+ onChange={() => (this._autoColor = !this._autoColor)}
+ />
+ </div>
+ <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '31%' }}>
+ Complexity
+ <Slider
+ sx={{
+ '& .MuiSlider-thumb': {
+ color: SettingsManager.userColor,
+ '&.Mui-focusVisible, &:hover, &.Mui-active': {
+ boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10`,
+ },
+ },
+ '& .MuiSlider-track': {
+ color: SettingsManager.userVariantColor,
+ },
+ '& .MuiSlider-rail': {
+ color: SettingsManager.userColor,
+ },
+ }}
+ style={{ width: '80%' }}
+ min={1}
+ max={10}
+ step={1}
+ size="small"
+ value={this._complexity}
+ onChange={(e, val) => {
+ this._complexity = val as number;
+ }}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '39%' }}>
+ Size (in pixels)
+ <Slider
+ sx={{
+ '& .MuiSlider-thumb': {
+ color: SettingsManager.userColor,
+ '&.Mui-focusVisible, &:hover, &.Mui-active': {
+ boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20`,
+ },
+ },
+ '& .MuiSlider-track': {
+ color: SettingsManager.userVariantColor,
+ },
+ '& .MuiSlider-rail': {
+ color: SettingsManager.userColor,
+ },
+ }}
+ style={{ width: '80%' }}
+ min={50}
+ max={700}
+ step={10}
+ size="small"
+ value={this._size}
+ onChange={(e, val) => {
+ this._size = val as number;
+ }}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ </div>
+ </>
+ )}
+ </div>
+ );
+ } else if (this._showRegenerate) {
+ return (
+ <div
+ id="smartdraw-options-menu"
+ className="contextMenu-cont"
+ style={{
+ left: this._pageX,
+ ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }),
+ background: SettingsManager.userBackgroundColor,
+ color: SettingsManager.userColor,
+ }}>
+ <div
+ style={{
+ display: 'flex',
+ flexDirection: 'row',
+ }}>
+ <IconButton tooltip="Cancel" onClick={this.hideRegenerate} icon={<FontAwesomeIcon icon="xmark" />} color={SettingsManager.userColor} style={{ width: '19px' }} />
+ <IconButton
+ tooltip="Regenerate"
+ icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
+ color={SettingsManager.userColor}
onClick={e => {
- this.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._userInput);
+ this.regenerate(e);
}}
/>
</div>