diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/Scripting.ts | 4 | ||||
-rw-r--r-- | src/client/util/bezierFit.ts | 4 | ||||
-rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 15 | ||||
-rw-r--r-- | src/client/views/InkStrokeProperties.ts | 35 | ||||
-rw-r--r-- | src/client/views/PropertiesView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 16 | ||||
-rw-r--r-- | src/client/views/smartdraw/AnnotationPalette.scss | 46 | ||||
-rw-r--r-- | src/client/views/smartdraw/AnnotationPalette.tsx | 56 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.scss | 41 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 80 |
10 files changed, 213 insertions, 90 deletions
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index c63d3d7cb..cb314e3f1 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -1,7 +1,7 @@ // export const ts = (window as any).ts; // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts' -import typescriptlib from 'type_decls.d'; +// import typescriptlib from 'type_decls.d'; import * as ts from 'typescript'; import { Doc, FieldType } from '../../fields/Doc'; import { RefField } from '../../fields/RefField'; @@ -248,7 +248,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp const funcScript = `(function(${paramString})${reqTypes} { ${body} })`; host.writeFile('file.ts', funcScript); - if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib); + // if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib); const program = ts.createProgram(['file.ts'], {}, host); const testResult = program.emit(); const outputText = host.readFile('file.js'); diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts index 4c7f4a0ba..4aef28e6b 100644 --- a/src/client/util/bezierFit.ts +++ b/src/client/util/bezierFit.ts @@ -696,7 +696,7 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { const matches: RegExpMatchArray[] = Array.from( attributes.d.matchAll(/Q(-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*)|C(-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*)|L(-?\d+\.?\d*),(-?\d+\.?\d*)/g) ); - let lastPt: Point = { X: 0, Y: 0 }; + let lastPt: Point = startPt; matches.forEach(match => { if (match[0].startsWith('Q')) { coordList.push({ X: parseInt(match[1]), Y: parseInt(match[2]) }); @@ -711,7 +711,7 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { coordList.push({ X: parseInt(match[9]), Y: parseInt(match[10]) }); lastPt = { X: parseInt(match[9]), Y: parseInt(match[10]) }; } else { - coordList.push(lastPt || { X: parseInt(startPt[1]), Y: parseInt(startPt[2]) }); + coordList.push(lastPt); coordList.push({ X: parseInt(match[11]), Y: parseInt(match[12]) }); coordList.push({ X: parseInt(match[11]), Y: parseInt(match[12]) }); coordList.push({ X: parseInt(match[11]), Y: parseInt(match[12]) }); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index e228f648c..28424c711 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -239,20 +239,6 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( ); } - saveAnno = undoable(async (targetDoc: Doc) => await AnnotationPalette.addToPalette(targetDoc), 'save to palette'); - - @computed - get saveAnnoButton() { - const targetDoc = this.view0?.Document; - return !targetDoc ? null : ( - <Tooltip title={<div className="dash-tooltip">{targetDoc.savedAsAnno ? 'Saved as Annotation!' : 'Save to Annotation Palette'}</div>}> - <div className="documentButtonBar-icon" style={{ color: 'white' }} onClick={() => this.saveAnno(targetDoc)}> - <FontAwesomeIcon className="documentdecorations-icon" icon={targetDoc.savedAsAnno ? 'clipboard-check' : 'file-arrow-down'} /> - </div> - </Tooltip> - ); - } - @computed get shareButton() { const targetDoc = this.view0?.Document; @@ -497,7 +483,6 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( <div className="documentButtonBar-button">{this.templateButton}</div> {!DocumentView.Selected().some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>} <div className="documentButtonBar-button">{this.pinButton}</div> - <div className="documentButtonBar-button">{this.saveAnnoButton}</div> <div className="documentButtonBar-button">{this.recordButton}</div> <div className="documentButtonBar-button">{this.calendarButton}</div> <div className="documentButtonBar-button">{this.keywordButton}</div> diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 13807c25f..5cacde0d4 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -499,31 +499,20 @@ export class InkStrokeProperties { const inkView = DocumentView.getDocumentView(inkDoc); const inkStroke = inkView?.ComponentView as InkingStroke; const { inkData } = inkStroke.inkScaledData(); - - const result = inkData.length > 2 && GestureUtils.GestureRecognizer.Recognize([inkData]); - console.log(result); - if (result && (result.Name === 'line' ? result.Score > 0.92 : result.Score > 0.85)) { - switch (result.Name) { - case Gestures.Line: - case Gestures.Triangle: - case Gestures.Rectangle: - case Gestures.Circle: - GestureOverlay.makeBezierPolygon(inkData, result.Name, true); - break; - default: - } - } else { - const polylinePoints = inkData.filter((pt, index) => { return index % 4 === 0 || pt === inkData.lastElement()}).map(pt => { return { x: pt.X, y: pt.Y }; }); // prettier-ignore - if (polylinePoints.length > 2) { - const toKeep = simplify(polylinePoints, tolerance).map(pt => {return { X: pt.x, Y: pt.y }}); // prettier-ignore - for (var i = 4; i < inkData.length - 3; i += 4) { - const contains = toKeep.find(pt => pt.X === inkData[i].X && pt.Y === inkData[i].Y); - if (!contains) { - this._currentPoint = i; - inkView && this.deletePoints(inkView, false); - } + const polylinePoints = inkData.filter((pt, index) => { return index % 4 === 0 || pt === inkData.lastElement()}).map(pt => { return { x: pt.X, y: pt.Y }; }); // prettier-ignore + if (polylinePoints.length > 2) { + const toKeep = simplify(polylinePoints, tolerance).map(pt => {return { X: pt.x, Y: pt.y }}); // prettier-ignore + for (var i = 4; i < inkData.length - 3; i += 4) { + const contains = toKeep.find(pt => pt.X === inkData[i].X && pt.Y === inkData[i].Y); + if (!contains) { + this._currentPoint = i; + inkView && this.deletePoints(inkView, false); } } + // close the curve if the first and last points are really close (based on tolerance) + if (!InkingStroke.IsClosed(inkData) && Math.sqrt((inkData.lastElement().X - inkData[0].X) ** 2 + (inkData.lastElement().Y - inkData[0].Y) ** 2) <= tolerance * 2) { + inkData[inkData.length - 1] = inkData[0]; + } } }); }, 'smooth ink stroke'); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 26a695b43..fcac4d464 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -991,12 +991,12 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps 'Smooth Amount', '', 1, - Math.max(20, this.smoothAmt), + Math.max(10, this.smoothAmt), this.smoothAmt, (val: number) => { !isNaN(val) && (this.smoothAmt = val); }, - 20, + 10, 1 )} </div> @@ -1021,7 +1021,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps doc[DocData].stroke_markerScale = Number(value); }); } - @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '10'); } // prettier-ignore + @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '5'); } // prettier-ignore set smoothAmt(value) { this.selectedDoc && (this.selectedDoc[DocData].stroke_smoothAmount = Number(value)); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 57b2d1ccf..ccce2662a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -42,6 +42,7 @@ import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { OpenWhere, OpenWhereMod } from '../../nodes/OpenWhere'; import { PinDocView, PinProps } from '../../PinFuncs'; +import { AnnotationPalette } from '../../smartdraw/AnnotationPalette'; import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler'; import { StyleProp } from '../../StyleProp'; import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; @@ -1267,21 +1268,22 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this._drawing.push(inkDoc); }); const collection = MarqueeView.getCollection(this._drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 }); + console.log('is error here'); return collection; }; /** * Part of regenerating a drawing--deletes the old drawing. */ - removeDrawing = (doc?: Doc) => { + removeDrawing = (useLastContainer: boolean, doc?: Doc) => { this._batch = UndoManager.StartBatch('regenerateDrawing'); - if (doc) { + if (useLastContainer && this._drawingContainer) { + this._props.removeDocument?.(this._drawingContainer); + } else if (doc) { const docData = doc[DocData]; const children = DocListCast(docData.data); this._props.removeDocument?.(doc); this._props.removeDocument?.(children); - } else { - if (this._drawingContainer) this._props.removeDocument?.(this._drawingContainer); } this._drawing = []; }; @@ -1292,6 +1294,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string) => { const docData = doc[DocData]; docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text; + docData.width = opts.size; docData.drawingInput = opts.text; docData.drawingComplexity = opts.complexity; docData.drawingColored = opts.autoColor; @@ -2010,6 +2013,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }), icon: 'pen-to-square', }); + optionItems.push({ + description: this.Document.savedAsAnno ? 'Saved as Annotation!' : 'Save to Annotation Palette', + event: action(undoable(async () => await AnnotationPalette.addToPalette(this.Document), 'save to palette')), + icon: this.Document.savedAsAnno ? 'clipboard-check' : 'file-arrow-down', + }); this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', diff --git a/src/client/views/smartdraw/AnnotationPalette.scss b/src/client/views/smartdraw/AnnotationPalette.scss index 9f875f61a..4f11e8afc 100644 --- a/src/client/views/smartdraw/AnnotationPalette.scss +++ b/src/client/views/smartdraw/AnnotationPalette.scss @@ -8,3 +8,49 @@ border-radius: 5px; margin: auto; } + +.palette-create { + display: flex; + flex-direction: row; + width: 170px; + + .palette-create-input { + color: black; + width: 170px; + } +} + +.palette-create-options { + display: flex; + flex-direction: row; + justify-content: space-between; + width: 170px; + margin-top: 5px; + + .palette-color { + display: flex; + flex-direction: column; + align-items: center; + width: 40px; + } + + .palette-detail, + .palette-size { + display: flex; + flex-direction: column; + align-items: center; + width: 60px; + } +} + +.palette-buttons { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.palette-save-reset { + display: flex; + flex-direction: row; +} diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx index a2d6cc88d..0c8dbf12d 100644 --- a/src/client/views/smartdraw/AnnotationPalette.tsx +++ b/src/client/views/smartdraw/AnnotationPalette.tsx @@ -23,11 +23,22 @@ import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { FieldView } from '../nodes/FieldView'; import './AnnotationPalette.scss'; import { DrawingOptions, SmartDrawHandler } from './SmartDrawHandler'; +import { ImageField } from '../../../fields/URLField'; +import { Copy } from '../../../fields/FieldSymbols'; interface AnnotationPaletteProps { Document: Doc; } +/** + * The AnnotationPalette can be toggled in the lightbox view of a document. The goal of the palette + * is to offer an easy way for users to save then drag and drop repeated annotations onto a document. + * These annotations can be of any annotation type and operate similarly to user templates. + * + * On the "add" side of the palette, there is a way to create a drawing annotation with GPT. Users can + * enter the item to draw, toggle different settings, then GPT will generate three versions of the drawing + * to choose from. These drawings can then be saved to the palette as annotations. + */ @observer export class AnnotationPalette extends ObservableReactComponent<AnnotationPaletteProps> { @observable private _paletteMode: 'create' | 'view' = 'view'; @@ -107,20 +118,25 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett */ public static addToPalette = async (doc: Doc) => { if (!doc.savedAsAnno) { - Doc.MakeClone(doc).then(cloneMap => - DocumentView.getDocumentView(doc) - ?.ComponentView?.updateIcon?.(true) - .then(() => { - const { clone } = cloneMap; - clone.title = doc.title; - const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href; - Doc.AddDocToList(Doc.MyAnnos, 'data', makeUserTemplateImage(clone, image)); - doc.savedAsAnno = true; - }) - ); + const docView = DocumentView.getDocumentView(doc); + await docView?.ComponentView?.updateIcon?.(true); + const { clone } = await Doc.MakeClone(doc); + clone.title = doc.title; + const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href; + Doc.AddDocToList(Doc.MyAnnos, 'data', makeUserTemplateImage(clone, image)); + doc.savedAsAnno = true; } }; + public static getIcon(group: Doc) { + const docView = DocumentView.getDocumentView(group); + if (docView) { + docView.ComponentView?.updateIcon?.(true); + return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); + } + return undefined; + } + /** * Calls the draw with GPT functions in SmartDrawHandler to allow users to generate drawings straight from * the annotation palette. @@ -172,6 +188,8 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett docData.drawingSize = this._opts.size; docData.drawingData = this._gptRes[cIndex]; docData.width = this._opts.size; + docData.x = this._opts.x; + docData.y = this._opts.y; await AnnotationPalette.addToPalette(focusedDrawing); this.resetPalette(true); }; @@ -206,12 +224,12 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett )} {this._paletteMode === 'create' && ( <> - <div style={{ display: 'flex', flexDirection: 'row', width: '170px' }}> + <div className="palette-create"> <input + className="palette-create-input" aria-label="label-input" id="new-label" type="text" - style={{ color: 'black', width: '170px' }} value={this._userInput} onChange={e => { this.setUserInput(e.target.value); @@ -228,8 +246,8 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett onClick={this.generateDrawings} /> </div> - <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', width: '170px', marginTop: '5px' }}> - <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '40px' }}> + <div className="palette-create-options"> + <div className="palette-color"> Color <Switch sx={{ @@ -246,7 +264,7 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett onChange={() => this.setColor(!this._opts.autoColor)} /> </div> - <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '60px' }}> + <div className="palette-detail"> Detail <Slider sx={{ @@ -275,7 +293,7 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett valueLabelDisplay="auto" /> </div> - <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '60px' }}> + <div className="palette-size"> Size <Slider sx={{ @@ -325,9 +343,9 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} /> - <div style={{ width: '100%', display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}> + <div className="palette-buttons"> <Button text="Back" tooltip="Back to All Annotations" icon={<FontAwesomeIcon icon="reply" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(true)} /> - <div style={{ display: 'flex', flexDirection: 'row' }}> + <div className="palette-save-reset"> <Button tooltip="Save" icon={<FontAwesomeIcon icon="file-arrow-down" />} color={SettingsManager.userColor} onClick={this.saveDrawing} /> <Button tooltip="Reset" icon={<FontAwesomeIcon icon="rotate-left" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(false)} /> </div> diff --git a/src/client/views/smartdraw/SmartDrawHandler.scss b/src/client/views/smartdraw/SmartDrawHandler.scss index 6d402a80f..0e8bd3349 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.scss +++ b/src/client/views/smartdraw/SmartDrawHandler.scss @@ -1,3 +1,44 @@ .smart-draw-handler { position: absolute; } + +.smartdraw-input { + color: black; +} + +.smartdraw-options { + display: flex; + flex-direction: row; + justify-content: space-around; + + .auto-color { + display: flex; + flex-direction: column; + justify-content: center; + width: 30%; + } + + .complexity { + display: flex; + flex-direction: column; + justify-content: center; + width: 31%; + } + + .size { + display: flex; + flex-direction: column; + justify-content: center; + width: 39%; + + .size-slider { + width: 80%; + } + } +} + +.regenerate-box, +.edit-box { + display: flex; + flex-direction: row; +} diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index 8271c0959..e362c0c89 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -12,7 +12,6 @@ import { Doc, DocListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { InkData, InkField, InkTool } from '../../../fields/InkField'; import { BoolCast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; -import { ImageField } from '../../../fields/URLField'; import { GPTCallType, gptAPICall, gptDrawingColor } from '../../apis/gpt/GPT'; import { Docs } from '../../documents/Documents'; import { SettingsManager } from '../../util/SettingsManager'; @@ -34,6 +33,21 @@ export interface DrawingOptions { y: number; } +/** + * The SmartDrawHandler allows users to generate drawings with GPT from text input. Users are able to enter + * the item to draw, how complex they want the drawing to be, how large the drawing should be, and whether + * it will be colored. If the drawing is colored, GPT will automatically define the stroke and fill of each + * stroke. Drawings are retrieved from GPT as SVG code then converted into Dash-supported Beziers. + * + * The handler is selected from the ink tools menu. To generate a drawing, users can click anywhere on the freeform + * canvas and a popup will appear that prompts them to create a drawing. Once the drawing is created, users have + * the option to regenerate or edit the drawing. + * + * When each drawing is created, it is added to Dash as a group of ink strokes. The group is tagged with metadata + * for user input, the drawing's SVG code, and its settings (size, complexity). In the context menu -> 'Options', + * users can then show the drawing editor and regenerate/edit them at any point in the future. + */ + @observer export class SmartDrawHandler extends ObservableReactComponent<object> { static Instance: SmartDrawHandler; @@ -65,8 +79,19 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { SmartDrawHandler.Instance = this; } + /** + * 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.) + */ public AddDrawing: (doc: Doc, opts: DrawingOptions, gptRes: string) => void = unimplementedFunction; - public RemoveDrawing: (doc?: Doc) => 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, + * creates ink documents for each stroke, then adds the strokes to a collection. This can also be redefined by other + * 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 CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions) => { const drawing: Doc[] = []; strokeList.forEach((stroke: [InkData, string, string]) => { @@ -101,6 +126,11 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { this._display = true; }; + /** + * This is called in two places: 1. In this class, where the regenerate popup shows as soon as a + * drawing is created to replace the original smart draw popup. 2. From the context menu to make + * the regenerate popup show by user command. + */ @action displayRegenerate = (x: number, y: number) => { this._selectedDoc = DocumentView.SelectedDocs()?.lastElement(); @@ -113,6 +143,9 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { this._lastInput = { text: StrCast(docData.drawingInput), complexity: NumCast(docData.drawingComplexity), size: NumCast(docData.drawingSize), autoColor: BoolCast(docData.drawingColored), x: this._pageX, y: this._pageY }; }; + /** + * Hides the smart draw handler and resets its fields to their default. + */ @action hideSmartDrawHandler = () => { this.ShowRegenerate = false; @@ -126,6 +159,9 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { Doc.ActiveTool = InkTool.None; }; + /** + * Hides the popup that allows users to regenerate a drawing and resets its corresponding fields. + */ @action hideRegenerate = () => { if (!this._isLoading) { @@ -136,12 +172,20 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { } }; + /** + * This allows users to press the return/enter key to send input. + */ handleKeyPress = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { this.handleSendClick(); } }; + /** + * This is called when a user hits "send" on the draw with GPT popup. It calls the drawWithGPT or regenerate + * functions depending on what mode is currently displayed, then sets various observable fields that facilitate + * what the user sees. + */ @action handleSendClick = async () => { this._isLoading = true; @@ -171,7 +215,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { }; /** - * Calls GPT API to create a drawing based on user input + * 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) => { @@ -182,6 +226,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { console.error('GPT call failed'); return; } + console.log(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); @@ -213,7 +258,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { 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(this._selectedDoc); + 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; @@ -223,7 +268,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { }; /** - * Parses the svg code that GPT returns into Bezier curves. + * 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) => { @@ -307,26 +352,18 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { }} icon={<FontAwesomeIcon icon="xmark" />} color={SettingsManager.userColor} - style={{ width: '19px' }} /> <input aria-label="Smart Draw Input" className="smartdraw-input" type="text" autoFocus - style={{ color: 'black' }} value={this._userInput} onChange={action(e => this._canInteract && (this._userInput = e.target.value))} placeholder="Enter item to draw" onKeyDown={this.handleKeyPress} /> - <IconButton - tooltip="Advanced Options" - icon={<FontAwesomeIcon icon={this._showOptions ? 'caret-down' : 'caret-right'} />} - color={SettingsManager.userColor} - style={{ width: '14px' }} - onClick={action(() => (this._showOptions = !this._showOptions))} - /> + <IconButton tooltip="Advanced Options" icon={<FontAwesomeIcon icon={this._showOptions ? 'caret-down' : 'caret-right'} />} color={SettingsManager.userColor} onClick={action(() => (this._showOptions = !this._showOptions))} /> <Button style={{ alignSelf: 'flex-end' }} text="Send" @@ -337,8 +374,8 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { /> </div> {this._showOptions && ( - <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-around' }}> - <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '30%' }}> + <div className="smartdraw-options"> + <div className="auto-color"> Auto color <Switch sx={{ @@ -351,7 +388,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { onChange={action(e => this._canInteract && (this._autoColor = !this._autoColor))} /> </div> - <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '31%' }}> + <div className="complexity"> Complexity <Slider sx={{ @@ -369,15 +406,15 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { valueLabelDisplay="auto" /> </div> - <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '39%' }}> + <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` } }, }} - style={{ width: '80%' }} min={50} max={700} step={10} @@ -403,7 +440,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor, }}> - <div style={{ display: 'flex', flexDirection: 'row' }}> + <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'} />} @@ -412,12 +449,11 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { /> <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={action(() => (this._showEditBox = !this._showEditBox))} /> {this._showEditBox && ( - <div style={{ display: 'flex', flexDirection: 'row' }}> + <div className="edit-box"> <input aria-label="Edit instructions input" className="smartdraw-input" type="text" - style={{ color: 'black' }} value={this._regenInput} onChange={action(e => this._canInteract && (this._regenInput = e.target.value))} onKeyDown={this.handleKeyPress} |