aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/smartdraw/SmartDrawHandler.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/smartdraw/SmartDrawHandler.tsx')
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx83
1 files changed, 43 insertions, 40 deletions
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index 1cceabed3..3976ec39e 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -10,9 +10,11 @@ import { INode, parse } from 'svgson';
import { imageUrlToBase64, setupMoveUpEvents } from '../../../ClientUtils';
import { unimplementedFunction } from '../../../Utils';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { InkData, InkField, InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
import { BoolCast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { PointData } from '../../../pen-gestures/GestureTypes';
+import { Upload } from '../../../server/SharedMediaTypes';
import { Networking } from '../../Network';
import { GPTCallType, gptAPICall, gptDrawingColor } from '../../apis/gpt/GPT';
import { DocumentType } from '../../documents/DocumentTypes';
@@ -26,9 +28,6 @@ import { MarqueeView } from '../collections/collectionFreeForm';
import { ActiveInkArrowEnd, ActiveInkArrowStart, ActiveInkBezierApprox, ActiveInkColor, ActiveInkDash, ActiveInkFillColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView';
import { FireflyDimensionsMap, FireflyImageData, FireflyImageDimensions } from './FireflyConstants';
import './SmartDrawHandler.scss';
-import { Upload } from '../../../server/SharedMediaTypes';
-import { PointData } from '../../../pen-gestures/GestureTypes';
-import { List } from '../../../fields/List';
export interface DrawingOptions {
text?: string;
@@ -62,7 +61,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
static Instance: SmartDrawHandler;
private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 };
- private _lastResponse: string = '';
private _selectedDocs: Doc[] = [];
@observable private _display: boolean = false;
@@ -98,7 +96,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
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, x?: number, y?: number) => void = unimplementedFunction;
+ public AddDrawing: (doc: Doc, opts: DrawingOptions, x?: number, y?: number) => void = unimplementedFunction;
public RemoveDrawing: (useLastContainer: boolean, doc?: Doc) => void = unimplementedFunction;
/**
* This creates the ink document that represents a drawing, so it goes through the strokes that make up the drawing,
@@ -106,7 +104,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
* classes to customize the way the drawing docs get created. For example, the freeform canvas has a different way of
* defining document bounds, so CreateDrawingDoc is redefined when that class calls gpt draw functions.
*/
- public static CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions) => {
+ public static CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => {
const drawing: Doc[] = [];
strokeList.forEach((stroke: [InkData, string, string]) => {
const bounds = InkField.getBounds(stroke[0]);
@@ -131,7 +129,14 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
drawing.push(inkDoc);
});
- return MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 });
+ const drawn = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 });
+
+ drawn.$ai_drawing = true;
+ drawn.$ai_drawing_complexity = opts.complexity;
+ drawn.$ai_drawing_colored = opts.autoColor;
+ drawn.$ai_drawing_size = opts.size;
+ drawn.$ai_drawing_data = gptRes;
+ return drawn;
};
@action
@@ -147,15 +152,16 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
* the regenerate popup show by user command.
*/
@action
- displayRegenerate = (x: number, y: number) => {
+ displayRegenerate = (x: number, y: number, scale: number) => {
this._selectedDocs = [DocumentView.SelectedDocs()?.lastElement()];
[this._pageX, this._pageY] = [x, y];
+ this._scale = scale;
this._display = false;
this.ShowRegenerate = true;
this._showEditBox = false;
- const docData = this._selectedDocs[0][DocData];
- this._lastResponse = StrCast(docData.drawingData);
- 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 };
+ const docData = this._selectedDocs[0];
+ this._regenInput = StrCast(docData.$ai_prompt, StrCast(docData.title));
+ this._lastInput = { text: StrCast(docData.$ai_prompt), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY };
};
/**
@@ -169,9 +175,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this._isLoading = false;
this._showOptions = false;
this._userInput = '';
- this._complexity = 5;
- this._size = 350;
- this._autoColor = true;
Doc.ActiveTool = InkTool.None;
}
};
@@ -185,7 +188,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this.ShowRegenerate = false;
this._isLoading = false;
this._regenInput = '';
- this._lastInput = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 };
}
};
@@ -209,7 +211,9 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this._isLoading = true;
this._canInteract = false;
if (this.ShowRegenerate) {
- await this.regenerate(this._selectedDocs, undefined, undefined, this._regenInput).then(action(() => (this._showEditBox = false)));
+ this._lastInput.x = X;
+ this._lastInput.y = Y;
+ await this.regenerate(this._selectedDocs).then(action(() => (this._showEditBox = false)));
} else {
this._showOptions = false;
try {
@@ -235,13 +239,15 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
*/
drawWithGPT = async (screenPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => {
if (input) {
- this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: screenPt.X, y: screenPt.Y };
+ this._lastInput = { text: input, complexity, size, autoColor, x: screenPt.X, y: screenPt.Y };
const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true);
if (res) {
const strokeData = await this.parseSvg(res, { X: 0, Y: 0 }, false, autoColor);
const drawingDoc = strokeData && SmartDrawHandler.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
- drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res, screenPt.X, screenPt.Y);
- drawingDoc && this._selectedDocs.push(drawingDoc);
+ if (drawingDoc) {
+ this.AddDrawing(drawingDoc, this._lastInput, screenPt.X, screenPt.Y);
+ this._selectedDocs.push(drawingDoc);
+ }
return strokeData;
} else {
console.error('GPT call failed');
@@ -256,7 +262,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
createImageWithFirefly = (input: string, seed?: number): Promise<FireflyImageData | Doc | undefined> => {
this._lastInput.text = input;
return SmartDrawHandler.CreateWithFirefly(input, this._imgDims, seed).then(doc => {
- doc instanceof Doc && this.AddDrawing(doc, this._lastInput, input, this._pageX, this._pageY);
+ doc instanceof Doc && this.AddDrawing(doc, this._lastInput, this._pageX, this._pageY);
return doc;
});
}; /**
@@ -302,8 +308,8 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
_width: Math.min(400, dims.width),
_height: (Math.min(400, dims.width) * dims.height) / dims.width,
ai: 'firefly',
- ai_firefly_seed: +(newseed ?? 0),
- ai_firefly_prompt: input,
+ ai_prompt_seed: +(newseed ?? 0),
+ ai_prompt: input,
});
})
.catch(e => {
@@ -317,33 +323,30 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
* @param doc the drawing Docs to regenerate
*/
@action
- regenerate = (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => {
- if (lastInput) this._lastInput = lastInput;
- if (lastResponse) this._lastResponse = lastResponse;
+ regenerate = (drawingDocs: Doc[], regenInput?: string, changeInPlace?: boolean) => {
if (regenInput) this._regenInput = regenInput;
return Promise.all(
drawingDocs.map(async doc => {
switch (doc.type) {
case DocumentType.IMG: {
const func = changeInPlace ? this.recreateImageWithFirefly : this.createImageWithFirefly;
- const newPrompt = doc.ai_firefly_prompt ? `${doc.ai_firefly_prompt} ~~~ ${this._regenInput}` : this._regenInput;
- return this._regenInput ? func(newPrompt, NumCast(doc?.ai_firefly_seed)) : func(this._lastInput.text || StrCast(doc.ai_firefly_prompt));
+ const newPrompt = doc.ai_prompt && doc.ai_prompt !== this._regenInput ? `${doc.ai_prompt} ~~~ ${this._regenInput}` : this._regenInput;
+ return this._regenInput ? func(newPrompt, NumCast(doc?.ai_prompt_seed)) : func(this._lastInput.text || StrCast(doc.ai_prompt));
}
case DocumentType.COL: {
try {
const res = await (async () => {
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.`;
+ const prompt = `This is your previously generated svg code: ${doc.$ai_drawing_data} for the user input "${doc.ai_prompt}". Please regenerate it with the provided specifications.`;
this._lastInput.text = `${this._lastInput.text} ~~~ ${this._regenInput}`;
return gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true);
}
- return gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true);
+ return gptAPICall(`"${doc.$ai_prompt}", "${doc.$ai_drawing_complexity}", "${doc.$ai_drawing_size}"`, GPTCallType.DRAW, undefined, true);
})();
if (res) {
- const strokeData = await this.parseSvg(res, { X: this._lastInput.x ?? 0, Y: this._lastInput.y ?? 0 }, true, lastInput?.autoColor || this._autoColor);
- this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc);
+ const strokeData = await this.parseSvg(res, { X: 0, Y: 0 }, true, this._autoColor);
const drawingDoc = strokeData && SmartDrawHandler.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
- drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+ drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, this._lastInput.x, this._lastInput.y);
} else {
console.error('GPT call failed');
}
@@ -364,7 +367,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
if (svg) {
- this._lastResponse = svg[0];
const svgObject = await parse(svg[0]);
console.log(res, svgObject);
const svgStrokes: INode[] = svgObject.children;
@@ -399,12 +401,12 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
*/
colorWithGPT = async (drawing: Doc) => {
const img = await DocumentView.GetDocImage(drawing);
- const { href } = ImageCast(img).url;
+ const { href } = ImageCast(img)?.url ?? { href: '' };
const hrefParts = href.split('.');
const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
try {
const hrefBase64 = await imageUrlToBase64(hrefComplete);
- const strokes = DocListCast(drawing[DocData].data);
+ const strokes = DocListCast(drawing.$data);
const coords: string[] = [];
strokes.forEach((stroke, i) => {
const inkingStroke = DocumentView.getDocumentView(stroke)?.ComponentView as InkingStroke;
@@ -423,14 +425,14 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
*/
colorStrokes = undoable((res: string, drawing: Doc) => {
const colorList = res.match(/\{.*?\}/g);
- const strokes = DocListCast(drawing[DocData].data);
+ const strokes = DocListCast(drawing.$data);
colorList?.forEach((colors, index) => {
const strokeAndFill = colors.match(/#[0-9A-Fa-f]{6}/g);
if (strokeAndFill && strokeAndFill.length == 2) {
- strokes[index][DocData].color = strokeAndFill[0];
+ strokes[index].$color = strokeAndFill[0];
const inkStroke = DocumentView.getDocumentView(strokes[index])?.ComponentView as InkingStroke;
const { inkData } = inkStroke.inkScaledData();
- InkingStroke.IsClosed(inkData) ? (strokes[index][DocData].fillColor = strokeAndFill[1]) : (strokes[index][DocData].fillColor = undefined);
+ InkingStroke.IsClosed(inkData) ? (strokes[index].$fillColor = strokeAndFill[1]) : (strokes[index].$fillColor = undefined);
}
});
}, 'color strokes');
@@ -577,6 +579,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
type="text"
autoFocus
value={this._userInput}
+ onPointerDown={e => e.stopPropagation()}
onChange={action(e => this._canInteract && (this._userInput = e.target.value))}
placeholder="Enter item to draw"
onKeyDown={this.handleKeyPress}
@@ -664,7 +667,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
<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'} />}
+ icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
color={SettingsManager.userColor}
onClick={() => this.handleSendClick(this._pageX, this._pageY)}
/>