diff options
Diffstat (limited to 'src/client/views')
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> |
