diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 28 | ||||
-rw-r--r-- | src/client/util/bezierFit.ts | 1 | ||||
-rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 25 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 2 | ||||
-rw-r--r-- | src/client/views/LightboxView.tsx | 7 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 10 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 18 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx | 3 | ||||
-rw-r--r-- | src/client/views/smartdraw/AnnotationPalette.scss | 10 | ||||
-rw-r--r-- | src/client/views/smartdraw/AnnotationPalette.tsx | 337 | ||||
-rw-r--r-- | src/client/views/smartdraw/DrawingPalette.scss | 11 | ||||
-rw-r--r-- | src/client/views/smartdraw/DrawingPalette.tsx | 89 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.scss | 3 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 95 | ||||
-rw-r--r-- | src/fields/Doc.ts | 6 |
15 files changed, 493 insertions, 152 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 141695d86..24a5de42b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -162,6 +162,33 @@ export class CurrentUserUtils { const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts); } + + static setupAnnoPalette(doc: Doc, field="myAnnos") { + const reqdOpts:DocumentOptions = { + title: "Saved Annotations", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, hidden: false, + _dragOnlyWithinContainer: true, layout_hideContextMenu: true, isSystem: true, _forceActive: true, + _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, + }; + const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; + const savedAnnos = DocCast(doc[field]); + return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.MasonryDocument(items??[], opts), reqdOpts, DocListCast(savedAnnos?.data), reqdScripts); + } + + // static setupNoteTemplates(doc: Doc, field="template_notes") { + // const tempNotes = DocCast(doc[field]); + // const reqdTempOpts:DocumentOptions[] = [ + // { title: "Postit", backgroundColor: "yellow", icon: "sticky-note", _layout_showTitle: "title", layout_borderRounding: "5px"}, + // { title: "Idea", backgroundColor: "pink", icon: "lightbulb" , _layout_showTitle: "title"}, + // { title: "Topic", backgroundColor: "lightblue", icon: "book-open" , _layout_showTitle: "title"}]; + // const reqdNoteList = [...reqdTempOpts.map(opts => { + // const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true}; + // const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(fdoc => fdoc.title === opts.title): undefined; + // return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts)); + // }), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))]; + + // const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true }; + // // eslint-disable-next-line no-return-assign + // r // setup templates for different document types when they are iconified from Document Decorations static setupDefaultIconTemplates(doc: Doc, field="template_icons") { @@ -986,6 +1013,7 @@ pie title Minerals in my tap water this.setupTopbarButtons(doc); this.setupDockedButtons(doc); // the bottom bar of font icons this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left + this.setupAnnoPalette(doc); this.setupDocTemplates(doc); // sets up the template menu of templates // sthis.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption DocUtils.AssignDocField(doc, "globalScriptDatabase", () => Docs.Prototypes.MainScriptDocument(), {}); diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts index f5696afaf..98a6feea0 100644 --- a/src/client/util/bezierFit.ts +++ b/src/client/util/bezierFit.ts @@ -627,7 +627,6 @@ export function GenerateControlPoints(coordinates: Point[], alpha = 0.1) { } export function SVGToBezier(name: SVGType, attributes: any): Point[] { - console.log('in svg to bezier', name, attributes); switch (name) { case 'line': const x1 = parseInt(attributes.x1); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 487868169..177546fdc 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -12,7 +12,7 @@ import * as React from 'react'; import { FaEdit } from 'react-icons/fa'; import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc } from '../../fields/Doc'; +import { Doc, DocListCast } from '../../fields/Doc'; import { Cast, DocCast } from '../../fields/Types'; import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CalendarManager } from '../util/CalendarManager'; @@ -31,6 +31,8 @@ import { DocumentLinksButton } from './nodes/DocumentLinksButton'; import { DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; +import { AnnotationPalette } from './smartdraw/AnnotationPalette'; +import { DocData } from '../../fields/DocSymbols'; @observer export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: any }> { @@ -241,6 +243,26 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( ); } + @observable _annoSaved: boolean = false; + @action + saveAnno = (targetDoc: Doc) => { + targetDoc.savedAsAnno = true; + this._annoSaved = true; + AnnotationPalette.Instance.saveAnno(this.view0, targetDoc); + }; + @computed + get saveAnnoButton() { + const targetDoc = this.view0?.Document; + if (targetDoc && targetDoc.savedAsAnno) this._annoSaved = true; + return !targetDoc ? null : ( + <Tooltip title={<div className="dash-tooltip">{this._annoSaved ? 'Saved as Annotation!' : 'Save to Annotation Palette'}</div>}> + <div className="documentButtonBar-icon" style={{ color: 'white' }} onClick={() => this.saveAnno(targetDoc)}> + <FontAwesomeIcon className="documentdecorations-icon" icon={this._annoSaved ? 'clipboard-check' : 'file-arrow-down'} /> + </div> + </Tooltip> + ); + } + @computed get shareButton() { const targetDoc = this.view0?.Document; @@ -450,6 +472,7 @@ 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> {!Doc.UserDoc().documentLinksButton_fullMenu ? null : <div className="documentButtonBar-button">{this.shareButton}</div>} diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 7d01bbabb..562827db5 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -162,7 +162,7 @@ export class KeyManager { case 'delete': case 'backspace': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - if (DocumentView.LightboxDoc()) { + if (DocumentView.LightboxDoc() && !DocumentView.Selected().length) { DocumentView.SetLightboxDoc(undefined); DocumentView.DeselectAll(); } else if (!window.getSelection()?.toString()) DocumentDecorations.Instance.onCloseClick(true); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index e93e4949b..dcd5a61c7 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -23,7 +23,7 @@ import { DocumentView } from './nodes/DocumentView'; import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { OverlayView } from './OverlayView'; -import { DrawingPalette } from './smartdraw/DrawingPalette'; +import { AnnotationPalette } from './smartdraw/AnnotationPalette'; interface LightboxViewProps { PanelWidth: number; @@ -73,6 +73,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { DocumentView._lightboxContains = LightboxView.Contains; DocumentView._lightboxDoc = LightboxView.LightboxDoc; } + /** * Sets the root Doc to render in the lightbox view. * @param doc @@ -105,6 +106,8 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { this._history = []; Doc.ActiveTool = InkTool.None; SnappingManager.SetExploreMode(false); + AnnotationPalette.Instance.displayPalette(false); + this._showPalette = false; } DocumentView.DeselectAll(); if (future) { @@ -206,6 +209,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { }; togglePalette = () => { this._showPalette = !this._showPalette; + AnnotationPalette.Instance.displayPalette(this._showPalette); }; togglePen = () => { Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; @@ -327,7 +331,6 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { {toggleBtn('lightboxView-paletteBtn', 'toggle annotation palette', this._showPalette === true, 'palette', '', this.togglePalette)} {toggleBtn('lightboxView-penBtn', 'toggle pen annotation', Doc.ActiveTool === InkTool.Pen, 'pen', '', this.togglePen)} {toggleBtn('lightboxView-exploreBtn', 'toggle navigate only mode', SnappingManager.ExploreMode, 'globe-americas', '', this.toggleExplore)} - {this._showPalette && <DrawingPalette />} </div> ); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 30720a3a2..2e7cb1102 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -77,6 +77,7 @@ import { AnchorMenu } from './pdf/AnchorMenu'; import { GPTPopup } from './pdf/GPTPopup/GPTPopup'; import { TopBar } from './topbar/TopBar'; import { SmartDrawHandler } from './smartdraw/SmartDrawHandler'; +import { AnnotationPalette } from './smartdraw/AnnotationPalette'; const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore const _global = (window /* browser */ || global) /* node */ as any; @@ -380,6 +381,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faXmark, fa.faExclamation, fa.faFileAlt, + fa.faFileArrowDown, fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, @@ -434,6 +436,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faBold, fa.faItalic, fa.faClipboard, + fa.faClipboardCheck, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, @@ -481,6 +484,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, + fa.faSquarePlus, fa.faListUl, fa.faWindowMinimize, fa.faWindowRestore, @@ -576,6 +580,9 @@ export class MainView extends ObservableReactComponent<{}> { Doc.linkFollowUnhighlight(); AudioBox.Enabled = true; const targets = document.elementsFromPoint(e.x, e.y); + const targetClasses: string[] = targets.map(target => { + return target.className.toString(); + }); if (targets.length) { let targClass = targets[0].className.toString(); for (let i = 0; i < targets.length - 1; i++) { @@ -583,6 +590,8 @@ export class MainView extends ObservableReactComponent<{}> { else break; } !targClass.includes('contextMenu') && ContextMenu.Instance.closeMenu(); + !targetClasses.includes('marqueeView') && !targetClasses.includes('smart-draw-handler') && SmartDrawHandler.Instance.hideSmartDrawHandler(); + !targetClasses.includes('smart-draw-handler') && SmartDrawHandler.Instance.hideRegenerate(); !['timeline-menu-desc', 'timeline-menu-item', 'timeline-menu-input'].includes(targClass) && TimelineMenu.Instance.closeMenu(); } }); @@ -1106,6 +1115,7 @@ export class MainView extends ObservableReactComponent<{}> { <GPTPopup key="gptpopup" /> <SchemaCSVPopUp key="schemacsvpopup" /> <GenerativeFill imageEditorOpen={ImageEditor.Open} imageEditorSource={ImageEditor.Source} imageRootDoc={ImageEditor.RootDoc} addDoc={ImageEditor.AddDoc} /> + <AnnotationPalette /> </div> ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8f8cb9083..3cd1e99ef 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1280,8 +1280,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; _drawing: Doc[] = []; + _drawingContainer: Doc | undefined = undefined; @undoBatch - createDrawing = (e: React.PointerEvent<Element>, strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => { + createDrawing = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => { + this._drawing = []; + this._drawingContainer = undefined; strokeData.forEach((stroke: [InkData, string, string]) => { const bounds = InkField.getBounds(stroke[0]); const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); @@ -1305,12 +1308,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const collection = this._marqueeViewRef.current?.collection(undefined, true, this._drawing); if (collection) { const docData = collection[DocData]; - docData.title = opts.text; + docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[0] || opts.text; docData.drawingInput = opts.text; docData.drawingComplexity = opts.complexity; docData.drawingColored = opts.autoColor; docData.drawingSize = opts.size; docData.drawingData = gptRes; + this._drawingContainer = collection; } this._batch?.end(); }; @@ -1318,14 +1322,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection removeDrawing = (doc?: Doc) => { this._batch = UndoManager.StartBatch('regenerateDrawing'); if (doc) { - const docData: Doc = doc[DocData]; - const children = docData.data as unknown as Doc[]; - this._props.removeDocument?.(doc); + const docData = DocCast(doc[DocData]); + const children = DocListCast(docData.data); this._props.removeDocument?.(children); + this._props.removeDocument?.(doc); } else { this._props.removeDocument?.(this._drawing); + if (this._drawingContainer) this._props.removeDocument?.(this._drawingContainer); } - this._drawing = []; }; @action @@ -2032,7 +2036,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection icon: 'eye', }); optionItems.push({ - description: (this._showDrawingEditor ? 'Close' : 'Show') + ' Drawing Editor', + description: 'Show Drawing Editor', event: action(() => { this._showDrawingEditor = !this._showDrawingEditor; this._showDrawingEditor ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, this.createDrawing, this.removeDrawing) : SmartDrawHandler.Instance.hideRegenerate(); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index f02cd9d45..b3fdd9379 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -3,6 +3,7 @@ import { IconButton } from 'browndash-components'; import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { Doc } from '../../../../fields/Doc'; import { unimplementedFunction } from '../../../../Utils'; import { SettingsManager } from '../../../util/SettingsManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; @@ -12,7 +13,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> { // eslint-disable-next-line no-use-before-define static Instance: MarqueeOptionsMenu; - public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => void = unimplementedFunction; + public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean, selection?: Doc[]) => Doc | void = unimplementedFunction; public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; public showMarquee: () => void = unimplementedFunction; diff --git a/src/client/views/smartdraw/AnnotationPalette.scss b/src/client/views/smartdraw/AnnotationPalette.scss new file mode 100644 index 000000000..9f875f61a --- /dev/null +++ b/src/client/views/smartdraw/AnnotationPalette.scss @@ -0,0 +1,10 @@ +.annotation-palette { + display: flex; + flex-direction: column; + align-items: center; + position: absolute; + right: 14px; + top: 50px; + border-radius: 5px; + margin: auto; +} diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx new file mode 100644 index 000000000..10e88e91e --- /dev/null +++ b/src/client/views/smartdraw/AnnotationPalette.tsx @@ -0,0 +1,337 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { flexibleCompare } from '@fullcalendar/core/internal'; +import { Slider, Switch } from '@mui/material'; +import { Button, IconButton } from 'browndash-components'; +import { data } from 'jquery'; +import { action, computed, makeObservable, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { AiOutlineSend } from 'react-icons/ai'; +import ReactLoading from 'react-loading'; +import { returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero } from '../../../ClientUtils'; +import { ActiveInkWidth, Doc, StrListCast } from '../../../fields/Doc'; +import { DocData } from '../../../fields/DocSymbols'; +import { InkData, InkField } from '../../../fields/InkField'; +import { BoolCast, DocCast } from '../../../fields/Types'; +import { emptyFunction } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; +import { CollectionViewType } from '../../documents/DocumentTypes'; +import { DragManager } from '../../util/DragManager'; +import { convertDropDataToButtons, makeUserTemplateButton } from '../../util/DropConverter'; +import { SettingsManager } from '../../util/SettingsManager'; +import { Transform } from '../../util/Transform'; +import { MarqueeOptionsMenu, MarqueeView } from '../collections/collectionFreeForm'; +import { CollectionGridView } from '../collections/collectionGrid'; +import { CollectionStackingView } from '../collections/CollectionStackingView'; +import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; +import { FieldViewProps } from '../nodes/FieldView'; +import { ObservableReactComponent } from '../ObservableReactComponent'; +import { DefaultStyleProvider } from '../StyleProvider'; +import './AnnotationPalette.scss'; +import { SmartDrawHandler } from './SmartDrawHandler'; + +@observer +export class AnnotationPalette extends ObservableReactComponent<{}> { + static Instance: AnnotationPalette; + @observable private _savedDrawings: Doc[] = []; + // @observable private _marqueeViewRef = React.createRef<MarqueeView>(); + @observable private _display: boolean = false; + @observable private _paletteMode: 'create' | 'view' = 'view'; + @observable private _userInput: string = ''; + @observable private _showDrawing: boolean = false; + @observable private _drawing: Doc | undefined = undefined; + @observable private _isLoading: boolean = false; + @observable private _detail: number = 5; + @observable private _size: number = 350; + @observable private _canInteract: boolean = true; + + constructor(props: any) { + super(props); + makeObservable(this); + AnnotationPalette.Instance = this; + } + + return170 = () => 170; + + @action + setPaletteMode = (mode: 'create' | 'view') => { + this._paletteMode = mode; + }; + + @action + setUserInput = (input: string) => { + this._userInput = input; + }; + + @action + setDetail = (detail: number) => { + this._detail = detail; + }; + + @action + setSize = (size: number) => { + this._size = size; + }; + + saveAnno = async (docView: DocumentView | undefined, doc: Doc) => { + const dragData = new DragManager.DocumentDragData([doc]); + // convertDropDataToButtons(dragData); + const clone = await Doc.MakeClone(doc); + clone.clone.title = doc.title; + const templateBtn = makeUserTemplateButton(clone.clone); + + // const cloneData: Doc = DocCast(clone.clone[DocData]); + // cloneData.dragFactory = doc; + Doc.AddDocToList(Doc.MyAnnos, 'data', templateBtn); + // const collection = this._marqueeViewRef.current?.collection(undefined, false, this._savedDrawings); + // if (docView) docView.ComponentView?.removeDocument?.(doc); + }; + + @action + displayPalette = (display: boolean) => { + this._display = display; + }; + + @action + generateDrawing = async () => { + this._isLoading = true; + try { + const drawingRes = await SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput); + const opts = drawingRes?.lastInput; + const drawing: Doc[] = []; + drawingRes?.data.forEach((stroke: [InkData, string, string]) => { + const bounds = InkField.getBounds(stroke[0]); + // const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); + const inkWidth = ActiveInkWidth(); + const inkDoc = Docs.Create.InkDocument( + 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, + stroke[1], + undefined, + stroke[2] === 'none' ? undefined : stroke[2] + ); + drawing.push(inkDoc); + }); + const collection = MarqueeOptionsMenu.Instance.createCollection(undefined, true, drawing); + if (collection) { + const docData = collection[DocData]; + docData.title = opts?.text; + docData.drawingInput = opts?.text; + docData.drawingComplexity = opts?.complexity; + docData.drawingColored = opts?.autoColor; + docData.drawingSize = opts?.size; + docData.drawingData = drawingRes?.lastRes; + this._drawing = collection; + } + this._showDrawing = true; + } catch (e) { + console.log('Error generating drawing'); + } + this._isLoading = false; + }; + + // @computed get drawingCreator() { + // return ( + // MarqueeOptionsMenu.Instance.createCollection(undefined, true, this._drawing); + // ); + // } + // return Docs.Create.FreeformDocument([], {}); + // Docs.Create. + // return ( + // <DocumentView + // Document={doc} + // addDocument={undefined} + // addDocTab={DocumentViewInternal.addDocTabFunc} + // pinToPres={DocumentView.PinDoc} + // containerViewPath={returnEmptyDoclist} + // styleProvider={DefaultStyleProvider} + // removeDocument={returnFalse} + // ScreenToLocalTransform={Transform.Identity} + // PanelWidth={this.return170} + // PanelHeight={this.return170} + // renderDepth={0} + // isContentActive={returnTrue} + // focus={emptyFunction} + // whenChildContentsActiveChanged={emptyFunction} + // childFilters={returnEmptyFilter} + // childFiltersByRanges={returnEmptyFilter} + // searchFilterDocs={returnEmptyDoclist} + // /> + // ); + + render() { + return !this._display ? null : ( + <div className="annotation-palette" style={{ zIndex: 1000 }}> + {this._paletteMode === 'view' && ( + <> + <DocumentView + Document={Doc.MyAnnos} + addDocument={undefined} + addDocTab={DocumentViewInternal.addDocTabFunc} + pinToPres={DocumentView.PinDoc} + containerViewPath={returnEmptyDoclist} + styleProvider={DefaultStyleProvider} + removeDocument={returnFalse} + ScreenToLocalTransform={Transform.Identity} + PanelWidth={this.return170} + PanelHeight={this.return170} + renderDepth={0} + isContentActive={returnTrue} + focus={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + /> + <Button + // style={{ alignSelf: 'center' }} + text="Add" + icon={<FontAwesomeIcon icon="square-plus" />} + // iconPlacement="" + color={SettingsManager.userColor} + onClick={() => this.setPaletteMode('create')} + /> + </> + )} + {this._paletteMode === 'create' && ( + <> + <div style={{ display: 'flex', flexDirection: 'row', width: '170px' }}> + {/* <IconButton + tooltip="Advanced Options" + icon={<FontAwesomeIcon icon="caret-right" />} + color={SettingsManager.userColor} + style={{ width: '14px' }} + // onClick={() => { + // this._showOptions = !this._showOptions; + // }} + /> */} + <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); + }} + placeholder="Enter item to draw" + // onKeyDown={this.handleKeyPress} + /> + <Button + style={{ alignSelf: 'flex-end' }} + // text="Send" + icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} + iconPlacement="right" + color={SettingsManager.userColor} + onClick={this.generateDrawing} + /> + </div> + <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', width: '170px', marginTop: '5px' }}> + <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '40px' }}> + 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.setAutoColor} + /> + </div> + <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '60px' }}> + Detail + <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._detail} + onChange={(e, val) => { + this.setDetail(val as number); + }} + valueLabelDisplay="auto" + /> + </div> + <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '60px' }}> + Size + <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.setSize(val as number); + }} + valueLabelDisplay="auto" + /> + </div> + </div> + {this._drawing !== undefined && ( + <DocumentView + Document={this._drawing} + addDocument={undefined} + addDocTab={DocumentViewInternal.addDocTabFunc} + pinToPres={DocumentView.PinDoc} + containerViewPath={returnEmptyDoclist} + styleProvider={DefaultStyleProvider} + removeDocument={returnFalse} + ScreenToLocalTransform={Transform.Identity} + PanelWidth={this.return170} + PanelHeight={this.return170} + renderDepth={0} + isContentActive={returnTrue} + focus={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + /> + )} + </> + )} + </div> + ); + } +} diff --git a/src/client/views/smartdraw/DrawingPalette.scss b/src/client/views/smartdraw/DrawingPalette.scss deleted file mode 100644 index 0f1152b71..000000000 --- a/src/client/views/smartdraw/DrawingPalette.scss +++ /dev/null @@ -1,11 +0,0 @@ -.drawing-palette { - display: grid; - grid-template-columns: auto; - position: absolute; - right: 14px; - width: 170px; - height: 170px; - top: 50px; - border-radius: 5px; - background-color: white; -} diff --git a/src/client/views/smartdraw/DrawingPalette.tsx b/src/client/views/smartdraw/DrawingPalette.tsx deleted file mode 100644 index 87a39bc85..000000000 --- a/src/client/views/smartdraw/DrawingPalette.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { computed, makeObservable, observable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { returnAll, returnFalse, returnOne, returnZero } from '../../../ClientUtils'; -import { Doc, StrListCast } from '../../../fields/Doc'; -import { emptyFunction } from '../../../Utils'; -import { CollectionViewType } from '../../documents/DocumentTypes'; -import { MarqueeView } from '../collections/collectionFreeForm'; -import { CollectionGridView } from '../collections/collectionGrid'; -import { CollectionStackingView } from '../collections/CollectionStackingView'; -import { DocumentView } from '../nodes/DocumentView'; -import { FieldViewProps } from '../nodes/FieldView'; -import { ObservableReactComponent } from '../ObservableReactComponent'; -import './DrawingPalette.scss'; - -@observer -export class DrawingPalette extends ObservableReactComponent<{}> { - @observable private _savedDrawings: Doc[] = []; - @observable _marqueeViewRef = React.createRef<MarqueeView>(); - private _stackRef = React.createRef<CollectionStackingView>(); - - constructor(props: any) { - super(props); - makeObservable(this); - } - - panelWidth = () => 100; - panelHeight = () => 100; - - getCollection = () => { - return this._marqueeViewRef.current?.collection(undefined, false, this._savedDrawings) || new Doc(); - }; - - @computed get savedDrawingAnnos() { - // const savedAnnos = Doc.MyDrawingAnnos; - return ( - <div className="collectionMenu-contMenuButtons" style={{ height: '100%' }}> - {/* <DocumentView PanelHeight={this.panelWidth} PanelWidth={this.panelHeight} Document={savedAnnos} renderDepth={2} isContentActive={returnFalse} childFilters={this.childFilters} /> */} - {/* <CollectionStackingView - {...this._props} - Document={savedAnnos} - // setContentViewBox={emptyFunction} - // NativeWidth={returnZero} - // NativeHeight={returnZero} - ref={this._stackRef} - PanelHeight={this.panelWidth} - PanelWidth={this.panelHeight} - // childFilters={this.childFilters} - // sortFunc={this.sortByLinkAnchorY} - // setHeight={this.setHeightCallback} - // isAnnotationOverlay={false} - // select={emptyFunction} - NativeDimScaling={returnOne} - // childlayout_showTitle={this.layout_showTitle} - isContentActive={returnFalse} - isSelected={returnFalse} - isAnyChildContentActive={returnFalse} - // childDocumentsActive={this._props.isContentActive} - whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} - childHideDecorationTitle - // ScreenToLocalTransform={this.screenToLocalTransform} - renderDepth={this._props.renderDepth + 1} - type_collection={CollectionViewType.Stacking} - // fieldKey={'drawing-palette'} - pointerEvents={returnAll} - /> */} - </div> - ); - } - - render() { - return ( - <div className="drawing-palette"> - {/* {this._savedDrawings.map(doc => { - return <DocumentView - Document={doc} - renderDepth={0} - PanelWidth={this.panelWidth} - PanelHeight={this.panelHeight} - isContentActive={this.isContentActive} />; - })} */} - {/* <CollectionGridView {...this._props} /> */} - {} - {/* <DocumentView Document={this.getCollection()} /> */} - {this.savedDrawingAnnos} - </div> - ); - } -} diff --git a/src/client/views/smartdraw/SmartDrawHandler.scss b/src/client/views/smartdraw/SmartDrawHandler.scss new file mode 100644 index 000000000..6d402a80f --- /dev/null +++ b/src/client/views/smartdraw/SmartDrawHandler.scss @@ -0,0 +1,3 @@ +.smart-draw-handler { + position: absolute; +} diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index d24cc9d50..24046bb83 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -7,15 +7,16 @@ 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 { InkData, InkTool } 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'; +import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; +import './SmartDrawHandler.scss'; export interface DrawingOptions { text: string; @@ -43,7 +44,8 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { @observable private _size: number = 200; @observable private _autoColor: boolean = true; @observable private _regenInput: string = ''; - private _addFunc: (e: React.PointerEvent<Element>, strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void = () => {}; + @observable private _canInteract: boolean = true; + private _addFunc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void = () => {}; private _deleteFunc: (doc?: Doc) => void = () => {}; private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 300, autoColor: true, x: 0, y: 0 }; private _lastResponse: string = ''; @@ -57,12 +59,12 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { @action setUserInput = (input: string) => { - this._userInput = input; + if (this._canInteract) this._userInput = input; }; @action setRegenInput = (input: string) => { - this._regenInput = input; + if (this._canInteract) this._regenInput = input; }; @action @@ -72,21 +74,21 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { @action setComplexity = (val: number) => { - this._complexity = val; + if (this._canInteract) this._complexity = val; }; @action setSize = (val: number) => { - this._size = val; + if (this._canInteract) this._size = val; }; @action setAutoColor = () => { - this._autoColor = !this._autoColor; + if (this._canInteract) this._autoColor = !this._autoColor; }; @action - displaySmartDrawHandler = (x: number, y: number, addFunc: (e: React.PointerEvent<Element>, strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => { + displaySmartDrawHandler = (x: number, y: number, addFunc: (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => { this._pageX = x; this._pageY = y; this._display = true; @@ -95,16 +97,18 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { }; @action - displayRegenerate = (x: number, y: number, addFunc: (e: React.PointerEvent<Element>, strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => { + displayRegenerate = (x: number, y: number, addFunc: (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => { this._selectedDoc = DocumentView.SelectedDocs().lastElement(); const docData = this._selectedDoc[DocData]; this._addFunc = addFunc; this._deleteFunc = deleteFunc; this._pageX = x; this._pageY = y; + this._display = false; 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 }; + this._showEditBox = false; + 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 }; }; @action @@ -117,7 +121,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { this._complexity = 5; this._size = 300; this._autoColor = true; - // this._regenInput = '' + Doc.ActiveTool = InkTool.None; }; @action @@ -127,12 +131,25 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { this._regenInput = ''; }; + @action + handleKeyPress = async (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + if (this._showRegenerate) { + this.regenerate(); + } else { + await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput); + this._userInput = ''; + } + } + }; + _errorOccurredOnce = false; @action - drawWithGPT = async (e: React.PointerEvent<Element>, input: string) => { + drawWithGPT = async (startPt: { X: number; Y: number }, 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._lastInput = { text: input, complexity: this._complexity, size: this._size, autoColor: this._autoColor, x: startPt.X, y: startPt.Y }; this._isLoading = true; + this._canInteract = false; this._showOptions = false; try { const res = await gptAPICall(`"${input}", "${this._complexity}", "${this._size}"`, GPTCallType.DRAW, undefined, true); @@ -141,20 +158,22 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { return; } console.log(res); - await this.parseResponse(e, res, { X: e.clientX, Y: e.clientY }, false); + const strokeData = await this.parseResponse(res, startPt, false); this.hideSmartDrawHandler(); this._showRegenerate = true; this._errorOccurredOnce = false; + this._isLoading = false; + this._canInteract = true; + return strokeData; } catch (err) { if (this._errorOccurredOnce) { console.error('GPT call failed', err); this._errorOccurredOnce = false; } else { this._errorOccurredOnce = true; - this.drawWithGPT(e, input); + this.drawWithGPT(startPt, input); } } - this._isLoading = false; }; @action @@ -163,14 +182,15 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { }; @action - regenerate = async (e: React.PointerEvent<Element>) => { + regenerate = async () => { this._isLoading = true; + this._canInteract = false; 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}`; + 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); } @@ -179,25 +199,26 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { return; } console.log(res); - this.parseResponse(e, res, { X: this._lastInput.x, Y: this._lastInput.y }, true); + this.parseResponse(res, { X: this._lastInput.x, Y: this._lastInput.y }, true); } catch (err) { console.error('GPT call failed', err); } this._isLoading = false; + this._canInteract = true; this._regenInput = ''; this._showEditBox = false; }; @action - parseResponse = async (e: React.PointerEvent<Element>, res: string, startPoint: { X: number; Y: number }, regenerate: boolean) => { + parseResponse = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean) => { const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g); + console.log(svg); 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([ @@ -210,10 +231,11 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { }); if (regenerate) { this._deleteFunc(this._selectedDoc); - this._addFunc(e, strokeData, this._lastInput, svg[0], this._selectedDoc); + this._addFunc(strokeData, this._lastInput, svg[0], this._selectedDoc); } else { - this._addFunc(e, strokeData, this._lastInput, svg[0]); + this._addFunc(strokeData, this._lastInput, svg[0]); } + return { data: strokeData, lastInput: this._lastInput, lastRes: svg[0] }; } }; @@ -222,7 +244,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { return ( <div id="label-handler" - className="contextMenu-cont" + className="smart-draw-handler" style={{ display: this._display ? '' : 'none', left: this._pageX, @@ -242,8 +264,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { style={{ width: '19px' }} /> <input - aria-label="label-input" - id="new-label" + aria-label="Smart Draw Input" + className="smartdraw-input" + id="smartdraw-input" type="text" style={{ color: 'black' }} value={this._userInput} @@ -251,7 +274,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { this.setUserInput(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={this.setShowOptions} /> <Button style={{ alignSelf: 'flex-end' }} text="Send" @@ -259,7 +284,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { iconPlacement="right" color={SettingsManager.userColor} onClick={e => { - this.drawWithGPT(e as React.PointerEvent<Element>, this._userInput); + this.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._userInput); }} /> </div> @@ -349,7 +374,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { return ( <div id="smartdraw-options-menu" - className="contextMenu-cont" + className="smart-draw-handler" style={{ left: this._pageX, ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }), @@ -365,9 +390,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { tooltip="Regenerate" icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />} color={SettingsManager.userColor} - onClick={e => { - this.regenerate(e as React.PointerEvent<Element>); - }} + onClick={this.regenerate} /> <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={this.edit} /> {this._showEditBox && ( @@ -378,6 +401,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { }}> <input aria-label="Edit instructions input" + className="smartdraw-input" id="regen-input" type="text" style={{ color: 'black' }} @@ -385,6 +409,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { onChange={e => { this.setRegenInput(e.target.value); }} + onKeyDown={this.handleKeyPress} placeholder="Edit instructions" /> <Button @@ -393,9 +418,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { icon={this._isLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} iconPlacement="right" color={SettingsManager.userColor} - onClick={e => { - this.regenerate(e as React.PointerEvent<Element>); - }} + onClick={this.regenerate} /> </div> )} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bc1abd26e..c86342870 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -276,7 +276,7 @@ export class Doc extends RefField { public static get MyPublishedDocs() { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs).concat(DocListCast(DocCast(Doc.UserDoc().myPublishedDocs)?.data)); } // prettier-ignore public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); } // prettier-ignore public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); } // prettier-ignore - public static get MyDrawingAnnos() { return DocCast(Doc.UserDoc().myDrawingAnnos); } // prettier-ignore + public static get MyAnnos() { return DocCast(Doc.UserDoc().myAnnos); } // prettier-ignore public static get MyImports() { return DocCast(Doc.UserDoc().myImports); } // prettier-ignore public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); } // prettier-ignore public static get MyTools() { return DocCast(Doc.UserDoc().myTools); } // prettier-ignore @@ -1741,8 +1741,8 @@ ScriptingGlobals.add(function getEmbedding(doc: any) { return Doc.MakeEmbedding(doc); }); // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) { - return doc.isTemplateDoc ? Doc.MakeDelegateWithProto(doc) : Doc.MakeCopy(doc, copyProto); +ScriptingGlobals.add(async function getCopy(doc: any, copyProto: any) { + return doc.isTemplateDoc ? (doc.isGroup ? (await Doc.MakeClone(doc)).clone : Doc.MakeDelegateWithProto(doc)) : Doc.MakeCopy(doc, copyProto); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function copyField(field: any) { |