aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/smartdraw/SmartDrawHandler.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-02-10 19:07:20 -0500
committerbobzel <zzzman@gmail.com>2025-02-10 19:07:20 -0500
commitc9686eaebffb3547b7e0f20aec64754627af76ce (patch)
tree7ebf1c38323a8d7af554ba564acf95cfe79b7709 /src/client/views/smartdraw/SmartDrawHandler.tsx
parentb72d018698ad1d2e713f0fcbef392d23bf1cf545 (diff)
parente93ca53af693fa1ec2186ca9417af122bb5e8e09 (diff)
updated from master
Diffstat (limited to 'src/client/views/smartdraw/SmartDrawHandler.tsx')
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx424
1 files changed, 271 insertions, 153 deletions
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index b4635673c..7db9ef133 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Slider, Switch } from '@mui/material';
-import { Button, IconButton } from 'browndash-components';
+import { Checkbox, FormControlLabel, Radio, RadioGroup, Slider, Switch } from '@mui/material';
+import { Button, IconButton } from '@dash/components';
import { action, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import React from 'react';
@@ -21,8 +21,12 @@ import { SVGToBezier, SVGType } from '../../util/bezierFit';
import { InkingStroke } from '../InkingStroke';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { MarqueeView } from '../collections/collectionFreeForm';
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView';
+import { ActiveInkArrowEnd, ActiveInkArrowStart, ActiveInkDash, ActiveInkFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
import './SmartDrawHandler.scss';
+import { Networking } from '../../Network';
+import { OpenWhere } from '../nodes/OpenWhere';
+import { FireflyDimensionsMap, FireflyImageDimensions, FireflyImageData } from './FireflyConstants';
+import { DocumentType } from '../../documents/DocumentTypes';
export interface DrawingOptions {
text: string;
@@ -55,22 +59,28 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 };
private _lastResponse: string = '';
- private _selectedDoc: Doc | undefined = undefined;
+ private _selectedDocs: Doc[] = [];
private _errorOccurredOnce = false;
@observable private _display: boolean = false;
@observable private _pageX: number = 0;
@observable private _pageY: number = 0;
+ @observable private _scale: number = 0;
@observable private _yRelativeToTop: boolean = true;
@observable private _isLoading: boolean = false;
+
@observable private _userInput: string = '';
+ @observable private _regenInput: string = '';
@observable private _showOptions: boolean = false;
@observable private _showEditBox: boolean = false;
@observable private _complexity: number = 5;
@observable private _size: number = 200;
@observable private _autoColor: boolean = true;
- @observable private _regenInput: string = '';
+ @observable private _imgDims: FireflyImageDimensions = FireflyImageDimensions.Square;
+
@observable private _canInteract: boolean = true;
+ @observable private _generateDrawing: boolean = true;
+ @observable private _generateImage: boolean = true;
@observable public ShowRegenerate: boolean = false;
@@ -82,8 +92,8 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
/**
* AddDrawing and RemoveDrawing are defined by the other classes that call the smart draw functions (i.e.
- CollectionFreeForm, FormattedTextBox, AnnotationPalette) to define how a drawing document should be added
- or removed in their respective locations (to the freeform canvs, to the annotation palette's preview, etc.)
+ CollectionFreeForm, FormattedTextBox, StickerPalette) to define how a drawing document should be added
+ or removed in their respective locations (to the freeform canvas, to the sticker palette's preview, etc.)
*/
public AddDrawing: (doc: Doc, opts: DrawingOptions, gptRes: string) => void = unimplementedFunction;
public RemoveDrawing: (useLastContainer: boolean, doc?: Doc) => void = unimplementedFunction;
@@ -105,14 +115,14 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
y: bounds.top - inkWidth / 2,
_width: bounds.width + inkWidth,
_height: bounds.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ stroke_showLabel: !BoolCast(Doc.UserDoc().activeHideTextLabels)}, // prettier-ignore
inkWidth,
opts.autoColor ? stroke[1] : ActiveInkColor(),
ActiveInkBezierApprox(),
- stroke[2] === 'none' ? ActiveFillColor() : stroke[2],
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
+ stroke[2] === 'none' ? ActiveInkFillColor() : stroke[2],
+ ActiveInkArrowStart(),
+ ActiveInkArrowEnd(),
+ ActiveInkDash(),
ActiveIsInkMask()
);
drawing.push(inkDoc);
@@ -122,9 +132,10 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
};
@action
- displaySmartDrawHandler = (x: number, y: number) => {
+ displaySmartDrawHandler = (x: number, y: number, scale: number) => {
[this._pageX, this._pageY] = [x, y];
this._display = true;
+ this._scale = scale;
};
/**
@@ -134,14 +145,14 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
*/
@action
displayRegenerate = (x: number, y: number) => {
- this._selectedDoc = DocumentView.SelectedDocs()?.lastElement();
+ this._selectedDocs = [DocumentView.SelectedDocs()?.lastElement()];
[this._pageX, this._pageY] = [x, y];
this._display = false;
this.ShowRegenerate = true;
this._showEditBox = false;
- const docData = this._selectedDoc[DocData];
+ const docData = this._selectedDocs[0][DocData];
this._lastResponse = StrCast(docData.drawingData);
- this._lastInput = { text: StrCast(docData.drawingInput), complexity: NumCast(docData.drawingComplexity), size: NumCast(docData.drawingSize), autoColor: BoolCast(docData.drawingColored), x: this._pageX, y: this._pageY };
+ this._lastInput = { text: StrCast(docData.ai_drawing_input), complexity: NumCast(docData.ai_drawing_complexity), size: NumCast(docData.ai_drawing_size), autoColor: BoolCast(docData.ai_drawing_colored), x: this._pageX, y: this._pageY };
};
/**
@@ -191,11 +202,13 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
*/
@action
handleSendClick = async () => {
+ if ((!this.ShowRegenerate && this._userInput == '') || (!this._generateImage && !this._generateDrawing)) return;
this._isLoading = true;
this._canInteract = false;
if (this.ShowRegenerate) {
- await this.regenerate();
+ await this.regenerate(this._selectedDocs);
runInAction(() => {
+ this._selectedDocs = [];
this._regenInput = '';
this._showEditBox = false;
});
@@ -204,7 +217,12 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this._showOptions = false;
});
try {
- await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor);
+ if (this._generateImage) {
+ await this.createImageWithFirefly(this._userInput);
+ }
+ if (this._generateDrawing) {
+ await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor);
+ }
this.hideSmartDrawHandler();
runInAction(() => {
@@ -216,7 +234,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this._errorOccurredOnce = false;
} else {
this._errorOccurredOnce = true;
- await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor);
+ await this.handleSendClick();
}
}
}
@@ -229,59 +247,101 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
/**
* Calls GPT API to create a drawing based on user input.
*/
- @action
drawWithGPT = async (startPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => {
- if (input === '') return;
- this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: startPt.X, y: startPt.Y };
- const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true);
- if (!res) {
- console.error('GPT call failed');
- return;
+ if (input) {
+ this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: startPt.X, y: startPt.Y };
+ const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true);
+ if (res) {
+ const strokeData = await this.parseSvg(res, startPt, false, autoColor);
+ const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
+ drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+ drawingDoc && this._selectedDocs.push(drawingDoc);
+ this._errorOccurredOnce = false;
+ return strokeData;
+ } else {
+ console.error('GPT call failed');
+ }
}
- const strokeData = await this.parseSvg(res, startPt, false, autoColor);
- const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
- drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+ return undefined;
+ };
- this._errorOccurredOnce = false;
- return strokeData;
+ /**
+ * Calls Firefly API to create an image based on user input
+ */
+ createImageWithFirefly = (input: string, seed?: number, changeInPlace?: boolean): Promise<FireflyImageData> => {
+ this._lastInput.text = input;
+ const dims = FireflyDimensionsMap[this._imgDims];
+ return Networking.PostToServer('/queryFireflyImage', { prompt: input, width: dims.width, height: dims.height, seed })
+ .then(img => {
+ const newseed = img.accessPaths.agnostic.client.match(/\/(\d+)upload/)[1];
+ if (!changeInPlace) {
+ const imgDoc: Doc = Docs.Create.ImageDocument(img.accessPaths.agnostic.client, {
+ title: input.match(/^(.*?)~~~.*$/)?.[1] || input,
+ nativeWidth: dims.width,
+ nativeHeight: dims.height,
+ ai: 'firefly',
+ ai_firefly_seed: newseed,
+ ai_firefly_prompt: input,
+ });
+ DocumentViewInternal.addDocTabFunc(imgDoc, OpenWhere.addRight);
+ this._selectedDocs.push(imgDoc);
+ }
+ return { prompt: input, seed, pathname: img.accessPaths.agnostic.client };
+ })
+ .catch(e => alert('create image failed: ' + e.toString()));
};
/**
* Regenerates drawings with the option to add a specific regenerate prompt/request.
+ * @param doc the drawing Docs to regenerate
*/
@action
- regenerate = async (lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string) => {
+ regenerate = (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => {
if (lastInput) this._lastInput = lastInput;
if (lastResponse) this._lastResponse = lastResponse;
if (regenInput) this._regenInput = regenInput;
-
- 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;
- }
- const strokeData = await this.parseSvg(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor);
- this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, this._selectedDoc);
- const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
- drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
- return strokeData;
- } catch (err) {
- console.error('Error regenerating drawing', err);
- }
+ return Promise.all(
+ drawingDocs.map(async doc => {
+ switch (doc.type) {
+ case DocumentType.IMG:
+ if (this._regenInput) {
+ // if (this._selectedDoc) {
+ const newPrompt = doc.ai_firefly_prompt ? `${doc.ai_firefly_prompt} ~~~ ${this._regenInput}` : this._regenInput;
+ return this.createImageWithFirefly(newPrompt, NumCast(doc?.ai_firefly_seed), changeInPlace);
+ // }
+ }
+ return this.createImageWithFirefly(this._lastInput.text || StrCast(doc.ai_firefly_prompt), undefined, changeInPlace);
+ case DocumentType.COL: {
+ try {
+ let res;
+ if (this._regenInput) {
+ const prompt = `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) {
+ const strokeData = await this.parseSvg(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor);
+ this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc);
+ const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
+ drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+ } else {
+ console.error('GPT call failed');
+ }
+ } catch (err) {
+ console.error('Error regenerating drawing', err);
+ }
+ break;
+ }
+ }
+ })
+ );
};
/**
* Parses the svg code that GPT returns into Bezier curves, with coordinates and colors.
*/
- @action
parseSvg = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => {
const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
if (svg) {
@@ -292,7 +352,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
svgStrokes.forEach(child => {
const convertedBezier: InkData = SVGToBezier(child.name as SVGType, child.attributes);
strokeData.push([
- convertedBezier.map(point => ({ X: point.X + startPoint.X - this._size / 1.5, Y: point.Y + startPoint.Y - this._size / 2 })),
+ convertedBezier.map(point => ({ X: startPoint.X + (point.X - startPoint.X) * this._scale, Y: startPoint.Y + (point.Y - startPoint.Y) * this._scale })),
(regenerate ? this._lastInput.autoColor : autoColor) ? child.attributes.stroke : '',
(regenerate ? this._lastInput.autoColor : autoColor) ? child.attributes.fill : '',
]);
@@ -342,10 +402,110 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
});
}, 'color strokes');
- renderDisplay() {
+ renderGenerateOutputOptions = () => (
+ <div className="smartdraw-output-options">
+ <div className="drawing-checkbox">
+ Generate Ink
+ <Checkbox
+ sx={{
+ color: 'white',
+ '&.Mui-checked': {
+ color: SettingsManager.userVariantColor,
+ },
+ }}
+ checked={this._generateDrawing}
+ onChange={() => this._canInteract && (this._generateDrawing = !this._generateDrawing)}
+ />
+ </div>
+ <div className="image-checkbox">
+ Generate Image
+ <Checkbox
+ sx={{
+ color: 'white',
+ '&.Mui-checked': {
+ color: SettingsManager.userVariantColor,
+ },
+ }}
+ checked={this._generateImage}
+ onChange={() => this._canInteract && (this._generateImage = !this._generateImage)}
+ />
+ </div>
+ </div>
+ );
+
+ renderGenerateDrawing = () => (
+ <div className="smartdraw-options-container">
+ Drawing Options
+ <div className="smartdraw-options">
+ <div className="smartdraw-auto-color">
+ Auto color
+ <Switch
+ sx={{
+ '& .MuiSwitch-switchBase.Mui-checked': { color: SettingsManager.userColor },
+ '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { backgroundColor: SettingsManager.userVariantColor },
+ }}
+ defaultChecked={true}
+ value={this._autoColor}
+ size="small"
+ onChange={action(() => this._canInteract && (this._autoColor = !this._autoColor))}
+ />
+ </div>
+ <div className="smartdraw-complexity">
+ Complexity
+ <Slider
+ className="smartdraw-slider"
+ sx={{
+ '& .MuiSlider-track': { color: SettingsManager.userVariantColor },
+ '& .MuiSlider-rail': { color: SettingsManager.userColor },
+ '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
+ }}
+ min={1}
+ max={10}
+ step={1}
+ size="small"
+ value={this._complexity}
+ onChange={action((e, val) => this._canInteract && (this._complexity = val as number))}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ <div className="smartdraw-size">
+ Size (in pixels)
+ <Slider
+ className="smartdraw-slider"
+ sx={{
+ '& .MuiSlider-track': { color: SettingsManager.userVariantColor },
+ '& .MuiSlider-rail': { color: SettingsManager.userColor },
+ '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20` } },
+ }}
+ min={50}
+ max={700}
+ step={10}
+ size="small"
+ value={this._size}
+ onChange={action((e, val) => this._canInteract && (this._size = val as number))}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ </div>
+ </div>
+ );
+
+ renderGenerateImage = () => (
+ <div className="smartdraw-options-container">
+ Image Options
+ <div className="smartdraw-dimensions">
+ <RadioGroup row defaultValue="square" sx={{ alignItems: 'center' }}>
+ {Object.values(FireflyImageDimensions).map(dim => (
+ <FormControlLabel sx={{ width: '40%' }} key={dim} value={dim} control={<Radio />} onChange={() => this._canInteract && (this._imgDims = dim)} label={dim} />
+ ))}
+ </RadioGroup>
+ </div>
+ </div>
+ );
+
+ renderDisplay = () => {
return (
<div
- id="label-handler"
className="smart-draw-handler"
style={{
display: this._display ? '' : 'none',
@@ -354,7 +514,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
background: SettingsManager.userBackgroundColor,
color: SettingsManager.userColor,
}}>
- <div>
+ <div className="smart-draw-main">
<IconButton
tooltip="Cancel"
onClick={() => {
@@ -385,107 +545,65 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
/>
</div>
{this._showOptions && (
- <div className="smartdraw-options">
- <div className="auto-color">
- Auto color
- <Switch
- sx={{
- '& .MuiSwitch-switchBase.Mui-checked': { color: SettingsManager.userColor },
- '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { backgroundColor: SettingsManager.userVariantColor },
- }}
- defaultChecked={true}
- value={this._autoColor}
- size="small"
- onChange={action(() => this._canInteract && (this._autoColor = !this._autoColor))}
- />
- </div>
- <div className="complexity">
- Complexity
- <Slider
- sx={{
- '& .MuiSlider-track': { color: SettingsManager.userVariantColor },
- '& .MuiSlider-rail': { color: SettingsManager.userColor },
- '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
- }}
- style={{ width: '80%' }}
- min={1}
- max={10}
- step={1}
- size="small"
- value={this._complexity}
- onChange={action((e, val) => this._canInteract && (this._complexity = val as number))}
- valueLabelDisplay="auto"
- />
- </div>
- <div className="size">
- Size (in pixels)
- <Slider
- className="size-slider"
- sx={{
- '& .MuiSlider-track': { color: SettingsManager.userVariantColor },
- '& .MuiSlider-rail': { color: SettingsManager.userColor },
- '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20` } },
- }}
- min={50}
- max={700}
- step={10}
- size="small"
- value={this._size}
- onChange={action((e, val) => this._canInteract && (this._size = val as number))}
- valueLabelDisplay="auto"
- />
- </div>
+ <div>
+ {this.renderGenerateOutputOptions()}
+ {this._generateDrawing ? this.renderGenerateDrawing() : null}
+ {this._generateImage ? this.renderGenerateImage() : null}
</div>
)}
</div>
);
- }
+ };
- renderRegenerate() {
- return (
- <div
- className="smart-draw-handler"
- style={{
- left: this._pageX,
- ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }),
- background: SettingsManager.userBackgroundColor,
- color: SettingsManager.userColor,
- }}>
- <div className="regenerate-box">
- <IconButton
- tooltip="Regenerate"
- icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
- color={SettingsManager.userColor}
- onClick={this.handleSendClick}
- />
- <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={action(() => (this._showEditBox = !this._showEditBox))} />
- {this._showEditBox && (
- <div className="edit-box">
- <input
- aria-label="Edit instructions input"
- className="smartdraw-input"
- type="text"
- value={this._regenInput}
- onChange={action(e => this._canInteract && (this._regenInput = e.target.value))}
- onKeyDown={this.handleKeyPress}
- placeholder="Edit instructions"
- />
- <Button
- style={{ alignSelf: 'flex-end' }}
- text="Send"
- icon={this._isLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
- iconPlacement="right"
- color={SettingsManager.userColor}
- onClick={this.handleSendClick}
- />
- </div>
- )}
- </div>
+ renderRegenerateEditBox = () => (
+ <div className="edit-box">
+ <input
+ aria-label="Edit instructions input"
+ className="smartdraw-input"
+ type="text"
+ value={this._regenInput}
+ onChange={action(e => this._canInteract && (this._regenInput = e.target.value))}
+ onKeyDown={this.handleKeyPress}
+ placeholder="Edit instructions"
+ />
+ <Button
+ style={{ alignSelf: 'flex-end' }}
+ text="Send"
+ icon={this._isLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
+ iconPlacement="right"
+ color={SettingsManager.userColor}
+ onClick={this.handleSendClick}
+ />
+ </div>
+ );
+
+ renderRegenerate = () => (
+ <div
+ className="smart-draw-handler"
+ style={{
+ left: this._pageX,
+ ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }),
+ background: SettingsManager.userBackgroundColor,
+ color: SettingsManager.userColor,
+ }}>
+ <div className="regenerate-box">
+ <IconButton
+ tooltip="Regenerate"
+ icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
+ color={SettingsManager.userColor}
+ onClick={this.handleSendClick}
+ />
+ <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={action(() => (this._showEditBox = !this._showEditBox))} />
+ {this._showEditBox ? this.renderRegenerateEditBox() : null}
</div>
- );
- }
+ </div>
+ );
render() {
- return this._display ? this.renderDisplay() : this.ShowRegenerate ? this.renderRegenerate() : null;
+ return this._display
+ ? this.renderDisplay() //
+ : this.ShowRegenerate
+ ? this.renderRegenerate()
+ : null;
}
}