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 './ImageLabelHandler.scss'; import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT'; import { InkData } from '../../../fields/InkField'; import { SVGToBezier } from '../../util/bezierFit'; const { parse } = require('svgson'); import { Slider, Switch } from '@mui/material'; import { Doc } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { DocumentView } from '../nodes/DocumentView'; export interface DrawingOptions { text: string; complexity: number; size: number; autoColor: boolean; x: number; y: number; } @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 private _showOptions: boolean = false; @observable private _showEditBox: boolean = false; @observable private _showRegenerate: boolean = false; @observable private _complexity: number = 5; @observable private _size: number = 200; @observable private _autoColor: boolean = true; @observable private _regenInput: string = ''; private _addFunc: (e: React.PointerEvent, strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => void = () => {}; private _deleteFunc: (doc?: Doc) => void = () => {}; private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 300, autoColor: true, x: 0, y: 0 }; private _lastResponse: string = ''; private _selectedDoc: Doc | undefined = undefined; constructor(props: any) { super(props); makeObservable(this); SmartDrawHandler.Instance = this; } @action setUserInput = (input: string) => { this._userInput = input; }; @action setRegenInput = (input: string) => { this._regenInput = input; }; @action setShowOptions = () => { this._showOptions = !this._showOptions; }; @action setComplexity = (val: number) => { this._complexity = val; }; @action setSize = (val: number) => { this._size = val; }; @action setAutoColor = () => { this._autoColor = !this._autoColor; }; @action displaySmartDrawHandler = (x: number, y: number, addFunc: (e: React.PointerEvent, strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => void, deleteFunc: (doc?: Doc) => void) => { this._pageX = x; this._pageY = y; this._display = true; this._addFunc = addFunc; this._deleteFunc = deleteFunc; }; @action displayRegenerate = (x: number, y: number, addFunc: (e: React.PointerEvent, strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => void, deleteFunc: (doc?: Doc) => void) => { const selectedDoc: Doc = DocumentView.SelectedDocs().lastElement(); const docData = selectedDoc[DocData]; this._addFunc = addFunc; this._deleteFunc = deleteFunc; this._pageX = x; this._pageY = y; this._showRegenerate = true; this._lastResponse = docData.drawingData as string; this._lastInput = { text: docData.drawingInput as string, complexity: docData.drawingComplexity as number, size: docData.drawingSize as number, autoColor: docData.drawingColored as boolean, x: this._pageX, y: this._pageY }; }; @action hideSmartDrawHandler = () => { this._showRegenerate = false; this._display = false; this._isLoading = false; this._showOptions = false; this._userInput = ''; this._complexity = 5; this._size = 300; this._autoColor = true; // this._regenInput = '' }; @action hideRegenerate = () => { this._showRegenerate = false; this._isLoading = false; this._regenInput = ''; }; _errorOccurredOnce = false; @action drawWithGPT = async (e: React.PointerEvent, input: string) => { if (input === '') return; this._lastInput = { text: input, complexity: this._complexity, size: this._size, autoColor: this._autoColor, x: e.clientX, y: e.clientY }; this._isLoading = true; this._showOptions = false; try { const res = await gptAPICall(`"${input}", "${this._complexity}", "${this._size}"`, GPTCallType.DRAW, undefined, true); if (!res) { console.error('GPT call failed'); return; } console.log(res); await this.parseResponse(e, res, { X: e.clientX, Y: e.clientY }, false); this.hideSmartDrawHandler(); this._showRegenerate = true; this._errorOccurredOnce = false; } catch (err) { if (this._errorOccurredOnce) { console.error('GPT call failed', err); this._errorOccurredOnce = false; } else { this._errorOccurredOnce = true; this.drawWithGPT(e, input); } } this._isLoading = false; }; @action edit = () => { this._showEditBox = !this._showEditBox; }; @action regenerate = async (e: React.PointerEvent) => { this._isLoading = true; try { let res; if (this._regenInput !== '') { const prompt: string = `This is your previously generated svg code: ${this._lastResponse} for the user input "${this._lastInput.text}". Please regenerate it with the provided specifications.`; res = await gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true); this._lastInput.text = `${this._lastInput.text} + ${this._regenInput}`; } else { res = await gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true); } if (!res) { console.error('GPT call failed'); return; } console.log(res); this.parseResponse(e, res, { X: this._lastInput.x, Y: this._lastInput.y }, true); } catch (err) { console.error('GPT call failed', err); } this._isLoading = false; this._regenInput = ''; this._showEditBox = false; }; @action parseResponse = async (e: React.PointerEvent, res: string, startPoint: { X: number; Y: number }, regenerate: boolean) => { const svg = res.match(/]*>([\s\S]*?)<\/svg>/g); console.log('start point is', startPoint); if (svg) { this._lastResponse = svg[0]; const svgObject = await parse(svg[0]); const svgStrokes: any = svgObject.children; const strokeData: [InkData, string, string][] = []; console.log('autocolor is', this._autoColor); svgStrokes.forEach((child: any) => { const convertedBezier: InkData = SVGToBezier(child.name, child.attributes); strokeData.push([ convertedBezier.map(point => { return { X: point.X + startPoint.X - this._size / 1.5, Y: point.Y + startPoint.Y - this._size / 2 }; }), (regenerate ? this._lastInput.autoColor : this._autoColor) ? child.attributes.stroke : undefined, (regenerate ? this._lastInput.autoColor : this._autoColor) ? child.attributes.fill : undefined, ]); }); if (regenerate) { this._deleteFunc(this._selectedDoc); } this._addFunc(e, strokeData, this._lastInput, svg[0]); } }; render() { if (this._display) { return (
{ this.hideSmartDrawHandler(); this.hideRegenerate(); }} icon={} color={SettingsManager.userColor} style={{ width: '19px' }} /> { this.setUserInput(e.target.value); }} placeholder="Enter item to draw" />
{this._showOptions && ( <>
Auto color
Complexity { this.setComplexity(val as number); }} valueLabelDisplay="auto" />
Size (in pixels) { this.setSize(val as number); }} valueLabelDisplay="auto" />
)}
); } else if (this._showRegenerate) { return (
: } color={SettingsManager.userColor} onClick={e => { this.regenerate(e as React.PointerEvent); }} /> } color={SettingsManager.userColor} onClick={this.edit} /> {this._showEditBox && (
{ this.setRegenInput(e.target.value); }} placeholder="Edit instructions" />
)}
); } else { return <>; } } }