From d3aebc8404685496673a8611a6d72ff7695ad191 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 18 Apr 2020 13:42:03 -0400
Subject: several pointerevent fixes broken by last change.  added scroll to
 target for webBox links.

---
 src/client/views/nodes/DocumentView.tsx | 118 ++++++++++++++++----------------
 1 file changed, 59 insertions(+), 59 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2f22488e4..2bae2fa96 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -80,6 +80,7 @@ export interface DocumentViewProps {
     ContentScaling: () => number;
     PanelWidth: () => number;
     PanelHeight: () => number;
+    pointerEvents?: boolean;
     focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void;
     parentActive: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
@@ -994,38 +995,42 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     screenToLocalTransform = () => this.props.ScreenToLocalTransform();
     @computed get contents() {
         TraceMobx();
-        return (<DocumentContentsView ContainingCollectionView={this.props.ContainingCollectionView}
-            ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-            NativeWidth={this.NativeWidth}
-            NativeHeight={this.NativeHeight}
-            Document={this.props.Document}
-            DataDoc={this.props.DataDoc}
-            LayoutDoc={this.props.LayoutDoc}
-            makeLink={this.makeLink}
-            rootSelected={this.rootSelected}
-            dontRegisterView={this.props.dontRegisterView}
-            fitToBox={this.props.fitToBox}
-            LibraryPath={this.props.LibraryPath}
-            addDocument={this.props.addDocument}
-            removeDocument={this.props.removeDocument}
-            moveDocument={this.props.moveDocument}
-            ScreenToLocalTransform={this.screenToLocalTransform}
-            renderDepth={this.props.renderDepth}
-            PanelWidth={this.panelWidth}
-            PanelHeight={this.panelHeight}
-            focus={this.props.focus}
-            parentActive={this.props.parentActive}
-            whenActiveChanged={this.props.whenActiveChanged}
-            bringToFront={this.props.bringToFront}
-            addDocTab={this.props.addDocTab}
-            pinToPres={this.props.pinToPres}
-            backgroundColor={this.props.backgroundColor}
-            ContentScaling={this.childScaling}
-            ChromeHeight={this.chromeHeight}
-            isSelected={this.isSelected}
-            select={this.select}
-            onClick={this.onClickHandler}
-            layoutKey={this.finalLayoutKey} />);
+        return (<>
+            <DocumentContentsView key={1} ContainingCollectionView={this.props.ContainingCollectionView}
+                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                NativeWidth={this.NativeWidth}
+                NativeHeight={this.NativeHeight}
+                Document={this.props.Document}
+                DataDoc={this.props.DataDoc}
+                LayoutDoc={this.props.LayoutDoc}
+                makeLink={this.makeLink}
+                rootSelected={this.rootSelected}
+                dontRegisterView={this.props.dontRegisterView}
+                fitToBox={this.props.fitToBox}
+                LibraryPath={this.props.LibraryPath}
+                addDocument={this.props.addDocument}
+                removeDocument={this.props.removeDocument}
+                moveDocument={this.props.moveDocument}
+                ScreenToLocalTransform={this.screenToLocalTransform}
+                renderDepth={this.props.renderDepth}
+                PanelWidth={this.panelWidth}
+                PanelHeight={this.panelHeight}
+                focus={this.props.focus}
+                parentActive={this.props.parentActive}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={this.props.bringToFront}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                backgroundColor={this.props.backgroundColor}
+                ContentScaling={this.childScaling}
+                ChromeHeight={this.chromeHeight}
+                isSelected={this.isSelected}
+                select={this.select}
+                onClick={this.onClickHandler}
+                layoutKey={this.finalLayoutKey} />
+            {this.anchors}
+        </>
+        );
     }
     linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document);
 
@@ -1049,20 +1054,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get anchors() {
         TraceMobx();
         return this.layoutDoc.presBox ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
-            <div className="documentView-linkAnchorBoxWrapper" key={d[Id]}>
-                <DocumentView {...this.props}
-                    Document={d}
-                    ContainingCollectionView={this.props.ContainingCollectionView}
-                    ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection  Need a better prop for passing the containing document to the LinkAnchorBox
-                    PanelWidth={this.anchorPanelWidth}
-                    PanelHeight={this.anchorPanelHeight}
-                    layoutKey={this.linkEndpoint(d)}
-                    ContentScaling={returnOne}
-                    backgroundColor={returnTransparent}
-                    removeDocument={this.hideLinkAnchor}
-                    LayoutDoc={undefined}
-                />
-            </div>);
+            <DocumentView {...this.props} key={i + 1}
+                Document={d}
+                ContainingCollectionView={this.props.ContainingCollectionView}
+                ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection  Need a better prop for passing the containing document to the LinkAnchorBox
+                PanelWidth={this.anchorPanelWidth}
+                PanelHeight={this.anchorPanelHeight}
+                layoutKey={this.linkEndpoint(d)}
+                ContentScaling={returnOne}
+                backgroundColor={returnTransparent}
+                removeDocument={this.hideLinkAnchor}
+                pointerEvents={false}
+                LayoutDoc={undefined}
+            />);
     }
     @computed get innards() {
         TraceMobx();
@@ -1101,22 +1105,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[showTitle] = value) ? true : true)}
                 />
             </div>);
-        return <>
-            {this.anchors}
-            {!showTitle && !showCaption ?
-                this.contents :
-                <div className="documentView-styleWrapper" >
-                    <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? `calc(100% - ${this.chromeHeight()}px)` : "100%", top: showTextTitle ? this.chromeHeight() : undefined }}>
-                        {this.contents}
-                    </div>
-                    {titleView}
-                    {captionView}
+        return !showTitle && !showCaption ?
+            this.contents :
+            <div className="documentView-styleWrapper" >
+                <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? `calc(100% - ${this.chromeHeight()}px)` : "100%", top: showTextTitle ? this.chromeHeight() : undefined }}>
+                    {this.contents}
                 </div>
-            }
-        </>;
+                {titleView}
+                {captionView}
+            </div>;
     }
     @computed get ignorePointerEvents() {
-        return (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || this.props.layoutKey?.includes("layout_key") || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
+        return this.props.pointerEvents === false || (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
     }
 
     @observable _animate = 0;
-- 
cgit v1.2.3-70-g09d2


From 9f8e76451b807ed8f51ef32aab9b2255dfccd525 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 18 Apr 2020 18:37:27 -0400
Subject: fixed pointer events for templated PDFs.  changed clicking on a
 document with a rootDocument to pass the event on up

---
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 +-
 src/client/views/nodes/DocumentView.tsx                   | 7 +++++--
 src/client/views/pdf/PDFViewer.scss                       | 5 ++---
 src/client/views/pdf/PDFViewer.tsx                        | 4 +++-
 4 files changed, 11 insertions(+), 7 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index e325a70c0..3a7e005ac 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -92,7 +92,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 height: this.height,
                 zIndex: this.ZInd,
                 display: this.ZInd === -99 ? "none" : undefined,
-                pointerEvents: this.props.Document.isBackground ? "none" : this.props.pointerEvents
+                pointerEvents: this.props.Document.isBackground ? "none" : this.props.pointerEvents ? "all" : undefined
             }} >
 
             {!this.props.fitToBox ?
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2bae2fa96..e3013698d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -315,7 +315,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             } else if (this.Document.isLinkButton) {
                 DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
             } else {
-                if ((this.props.Document.onDragStart || (this.props.Document.rootDocument && this.props.Document.isTemplateForField)) && !(e.ctrlKey || e.button > 0)) {  // onDragStart implies a button doc that we don't want to select when clicking.   RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
+                if ((this.props.Document.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) {  // onDragStart implies a button doc that we don't want to select when clicking.   RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
                 } else {
                     DocumentView._focusHack = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY) || [0, 0];
@@ -480,7 +480,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
             return;
         }
-        if (!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) {
+        if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) &&
+            // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
+            !((this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0))) {
             this._downX = e.clientX;
             this._downY = e.clientY;
             if ((this.active || this.Document.onDragStart || this.onClickHandler) &&
@@ -489,6 +491,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 !this.Document.lockedPosition &&
                 !this.Document.inOverlay) {
                 e.stopPropagation(); // events stop at the lowest document that is active.  if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
+
             }
             document.removeEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 26c31a80e..760f64a72 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -57,12 +57,11 @@
         display: inline-block;
         width:100%;
         pointer-events: none;
-    }
-    .pdfViewer-overlay-inking {
-        .collectionfreeformview-container {
+        .collectionFreeFormDocumentView-container {
             pointer-events: all;
         }
     }
+    
     .pdfViewer-annotationLayer {
         position: absolute;
         transform-origin: left top;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d54390236..10cfaa2f1 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -573,7 +573,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         clipDoc._scrollTop = this.marqueeY();
         const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
         Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
+        clipDoc.rootDocument = targetDoc;
         DocumentView.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined);
+        targetDoc.layoutKey = "layout";
         // const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
         // Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy]();
         // const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
@@ -642,7 +644,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0);
     @computed get overlayLayer() {
         return <div className={`pdfViewer-overlay${InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : ""}`} id="overlay"
-            style={{ transform: `scale(${this._zoomed})`, pointerEvents: this.active(false) ? "all" : undefined }}>
+            style={{ transform: `scale(${this._zoomed})` }}>
             <CollectionFreeFormView {...this.props}
                 LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
                 annotationsKey={this.annotationKey}
-- 
cgit v1.2.3-70-g09d2


From b7c1c6890c9186578ce06e8c31cdd82662d1c934 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 18 Apr 2020 23:25:15 -0400
Subject: changed structure of template docs in sidebar. got rid of special
 context menu in tree view in favor of pushing everything to document context
 menus which ca now be extended by the local document or through props.

---
 src/client/documents/Documents.ts                  |   1 +
 src/client/util/DocumentManager.ts                 |   2 +-
 src/client/views/MainView.scss                     |   2 +
 src/client/views/MainView.tsx                      |  15 +-
 .../views/collections/CollectionStackingView.tsx   |   2 +-
 .../views/collections/CollectionTreeView.tsx       |  46 ++----
 src/client/views/nodes/DocumentView.scss           |   1 -
 src/client/views/nodes/DocumentView.tsx            |  26 ++--
 .../authentication/models/current_user_utils.ts    | 159 ++++++++++++---------
 9 files changed, 136 insertions(+), 118 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 3ac823bb0..3073707a6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -79,6 +79,7 @@ export interface DocumentOptions {
     x?: number;
     y?: number;
     z?: number;
+    author?: string;
     dropAction?: dropActionType;
     childDropAction?: dropActionType;
     layoutKey?: string;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index e66e67723..4683e77a8 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -241,4 +241,4 @@ export class DocumentManager {
         }
     }
 }
-Scripting.addGlobal(function focus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); });
\ No newline at end of file
+Scripting.addGlobal(function DocFocus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); });
\ No newline at end of file
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index e9f2248ad..81d427f64 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -5,6 +5,7 @@
 .mainView-tabButtons {
     position: relative;
     width: 100%;
+    margin-top: 10px;
 }
 
 .mainContent-div {
@@ -72,6 +73,7 @@
     flex-direction: column;
     position: relative;
     height: 100%;
+    background: dimgray;
 
     .documentView-node-topmost {
         background: lightgrey;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index aec1f960a..40cabcf83 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faTerminal, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons';
+import { faTerminal, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -41,11 +41,12 @@ import { RadialMenu } from './nodes/RadialMenu';
 import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
+import { ScriptField } from '../../new_fields/ScriptField';
 
 @observer
 export class MainView extends React.Component {
     public static Instance: MainView;
-    private _buttonBarHeight = 35;
+    private _buttonBarHeight = 26;
     private _flyoutSizeOnDown = 0;
     private _urlState: HistoryUtil.DocUrl;
     private _docBtnRef = React.createRef<HTMLDivElement>();
@@ -102,7 +103,10 @@ export class MainView extends React.Component {
         }
 
         library.add(faTerminal);
+        library.add(faWindowMaximize);
         library.add(faFileAlt);
+        library.add(faAddressCard);
+        library.add(faQuestionCircle);
         library.add(faStickyNote);
         library.add(faFont);
         library.add(faExclamation);
@@ -212,6 +216,11 @@ export class MainView extends React.Component {
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
         const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().documents as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+
+        const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
+        mainDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!]);
+        mainDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors"]);
+
         Doc.AddDocToList(workspaces, "data", mainDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
         setTimeout(() => this.openWorkspace(mainDoc), 0);
@@ -459,7 +468,7 @@ export class MainView extends React.Component {
     }
 
     @computed get mainContent() {
-        const sidebar = this.userDoc && this.userDoc.sidebarContainer;
+        const sidebar = this.userDoc?.sidebarContainer;
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
             <div className="mainView-mainContent" style={{ color: this.darkScheme ? "rgb(205,205,205)" : "black" }} >
                 <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index dd84c4d6e..b15649d83 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -63,7 +63,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             const dxf = () => this.getDocTransform(d, dref.current!);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
             const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
-            const style = this.isStackingView ? { width: width(), marginTop: this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+            const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
             return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
                 {this.getDisplayDoc(d, (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc, dxf, width)}
             </div>;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5ff8c11ee..2df1614de 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -32,7 +32,7 @@ import { Templates } from '../Templates';
 import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import React = require("react");
-import { CollectionViewType } from './CollectionView';
+import { CollectionViewType, CollectionView } from './CollectionView';
 import { RichTextField } from '../../../new_fields/RichTextField';
 import { DocumentView } from '../nodes/DocumentView';
 
@@ -210,33 +210,6 @@ class TreeView extends React.Component<TreeViewProps> {
         })}
     />)
 
-    onWorkspaceContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view
-            const sort = this.props.document[`${this.fieldKey}-sortAscending`];
-            if (this.props.document === CurrentUserUtils.UserDocument.recentlyClosed) {
-                ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
-            } else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
-                ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
-                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab", this.props.libraryPath), icon: "folder" });
-                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath), icon: "caret-square-right" });
-                if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
-                    ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.props.document)), icon: "camera" });
-                }
-                ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
-            } else {
-                ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
-                ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
-            }
-            ContextMenu.Instance.addItem({ description: (sort ? "Sort Descending" : (sort === false ? "Unsort" : "Sort Ascending")), event: () => this.props.document[`${this.fieldKey}-sortAscending`] = (sort ? false : (sort === false ? undefined : true)), icon: "minus" });
-            ContextMenu.Instance.addItem({ description: "Toggle Theme Colors", event: () => this.props.document.darkScheme = !this.props.document.darkScheme, icon: "minus" });
-            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, "onRight"); }, icon: "layer-group" });
-            ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
-            ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
-            e.stopPropagation();
-            e.preventDefault();
-        }
-    }
-
     @undoBatch
     treeDrop = (e: Event, de: DragManager.DropEvent) => {
         const pt = [de.x, de.y];
@@ -356,7 +329,11 @@ class TreeView extends React.Component<TreeViewProps> {
             const remDoc = (doc: Doc) => this.remove(doc, expandKey);
             const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
             const docs = expandKey === "links" ? this.childLinks : this.childDocs;
-            return <ul key={expandKey + "more"}>
+            const sortKey = `${this.fieldKey}-sortAscending`;
+            return <ul key={expandKey + "more"} onClick={(e) => {
+                this.props.document[sortKey] = (this.props.document[sortKey] ? false : (this.props.document[sortKey] === false ? undefined : true));
+                e.stopPropagation();
+            }}>
                 {!docs ? (null) :
                     TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
                         this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
@@ -421,6 +398,12 @@ class TreeView extends React.Component<TreeViewProps> {
             {<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />}
         </div>;
     }
+
+    focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
+    contextMenuItems = () => {
+        const focusScript = ScriptField.MakeFunction(`DocFocus(self)`);
+        return [{ script: focusScript!, label: "Focus" }];
+    }
     /**
      * Renders the EditableView title element for placement into the tree.
      */
@@ -442,7 +425,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 })}>
                 {this.treeViewExpandedView}
             </span>);
-        const openRight = (<div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
+        const openRight = (<div className="treeViewItem-openRight" onClick={this.openRight}>
             <FontAwesomeIcon title="open in pane on right" icon="angle-right" size="lg" />
         </div>);
         return <>
@@ -474,6 +457,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         PanelHeight={returnZero}
                         NativeHeight={returnZero}
                         NativeWidth={returnZero}
+                        contextMenuItems={this.contextMenuItems}
                         renderDepth={1}
                         focus={emptyFunction}
                         parentActive={returnTrue}
@@ -491,7 +475,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     render() {
         setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0);
-        return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onContextMenu={this.onWorkspaceContextMenu}>
+        return <div className="treeViewItem-container" ref={this.createTreeDropTarget}>
             <li className="collection-child">
                 <div className="treeViewItem-header" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
                     {this.renderBullet}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 81ae36cc0..692493414 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -71,7 +71,6 @@
         display: inline-block;
         width: 100%;
         height: 100%;
-        pointer-events: none;
 
         .documentView-styleContentWrapper {
             width: 100%;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e3013698d..12f5b27e1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,21 +1,24 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, runInAction, trace, observable } from "mobx";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from "request-promise";
-import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
 import { Document, PositionDocument } from '../../../new_fields/documentSchemas';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { InkTool } from '../../../new_fields/InkField';
 import { RichTextField } from '../../../new_fields/RichTextField';
 import { listSpec } from "../../../new_fields/Schema";
+import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
 import { ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { AudioField, ImageField, PdfField, VideoField } from '../../../new_fields/URLField';
 import { TraceMobx } from '../../../new_fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, returnOne, returnTransparent, returnTrue, Utils, OmitKeys, returnZero } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
+import { ClientRecommender } from '../../ClientRecommender';
 import { DocServer } from "../../DocServer";
 import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -24,6 +27,7 @@ import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { Scripting } from '../../util/Scripting';
+import { SearchUtil } from '../../util/SearchUtil';
 import { SelectionManager } from "../../util/SelectionManager";
 import SharingManager from '../../util/SharingManager';
 import { Transform } from "../../util/Transform";
@@ -35,19 +39,13 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { DocComponent } from "../DocComponent";
 import { EditableView } from '../EditableView';
 import { InkingControl } from '../InkingControl';
+import { KeyphraseQueryView } from '../KeyphraseQueryView';
 import { OverlayView } from '../OverlayView';
-import { ScriptBox } from '../ScriptBox';
 import { ScriptingRepl } from '../ScriptingRepl';
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
-import React = require("react");
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
-import { ClientRecommender } from '../../ClientRecommender';
-import { SearchUtil } from '../../util/SearchUtil';
 import { RadialMenu } from './RadialMenu';
-import { KeyphraseQueryView } from '../KeyphraseQueryView';
-import { undo } from 'prosemirror-history';
+import React = require("react");
 
 library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
     fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -65,6 +63,7 @@ export interface DocumentViewProps {
     LayoutDoc?: () => Opt<Doc>;
     LibraryPath: Doc[];
     fitToBox?: boolean;
+    contextMenuItems?: () => { script: ScriptField, label: string }[];
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     onClick?: ScriptField;
     onPointerDown?: ScriptField;
@@ -720,6 +719,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const cm = ContextMenu.Instance;
         const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
 
+        const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
+        Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
+            cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
+        this.props.contextMenuItems?.().forEach(item =>
+            cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
         const existing = cm.findByDescription("Layout...");
         const layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
         layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: (e) => this.toggleBackground(false), icon: this.Document.lockedPosition ? "unlock" : "lock" });
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 32cce9608..e7a163a3e 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -18,6 +18,7 @@ import { makeTemplate } from "../../../client/util/DropConverter";
 import { RichTextField } from "../../../new_fields/RichTextField";
 import { PrefetchProxy } from "../../../new_fields/Proxy";
 import { FormattedTextBox } from "../../../client/views/nodes/FormattedTextBox";
+import { MainView } from "../../../client/views/MainView";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -51,7 +52,17 @@ export class CurrentUserUtils {
         ];
         doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" });
         Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues);
-        doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Layouts", _height: 75 }));
+        doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Layouts", _height: 75 }))
+        doc.templateButtons = Docs.Create.MasonryDocument(CurrentUserUtils.setupTemplateButtons(doc), {
+            title: "template buttons",
+            _pivotField: "author", _xMargin: 0,
+            _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+        });
+        doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([doc.noteTypes as any as Doc, doc.templateButtons as Doc, doc.clickFuncs as Doc], {
+            title: "template layouts", _xPadding: 0,
+            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
+        }));
     }
     static setupDefaultIconTypes(doc: Doc, buttons?: string[]) {
         doc.iconView = new PrefetchProxy(Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(this)") }));
@@ -86,7 +97,7 @@ export class CurrentUserUtils {
             // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
             // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc },
             // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc },
-            { title: "Drag a search box", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
+            { title: "Drag a search box", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
             { title: "Drag a document previewer", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
             // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" },
         ];
@@ -94,6 +105,7 @@ export class CurrentUserUtils {
             _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
             icon: data.icon,
             title: data.title,
+            author: "Draggable Items",
             ignoreClick: data.ignoreClick,
             dropAction: data.click ? "copy" : undefined,
             onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
@@ -105,22 +117,40 @@ export class CurrentUserUtils {
         }));
     }
 
+    static setupTemplateButtons(doc: Doc) {
+        const queryTemplate = Docs.Create.MulticolumnDocument(
+            [
+                Docs.Create.QueryDocument({ title: "query", _height: 200, forceActive: true }),
+                Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true, forceActive: true })
+            ],
+            { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true });
+        queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
+        const slideTemplate = Docs.Create.MultirowDocument(
+            [
+                Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, forceActive: true }),
+                Docs.Create.TextDocument("", { title: "text", _height: 100, forceActive: true })
+            ],
+            { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true });
+        slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
+        const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" });
+        Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description");
+        descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView");
+
+        doc.slidesBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "address-card" });
+        doc.descriptionBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: descriptionTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize" });
+        doc.queryBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: queryTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "question-circle" });
+
+        return [doc.slidesBtn as Doc, doc.descriptionBtn as Doc, doc.queryBtn as Doc];
+    }
+
+
     static async updateCreatorButtons(doc: Doc) {
-        const toolsBtn = await Cast(doc.ToolsBtn, Doc);
-        if (toolsBtn) {
-            const stackingDoc = await Cast(toolsBtn.sourcePanel, Doc);
-            if (stackingDoc) {
-                const stackdocs = await Cast(stackingDoc.data, listSpec(Doc));
-                if (stackdocs) {
-                    const dragset = await Cast(stackdocs[0], Doc);
-                    if (dragset) {
-                        const dragdocs = await Cast(dragset.data, listSpec(Doc));
-                        if (dragdocs) {
-                            const dragDocs = await Promise.all(dragdocs);
-                            this.setupCreatorButtons(doc, dragDocs.map(d => StrCast(d.title))).map(nb => Doc.AddDocToList(dragset, "data", nb));
-                        }
-                    }
-                }
+        const dragset = await Cast(doc.dragCreators, Doc);
+        if (dragset) {
+            const dragdocs = await Cast(dragset.data, listSpec(Doc));
+            if (dragdocs) {
+                const dragDocs = await Promise.all(dragdocs);
+                this.setupCreatorButtons(doc, dragDocs.map(d => StrCast(d.title))).map(nb => Doc.AddDocToList(dragset, "data", nb));
             }
         }
     }
@@ -203,9 +233,16 @@ export class CurrentUserUtils {
     // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker.  when clicked, this panel will be displayed in the target container (ie, sidebarContainer)  
     static setupToolsPanel(sidebarContainer: Doc, doc: Doc) {
         // setup a masonry view of all he creators
-        const dragCreators = Docs.Create.MasonryDocument(CurrentUserUtils.setupCreatorButtons(doc), {
-            _width: 500, _autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons",
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), _yMargin: 5
+        const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc);
+        doc.dragCreators = Docs.Create.MasonryDocument(creatorBtns, {
+            title: "drag Creators",
+            _pivotField: "author", _xMargin: 0,
+            _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+        });
+        doc.allCreators = Docs.Create.StackingDocument([doc.dragCreators as any as Doc, doc.templateButtons as any as Doc], {
+            title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
+            _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
         });
         // setup a color picker
         const color = Docs.Create.ColorDocument({
@@ -215,7 +252,7 @@ export class CurrentUserUtils {
         return Docs.Create.ButtonDocument({
             _width: 35, _height: 25, title: "Tools", fontSize: 10, targetContainer: sidebarContainer,
             letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-            sourcePanel: Docs.Create.StackingDocument([dragCreators, color], {
+            sourcePanel: Docs.Create.StackingDocument([doc.allCreators as Doc, color], {
                 _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true
             }),
             onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"),
@@ -228,6 +265,9 @@ export class CurrentUserUtils {
         doc.workspaces = Docs.Create.TreeDocument([], {
             title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true,
         });
+        const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
+        (doc.workspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
+        (doc.workspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
 
         doc.documents = Docs.Create.TreeDocument([], {
             title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
@@ -237,11 +277,14 @@ export class CurrentUserUtils {
         doc.recentlyClosed = Docs.Create.TreeDocument([], {
             title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
         });
+        const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
+        (doc.recentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+        (doc.recentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
 
         return Docs.Create.ButtonDocument({
             _width: 50, _height: 25, title: "Library", fontSize: 10,
             letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-            sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, Docs.Prototypes.MainLinkDocument(), doc, doc.recentlyClosed as Doc], {
+            sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc, doc], {
                 title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true
             }),
             targetContainer: sidebarContainer,
@@ -279,54 +322,28 @@ export class CurrentUserUtils {
         }));
     }
 
-    /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
-    static setupExpandingButtons(doc: Doc) {
-        const queryTemplate = Docs.Create.MulticolumnDocument(
-            [
-                Docs.Create.QueryDocument({ title: "query", _height: 200, forceActive: true }),
-                Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true, forceActive: true })
-            ],
-            { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true });
-        queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
-        const slideTemplate = Docs.Create.MultirowDocument(
-            [
-                Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, forceActive: true }),
-                Docs.Create.TextDocument("", { title: "text", _height: 100, forceActive: true })
-            ],
-            { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true });
-        slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
-        const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" });
-        Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description");
-        descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView");
+    static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
+        ...opts,
+        _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", forceActive: true,
+        dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+        backgroundColor: "black", treeViewPreventOpen: true, lockedPosition: true, _chromeStatus: "disabled", linearViewIsExpanded: true
+    })) as any as Doc;
 
-        const ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
-            ...opts,
-            dropAction: "alias", removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100
-        })) as any as Doc;
-        const blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
-            ...opts,
-            _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", forceActive: true,
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-            backgroundColor: "black", treeViewPreventOpen: true, lockedPosition: true, _chromeStatus: "disabled", linearViewIsExpanded: true
-        })) as any as Doc;
+    static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
+        ...opts,
+        dropAction: "alias", removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100
+    })) as any as Doc;
 
-        doc.penBtn = ficon({
+    /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
+    static setupExpandingButtons(doc: Doc) {
+        doc.penBtn = CurrentUserUtils.ficon({
             onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"),
-            title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen,  this)`), activePen: doc
+            author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen,  this)`), activePen: doc
         })
-        doc.undoBtn = ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
-        doc.redoBtn = ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
-        doc.slidesBtn = ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "sticky-note" });
-        doc.descriptionBtn = ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: descriptionTemplate, removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "sticky-note" });
-        doc.queryBtn = ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: queryTemplate, removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "sticky-note" });
-        doc.templateButtons = blist({ title: "template buttons", ignoreClick: true }, [doc.slidesBtn as Doc, doc.descriptionBtn as Doc, doc.queryBtn as Doc]);
-        doc.expandingButtons = blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc, doc.templateButtons as Doc]);
-        doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([doc.noteTypes as Doc, doc.templateButtons as Doc, doc.clickFuncs as Doc], {
-            title: "template layouts", _xPadding: 0,
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
-        }));
+        doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
+        doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
+        doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]);
     }
-
     // sets up the default set of documents to be shown in the Overlay layer
     static setupOverlays(doc: Doc) {
         doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" }));
@@ -356,14 +373,15 @@ export class CurrentUserUtils {
     static updateUserDocument(doc: Doc) {
         doc.title = Doc.CurrentUserEmail;
         new InkingControl();
-        (doc.iconTypes === undefined) && CurrentUserUtils.setupDefaultIconTypes(doc);
-        (doc.noteTypes === undefined) && CurrentUserUtils.setupDefaultDocTemplates(doc);
         (doc.childClickFuncs === undefined) && CurrentUserUtils.setupChildClicks(doc);
+        (doc.iconTypes === undefined) && CurrentUserUtils.setupDefaultIconTypes(doc);
+        (doc.templateDocs === undefined) && CurrentUserUtils.setupDefaultDocTemplates(doc);
         (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc);
         (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc);
         (doc.expandingButtons === undefined) && CurrentUserUtils.setupExpandingButtons(doc);
         (doc.curPresentation === undefined) && CurrentUserUtils.setupDefaultPresentation(doc);
         (doc.sidebarButtons === undefined) && CurrentUserUtils.setupSidebarButtons(doc);
+        doc.linkDb = Docs.Prototypes.MainLinkDocument();
 
         // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved.
         PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
@@ -375,9 +393,9 @@ export class CurrentUserUtils {
         PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
         PromiseValue(Cast(doc.sidebarButtons, Doc)).then(stackingDoc => {
             stackingDoc && PromiseValue(Cast(stackingDoc.data, listSpec(Doc))).then(sidebarButtons => {
-                sidebarButtons && sidebarButtons.map((sidebarBtn, i) => {
-                    sidebarBtn && PromiseValue(Cast(sidebarBtn, Doc)).then(async btn => {
-                        btn && btn.sourcePanel && btn.targetContainer && i === 1 && (btn.onClick as ScriptField).script.run({ this: btn });
+                sidebarButtons?.map((sidebarBtn, i) => {
+                    sidebarBtn && PromiseValue(Cast(sidebarBtn, Doc)).then(async btn => { // choose which item to display first in sidebar panel (i === 0,1, or 2)
+                        btn?.sourcePanel && btn.targetContainer && i === 2 && (btn.onClick as ScriptField).script.run({ this: btn });
                     });
                 });
             });
@@ -427,3 +445,4 @@ export class CurrentUserUtils {
 
 Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); });
 Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); });
+Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 34ee553af7b2133632b66f966c07fd1bb1085e81 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 19 Apr 2020 00:30:23 -0400
Subject: changed tree view to show context menu icon to avoid right-clicking

---
 src/Utils.ts                                       | 29 +++++++++++++++
 src/client/views/DocumentDecorations.tsx           | 33 ++---------------
 .../views/collections/CollectionTreeView.scss      | 31 +++++++++-------
 .../views/collections/CollectionTreeView.tsx       | 42 ++++++++++++----------
 src/client/views/nodes/DocumentView.tsx            |  6 ++--
 5 files changed, 76 insertions(+), 65 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 58f272ba5..9acdc8731 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -470,6 +470,35 @@ export function clearStyleSheetRules(sheet: any) {
     return false;
 }
 
+export function simulateMouseClick(element: Element, x: number, y: number, sx: number, sy: number) {
+    ["pointerdown", "pointerup"].map(event => element.dispatchEvent(
+        new PointerEvent(event, {
+            view: window,
+            bubbles: true,
+            cancelable: true,
+            button: 2,
+            pointerType: "mouse",
+            clientX: x,
+            clientY: y,
+            screenX: sx,
+            screenY: sy,
+        })));
+
+    element.dispatchEvent(
+        new MouseEvent("contextmenu", {
+            view: window,
+            bubbles: true,
+            cancelable: true,
+            button: 2,
+            clientX: x,
+            clientY: y,
+            movementX: 0,
+            movementY: 0,
+            screenX: sx,
+            screenY: sy,
+        }));
+}
+
 export function setupMoveUpEvents(
     target: object,
     e: React.PointerEvent,
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c49fe157c..9ba418d74 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -8,7 +8,7 @@ import { PositionDocument } from '../../new_fields/documentSchemas';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { Cast, StrCast, NumCast } from "../../new_fields/Types";
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils";
+import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils";
 import { DocUtils } from "../documents/Documents";
 import { DocumentType } from '../documents/DocumentTypes';
 import { DragManager } from "../util/DragManager";
@@ -142,40 +142,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action onSettingsDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, () => false, (e) => { }, this.onSettingsClick);
     }
-
-    simulateMouseClick(element: Element, x: number, y: number, sx: number, sy: number) {
-        ["pointerdown", "pointerup"].map(event => element.dispatchEvent(
-            new PointerEvent(event, {
-                view: window,
-                bubbles: true,
-                cancelable: true,
-                button: 2,
-                pointerType: "mouse",
-                clientX: x,
-                clientY: y,
-                screenX: sx,
-                screenY: sy,
-            })));
-
-        element.dispatchEvent(
-            new MouseEvent("contextmenu", {
-                view: window,
-                bubbles: true,
-                cancelable: true,
-                button: 2,
-                clientX: x,
-                clientY: y,
-                movementX: 0,
-                movementY: 0,
-                screenX: sx,
-                screenY: sy,
-            }));
-    }
     @action onSettingsClick = (e: PointerEvent): void => {
         if (e.button === 0 && !e.altKey && !e.ctrlKey) {
             let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0];
             while (child.children.length && child.className !== "jsx-parser") child = child.children[0];
-            this.simulateMouseClick(child.children[0], e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+            simulateMouseClick(child.children[0], e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
         }
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 8e95f7fbe..1e59c493f 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -100,10 +100,29 @@
     border-left: dashed 1px #00000042;
 }
 
+.treeViewItem-header {
+    border: transparent 1px solid;
+    display: flex;
+
+    .editableView-container-editing-oneLine {
+        min-width: 15px;
+    }
+    .documentView-node-topmost {
+        width: unset;
+    }
+    > svg {
+        display: none;
+    }
+
+}
+
 .treeViewItem-header:hover {
     .collectionTreeView-keyHeader {
         display: inherit;
     }
+    > svg {
+        display: inherit;
+    }
 
     .treeViewItem-openRight {
         display: inline-block;
@@ -119,18 +138,6 @@
     }
 }
 
-.treeViewItem-header {
-    border: transparent 1px solid;
-    display: flex;
-
-    .editableView-container-editing-oneLine {
-        min-width: 15px;
-    }
-    .documentView-node-topmost {
-        width: unset;
-    }
-}
-
 .treeViewItem-header-above {
     border-top: black 1px solid;
 }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 2df1614de..c2838a15e 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -10,7 +10,7 @@ import { Document, listSpec, createSchema, makeInterface } from '../../../new_fi
 import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue } from '../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue, simulateMouseClick } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from '../../util/DocumentManager';
@@ -182,16 +182,12 @@ class TreeView extends React.Component<TreeViewProps> {
         SetValue={undoBatch((value: string) => {
             Doc.SetInPlace(this.props.document, key, value, false) || true;
             Doc.SetInPlace(this.props.document, "editTitle", undefined, false);
-            //this.props.document.editTitle = undefined;
         })}
         OnFillDown={undoBatch((value: string) => {
             Doc.SetInPlace(this.props.document, key, value, false);
             const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
-            //EditableView.loadId = doc[Id];
             Doc.SetInPlace(this.props.document, "editTitle", undefined, false);
-            // this.props.document.editTitle = undefined;
             Doc.SetInPlace(this.props.document, "editTitle", true, false);
-            //doc.editTitle = true;
             return this.props.addDocument(doc);
         })}
         onClick={() => {
@@ -399,11 +395,16 @@ class TreeView extends React.Component<TreeViewProps> {
         </div>;
     }
 
+    showContextMenu = (e: React.MouseEvent) => {
+        simulateMouseClick(this._docRef.current!.ContentDiv!, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+        e.stopPropagation();
+    }
     focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
     contextMenuItems = () => {
         const focusScript = ScriptField.MakeFunction(`DocFocus(self)`);
         return [{ script: focusScript!, label: "Focus" }];
     }
+    _docRef = React.createRef<DocumentView>();
     /**
      * Renders the EditableView title element for placement into the tree.
      */
@@ -413,18 +414,21 @@ class TreeView extends React.Component<TreeViewProps> {
         const editTitle = ScriptField.MakeFunction("setInPlace(this, 'editTitle', true)");
 
         const headerElements = (
-            <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
-                onPointerDown={action(() => {
-                    if (this.treeViewOpen) {
-                        this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" :
-                            this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
-                                this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
-                                    this.childDocs ? this.fieldKey : "fields";
-                    }
-                    this.treeViewOpen = true;
-                })}>
-                {this.treeViewExpandedView}
-            </span>);
+            <>
+                <FontAwesomeIcon icon="cog" size="sm" onClick={e => this.showContextMenu(e)}></FontAwesomeIcon>
+                <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
+                    onPointerDown={action(() => {
+                        if (this.treeViewOpen) {
+                            this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" :
+                                this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
+                                    this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
+                                        this.childDocs ? this.fieldKey : "fields";
+                        }
+                        this.treeViewOpen = true;
+                    })}>
+                    {this.treeViewExpandedView}
+                </span>
+            </>);
         const openRight = (<div className="treeViewItem-openRight" onClick={this.openRight}>
             <FontAwesomeIcon title="open in pane on right" icon="angle-right" size="lg" />
         </div>);
@@ -440,6 +444,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 {Doc.GetT(this.props.document, "editTitle", "boolean", true) ?
                     this.editableView("title") :
                     <DocumentView
+                        ref={this._docRef}
                         Document={this.props.document}
                         DataDoc={undefined}
                         LibraryPath={this.props.libraryPath || []}
@@ -474,6 +479,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     render() {
+        const sorting = this.props.document[`${this.fieldKey}-sortAscending`];
         setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0);
         return <div className="treeViewItem-container" ref={this.createTreeDropTarget}>
             <li className="collection-child">
@@ -481,7 +487,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     {this.renderBullet}
                     {this.renderTitle}
                 </div>
-                <div className="treeViewItem-border">
+                <div className="treeViewItem-border" style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
                     {!this.treeViewOpen || this.props.renderedIds.indexOf(this.props.document[Id]) !== -1 ? (null) : this.renderContent}
                 </div>
             </li>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 12f5b27e1..d114698ea 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -823,11 +823,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             icon: "brain"
         });
 
-        cm.addItem({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" });
-
-
-        moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
         moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
+        moreItems.push({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" });
+        moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
         moreItems.push({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" });
         !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
         runInAction(() => {
-- 
cgit v1.2.3-70-g09d2


From ea359c9d247a9be59b78ed043be6427ab10cb8bd Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 19 Apr 2020 01:40:54 -0400
Subject: added optional labels for font icons.

---
 src/client/documents/Documents.ts                  |  1 +
 .../views/collections/CollectionTreeView.tsx       |  8 +++++-
 src/client/views/nodes/DocumentView.tsx            |  4 ++-
 src/client/views/nodes/FontIconBox.scss            | 12 +++++++++
 src/client/views/nodes/FontIconBox.tsx             |  1 +
 .../authentication/models/current_user_utils.ts    | 29 +++++++++++-----------
 6 files changed, 39 insertions(+), 16 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 3073707a6..209a72d52 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -85,6 +85,7 @@ export interface DocumentOptions {
     layoutKey?: string;
     type?: string;
     title?: string;
+    label?: string; // short form of title for use as an icon label
     style?: string;
     page?: number;
     scale?: number;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c2838a15e..cd1e23bbd 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -483,7 +483,13 @@ class TreeView extends React.Component<TreeViewProps> {
         setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0);
         return <div className="treeViewItem-container" ref={this.createTreeDropTarget}>
             <li className="collection-child">
-                <div className="treeViewItem-header" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+                <div className="treeViewItem-header" ref={this._header} onClick={e => {
+                    e.stopPropagation();
+                    e.preventDefault();
+                }} onPointerDown={e => {
+                    e.stopPropagation();
+                    e.preventDefault();
+                }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
                     {this.renderBullet}
                     {this.renderTitle}
                 </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d114698ea..d8178ea77 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1172,7 +1172,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 <div className="documentView-contentBlocker" />
             </> :
                 this.innards}
-            {(this.Document.isBackground !== undefined || this.isSelected(false)) && this.props.renderDepth > 0 ? <div className="documentView-lock" onClick={() => this.toggleBackground(true)}> <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} size="lg" /> </div> : (null)}
+            {(this.Document.isBackground !== undefined || this.isSelected(false)) && this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ?
+                <div className="documentView-lock" onClick={() => this.toggleBackground(true)}> <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} size="lg" /> </div>
+                : (null)}
         </div>;
         { this._showKPQuery ? <KeyphraseQueryView keyphrases={this._queries}></KeyphraseQueryView> : undefined; }
     }
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index f0fe7a54e..68b00a5be 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -8,6 +8,18 @@
     border-radius: 100%;
     transform-origin: top left;
 
+    .fontIconBox-label {
+        background: gray;
+        color:white;
+        margin-left: -10px;
+        border-radius: 8px;
+        width:100%;
+        position: absolute;
+        text-align: center;
+        font-size: 8px;
+        margin-top:4px;
+    }
+
     svg {
         width: 95% !important;
         height: 95%;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 9329cf210..c6ea6a882 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -57,6 +57,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
                 boxShadow: this.props.Document.ischecked ? `4px 4px 12px black` : undefined
             }}>
             <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={this._foregroundColor} size="sm" />
+            {!this.rootDoc.label ? (null) : <div className="fontIconBox-label"> {StrCast(this.rootDoc.label).substring(0, 5)} </div>}
         </button>;
     }
 }
\ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index e7a163a3e..7e5f7257a 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -80,31 +80,32 @@ export class CurrentUserUtils {
         const emptyPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
         const emptyCollection = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
         doc.activePen = doc;
-        const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
-            { title: "Drag a collection", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection },
-            { title: "Drag a web page", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", {_width: 300, _height: 300, title: "New Webpage" })' },
-            { title: "Drag a cat image", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' },
-            { title: "Drag a screenshot", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' },
-            { title: "Drag a webcam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
-            { title: "Drag a audio recorder", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
-            { title: "Drag a clickabl button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
-            { title: "Drag a presentation view", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation },
-            { title: "Drag a scripting box", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
-            { title: "Drag an import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
-            { title: "Drag a mobile view", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
+        const docProtoData: { title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
+            { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection },
+            { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", {_width: 300, _height: 300, title: "New Webpage" })' },
+            { title: "Drag a cat image", label: "Img", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' },
+            { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' },
+            { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
+            { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
+            { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
+            { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation },
+            { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
+            { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
+            { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
             // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen,  this)`, activePen: doc },
             // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
             // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
             // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc },
             // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc },
-            { title: "Drag a search box", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
-            { title: "Drag a document previewer", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
+            { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
+            { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
             // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" },
         ];
         return docProtoData.filter(d => !alreadyCreatedButtons?.includes(d.title)).map(data => Docs.Create.FontIconDocument({
             _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
             icon: data.icon,
             title: data.title,
+            label: data.label,
             author: "Draggable Items",
             ignoreClick: data.ignoreClick,
             dropAction: data.click ? "copy" : undefined,
-- 
cgit v1.2.3-70-g09d2


From a8438bd233d497a4471c4e0776c0f32b8c4ff8fd Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 19 Apr 2020 02:24:22 -0400
Subject: fixed locked position to stop propagation.  fixed rootSelected to
 only propage if rootDoc is not == this

---
 src/client/views/nodes/DocumentView.tsx | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d8178ea77..5c1326b1a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -479,15 +479,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
             return;
         }
+        this._downX = e.clientX;
+        this._downY = e.clientY;
         if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) &&
             // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
             !((this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0))) {
-            this._downX = e.clientX;
-            this._downY = e.clientY;
             if ((this.active || this.Document.onDragStart || this.onClickHandler) &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !this.Document.lockedPosition &&
                 !this.Document.inOverlay) {
                 e.stopPropagation(); // events stop at the lowest document that is active.  if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
 
@@ -992,7 +991,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return typeof fallback === "string" ? fallback : "layout";
     }
     rootSelected = (outsideReaction?: boolean) => {
-        return this.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected?.(outsideReaction));
+        return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction));
     }
     childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
     panelWidth = () => this.props.PanelWidth();
-- 
cgit v1.2.3-70-g09d2


From bdf4ac9601e54bf8e2a3a8f988c97274d84ae8a4 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 19 Apr 2020 13:30:20 -0400
Subject: fixed removing docs from tree view with menu.  restructured
 current_user_utils completely

---
 src/client/views/MainView.tsx                      |  11 +-
 .../views/collections/CollectionTreeView.tsx       |   6 +-
 src/client/views/nodes/AudioBox.tsx                |   5 +-
 src/client/views/nodes/DocumentView.tsx            |   6 +-
 src/client/views/nodes/WebBox.tsx                  |   4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 .../authentication/models/current_user_utils.ts    | 562 +++++++++++++--------
 src/server/database.ts                             |   3 -
 8 files changed, 369 insertions(+), 230 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 40cabcf83..0877cc5f6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -61,7 +61,7 @@ export class MainView extends React.Component {
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
-    @computed public get sidebarButtonsDoc() { return Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; }
+    @computed public get sidebarButtonsDoc() { return Cast(this.userDoc["tabs-buttons"], Doc) as Doc; }
 
     public isPointerDown = false;
 
@@ -399,15 +399,14 @@ export class MainView extends React.Component {
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
 
     @computed get flyout() {
-        const sidebarContent = this.userDoc?.sidebarContainer;
+        const sidebarContent = this.userDoc?.["tabs-panelContainer"];
         if (!(sidebarContent instanceof Doc)) {
             return (null);
         }
-        const sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc;
         return <div className="mainView-flyoutContainer" >
-            <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px`, backgroundColor: StrCast(sidebarButtonsDoc.backgroundColor) }}>
+            <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px`, backgroundColor: StrCast(this.sidebarButtonsDoc.backgroundColor) }}>
                 <DocumentView
-                    Document={sidebarButtonsDoc}
+                    Document={this.sidebarButtonsDoc}
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
                     addDocument={undefined}
@@ -468,7 +467,7 @@ export class MainView extends React.Component {
     }
 
     @computed get mainContent() {
-        const sidebar = this.userDoc?.sidebarContainer;
+        const sidebar = this.userDoc?.["tabs-panelContainer"];
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
             <div className="mainView-mainContent" style={{ color: this.darkScheme ? "rgb(205,205,205)" : "black" }} >
                 <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index cd1e23bbd..c243d8f29 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -139,6 +139,9 @@ class TreeView extends React.Component<TreeViewProps> {
     @undoBatch @action remove = (document: Document, key: string) => {
         return Doc.RemoveDocFromList(this.dataDoc, key, document);
     }
+    @undoBatch @action removeDoc = (document: Document) => {
+        return Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), document);
+    }
 
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this._treedropDisposer && this._treedropDisposer();
@@ -455,7 +458,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         onClick={this.props.onChildClick || editTitle}
                         dropAction={this.props.dropAction}
                         moveDocument={this.props.moveDocument}
-                        removeDocument={undefined}
+                        removeDocument={this.removeDoc}
                         ScreenToLocalTransform={this.getTransform}
                         ContentScaling={returnOne}
                         PanelWidth={returnZero}
@@ -778,6 +781,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
     }
 
     render() {
+        if (!(this.props.Document instanceof Doc)) return (null);
         const dropAction = StrCast(this.props.Document.childDropAction) as dropActionType;
         const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 7078cc01c..8f40ea2be 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -20,6 +20,7 @@ import { DocumentView } from "./DocumentView";
 import { Docs } from "../../documents/Documents";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Networking } from "../../Network";
+import { Upload } from "../../../server/SharedMediaTypes";
 
 // testing testing 
 
@@ -146,7 +147,9 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
         AudioBox.ActiveRecordings.push(this.props.Document);
         this._recorder.ondataavailable = async (e: any) => {
             const [{ result }] = await Networking.UploadFilesToServer(e.data);
-            this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+            if (!(result instanceof Error)) {
+                this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+            }
         };
         this._recordStart = new Date().getTime();
         runInAction(() => this.audioState = "recording");
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5c1326b1a..ca4ec7aff 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -560,14 +560,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     static findTemplate(templateName: string, type: string, signature: string) {
         let docLayoutTemplate: Opt<Doc>;
-        const iconViews = DocListCast(Cast(Doc.UserDoc().iconViews, Doc, null)?.data);
+        const iconViews = DocListCast(Cast(Doc.UserDoc()["icon-view-all"], Doc, null)?.data);
         const templBtns = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data);
         const noteTypes = DocListCast(Cast(Doc.UserDoc().noteTypes, Doc, null)?.data);
         const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data);
         const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc);
         // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document.  but type is not reliable and there could be other types of template searches so this should be generalized
         // first try to find a template that matches the specific document type (<typeName>_<templateName>).  otherwise, fallback to a general match on <templateName>
-        !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === type + "_" + templateName && (docLayoutTemplate = tempDoc));
+        !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc));
         !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc));
         return docLayoutTemplate;
     }
@@ -991,7 +991,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return typeof fallback === "string" ? fallback : "layout";
     }
     rootSelected = (outsideReaction?: boolean) => {
-        return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction));
+        return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     }
     childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
     panelWidth = () => this.props.PanelWidth();
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 36907c4a7..66ddf64c9 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -68,7 +68,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         this._setPreviewCursor?.(e.screenX, e.screenY, false);
     }
     iframeScrolled = (e: any) => {
-        const scroll = (e.target as any)?.children?.[0].scrollTop;
+        const scroll = e.target?.children?.[0].scrollTop;
         this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scroll;
     }
     async componentDidMount() {
@@ -345,7 +345,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 </div>}
         </>);
     }
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop))
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop));
     render() {
         return (<div className={`webBox-container`}
             style={{
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 10cfaa2f1..46b60b554 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -553,7 +553,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     highlight = (color: string) => {
         // creates annotation documents for current highlights
         const annotationDoc = this.makeAnnotationDocument(color);
-        annotationDoc && this.props?.addDocument(annotationDoc);
+        annotationDoc && this.props.addDocument?.(annotationDoc);
         return annotationDoc;
     }
 
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 7e5f7257a..3d68d8425 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -19,6 +19,7 @@ import { RichTextField } from "../../../new_fields/RichTextField";
 import { PrefetchProxy } from "../../../new_fields/Proxy";
 import { FormattedTextBox } from "../../../client/views/nodes/FormattedTextBox";
 import { MainView } from "../../../client/views/MainView";
+import { DocumentType } from "../../../client/documents/DocumentTypes";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -35,60 +36,151 @@ export class CurrentUserUtils {
     @observable public static GuestWorkspace: Doc | undefined;
     @observable public static GuestMobile: Doc | undefined;
 
-    static setupDefaultDocTemplates(doc: Doc, buttons?: string[]) {
-        const taskStatusValues = [{ title: "todo", _backgroundColor: "blue", color: "white" },
-        { title: "in progress", _backgroundColor: "yellow", color: "black" },
-        { title: "completed", _backgroundColor: "green", color: "white" }
-        ];
-        const noteTemplates = [
-            Docs.Create.TextDocument("", { title: "text", style: "Note", isTemplateDoc: true, backgroundColor: "yellow" }),
-            Docs.Create.TextDocument("", { title: "text", style: "Idea", isTemplateDoc: true, backgroundColor: "pink" }),
-            Docs.Create.TextDocument("", { title: "text", style: "Topic", isTemplateDoc: true, backgroundColor: "lightBlue" }),
-            Docs.Create.TextDocument("", { title: "text", style: "Person", isTemplateDoc: true, backgroundColor: "lightGreen" }),
-            Docs.Create.TextDocument("", {
-                title: "text", style: "Todo", isTemplateDoc: true, backgroundColor: "orange", _autoHeight: false,
-                layout: FormattedTextBox.LayoutString("Todo"), _height: 100, _showCaption: "caption", caption: RichTextField.DashField("taskStatus")
-            })
-        ];
-        doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" });
-        Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues);
-        doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Layouts", _height: 75 }))
-        doc.templateButtons = Docs.Create.MasonryDocument(CurrentUserUtils.setupTemplateButtons(doc), {
-            title: "template buttons",
-            _pivotField: "author", _xMargin: 0,
-            _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-        });
-        doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([doc.noteTypes as any as Doc, doc.templateButtons as Doc, doc.clickFuncs as Doc], {
-            title: "template layouts", _xPadding: 0,
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
-        }));
+    // sets up the default User Templates - slideView, queryView, descriptionView
+    static setupUserTemplateButtons(doc: Doc) {
+        if (doc["template-button-query"] === undefined) {
+            const queryTemplate = Docs.Create.MulticolumnDocument(
+                [
+                    Docs.Create.QueryDocument({ title: "query", _height: 200 }),
+                    Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true })
+                ],
+                { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
+            );
+            queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
+            doc["template-button-query"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+                dragFactory: new PrefetchProxy(queryTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "question-circle"
+            });
+        }
+
+        if (doc["template-button-slides"] === undefined) {
+            const slideTemplate = Docs.Create.MultirowDocument(
+                [
+                    Docs.Create.MulticolumnDocument([], { title: "data", _height: 200 }),
+                    Docs.Create.TextDocument("", { title: "text", _height: 100 })
+                ],
+                { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
+            );
+            slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
+            doc["template-button-slides"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+                dragFactory: new PrefetchProxy(slideTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "address-card"
+            });
+        }
+
+        if (doc["template-button-description"] === undefined) {
+            const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" });
+            Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description");
+            descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView");
+
+            doc["template-button-description"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+                dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize"
+            });
+        }
+
+        if (doc["template-buttons"] === undefined) {
+            doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc], {
+                title: "template buttons", _xMargin: 0, _showTitle: "title",
+                _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+            }));
+        }
+        return doc["template-buttons"] as Doc;
     }
-    static setupDefaultIconTypes(doc: Doc, buttons?: string[]) {
-        doc.iconView = new PrefetchProxy(Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(this)") }));
-        Doc.GetProto(doc.iconView as any as Doc).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
-        doc.isTemplateDoc = makeTemplate(doc.iconView as any as Doc);
-        doc.iconImageView = new PrefetchProxy(Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }));
-        doc.isTemplateDoc = makeTemplate(doc.iconImageView as any as Doc, true, "image_icon");
-        doc.iconColView = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }));
-        doc.isTemplateDoc = makeTemplate(doc.iconColView as any as Doc, true, "collection_icon");
-        doc.iconViews = Docs.Create.TreeDocument([doc.iconView as any as Doc, doc.iconImageView as any as Doc, doc.iconColView as any as Doc], { title: "icon types", _height: 75 });
+
+    // setup the different note type skins
+    static setupNoteTemplates(doc: Doc) {
+        if (doc.noteTypes === undefined) {
+            const taskStatusValues = [
+                { title: "todo", _backgroundColor: "blue", color: "white" },
+                { title: "in progress", _backgroundColor: "yellow", color: "black" },
+                { title: "completed", _backgroundColor: "green", color: "white" }
+            ];
+            const noteTemplates = [
+                Docs.Create.TextDocument("", { title: "text", style: "Note", isTemplateDoc: true, backgroundColor: "yellow" }),
+                Docs.Create.TextDocument("", { title: "text", style: "Idea", isTemplateDoc: true, backgroundColor: "pink" }),
+                Docs.Create.TextDocument("", { title: "text", style: "Topic", isTemplateDoc: true, backgroundColor: "lightBlue" }),
+                Docs.Create.TextDocument("", { title: "text", style: "Person", isTemplateDoc: true, backgroundColor: "lightGreen" }),
+                Docs.Create.TextDocument("", {
+                    title: "text", style: "Todo", isTemplateDoc: true, backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption",
+                    layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus")
+                })
+            ];
+            doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" });
+            Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues);
+            doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt),
+                { title: "Note Layouts", _height: 75 }));
+        }
+        // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved.
+        PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast));
+        return doc.noteTypes as Doc;
     }
 
-    // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
-    static setupCreatorButtons(doc: Doc, alreadyCreatedButtons?: string[]) {
-        const emptyPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
-        const emptyCollection = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
-        doc.activePen = doc;
-        const docProtoData: { title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
-            { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection },
-            { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", {_width: 300, _height: 300, title: "New Webpage" })' },
+    // creates Note templates, and initial "user" templates
+    static setupDocTemplates(doc: Doc) {
+        const noteTemplates = CurrentUserUtils.setupNoteTemplates(doc);
+        const userTemplateBtns = CurrentUserUtils.setupUserTemplateButtons(doc);
+        const clickTemplates = CurrentUserUtils.setupClickEditorTemplates(doc);
+        if (doc.templateDocs === undefined) {
+            doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([noteTemplates, userTemplateBtns, clickTemplates], {
+                title: "template layouts", _xPadding: 0,
+                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
+            }));
+        }
+    }
+
+    // setup templates for different document types when they are iconified from Document Decorations
+    static setupDefaultIconTemplates(doc: Doc) {
+        if (doc["icon-view"] === undefined) {
+            const iconView = Docs.Create.TextDocument("", {
+                title: "icon", _width: 150, _height: 30, isTemplateDoc: true,
+                onClick: ScriptField.MakeScript("deiconifyView(this)")
+            });
+            Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
+            iconView.isTemplateDoc = makeTemplate(iconView);
+            doc["icon-view"] = new PrefetchProxy(iconView);
+        }
+        if (doc["icon-view-img"] === undefined) {
+            const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") });
+            iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
+            doc["icon-view-img"] = new PrefetchProxy(iconImageView);
+        }
+        if (doc["icon-view-col"] === undefined) {
+            const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onClick: ScriptField.MakeScript("deiconifyView(self)") });
+            iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
+            doc["icon-view-col"] = new PrefetchProxy(iconColView);
+        }
+        if (doc["icon-view-all"] === undefined) {
+            doc["icon-view-all"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["icon-view"] as Doc, doc["icon-view-img"] as Doc, doc["icon-view-col"] as Doc], { title: "icon templates", _height: 75 }));
+        }
+        return doc["icon-view-all"] as Doc;
+    }
+
+    static creatorBtnDescriptors(doc: Doc): {
+        title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean,
+        click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc
+    }[] {
+        if (doc.emptyPresentation === undefined) {
+            doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
+                { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
+        }
+        if (doc.emptyCollection === undefined) {
+            doc.emptyCollection = Docs.Create.FreeformDocument([],
+                { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
+        }
+        return [
+            { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc },
+            { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", { title: "New Webpage" })' },
             { title: "Drag a cat image", label: "Img", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' },
             { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' },
             { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
             { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
             { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
-            { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation },
+            { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc },
             { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
             { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
             { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
@@ -101,59 +193,45 @@ export class CurrentUserUtils {
             { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
             // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" },
         ];
-        return docProtoData.filter(d => !alreadyCreatedButtons?.includes(d.title)).map(data => Docs.Create.FontIconDocument({
-            _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
-            icon: data.icon,
-            title: data.title,
-            label: data.label,
-            author: "Draggable Items",
-            ignoreClick: data.ignoreClick,
-            dropAction: data.click ? "copy" : undefined,
-            onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
-            onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
-            ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined,
-            activePen: data.activePen,
-            backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]),
-            dragFactory: data.dragFactory,
-        }));
-    }
-
-    static setupTemplateButtons(doc: Doc) {
-        const queryTemplate = Docs.Create.MulticolumnDocument(
-            [
-                Docs.Create.QueryDocument({ title: "query", _height: 200, forceActive: true }),
-                Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true, forceActive: true })
-            ],
-            { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true });
-        queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
-        const slideTemplate = Docs.Create.MultirowDocument(
-            [
-                Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, forceActive: true }),
-                Docs.Create.TextDocument("", { title: "text", _height: 100, forceActive: true })
-            ],
-            { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true });
-        slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
-        const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" });
-        Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description");
-        descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView");
-
-        doc.slidesBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "address-card" });
-        doc.descriptionBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: descriptionTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize" });
-        doc.queryBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: queryTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "question-circle" });
-
-        return [doc.slidesBtn as Doc, doc.descriptionBtn as Doc, doc.queryBtn as Doc];
     }
 
-
-    static async updateCreatorButtons(doc: Doc) {
-        const dragset = await Cast(doc.dragCreators, Doc);
-        if (dragset) {
-            const dragdocs = await Cast(dragset.data, listSpec(Doc));
-            if (dragdocs) {
-                const dragDocs = await Promise.all(dragdocs);
-                this.setupCreatorButtons(doc, dragDocs.map(d => StrCast(d.title))).map(nb => Doc.AddDocToList(dragset, "data", nb));
+    // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
+    static async setupCreatorButtons(doc: Doc) {
+        let alreadyCreatedButtons: string[] = [];
+        const dragCreatorSet = await Cast(doc.dragCreators, Doc, null);
+        if (dragCreatorSet) {
+            const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc));
+            if (dragCreators) {
+                const dragDocs = await Promise.all(dragCreators);
+                alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title));
             }
         }
+        const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title)).
+            map(data => Docs.Create.FontIconDocument({
+                _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
+                icon: data.icon,
+                title: data.title,
+                label: data.label,
+                ignoreClick: data.ignoreClick,
+                dropAction: data.click ? "copy" : undefined,
+                onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
+                onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
+                ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined,
+                activePen: data.activePen,
+                backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]),
+                dragFactory: data.dragFactory,
+            }));
+
+        if (dragCreatorSet === undefined) {
+            doc.dragCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
+                title: "Draggable Items", _showTitle: "title", _xMargin: 0,
+                _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+            }));
+        } else {
+            creatorBtns.forEach(nb => Doc.AddDocToList(doc.dragCreators as Doc, "data", nb));
+        }
+        return doc.dragCreators as Doc;
     }
 
     static setupMobileButtons(doc: Doc, buttons?: string[]) {
@@ -231,96 +309,138 @@ export class CurrentUserUtils {
         });
     }
 
-    // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker.  when clicked, this panel will be displayed in the target container (ie, sidebarContainer)  
-    static setupToolsPanel(sidebarContainer: Doc, doc: Doc) {
+    // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker. 
+    // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)  
+    static async setupToolsBtnPanel(doc: Doc, sidebarContainer: Doc) {
         // setup a masonry view of all he creators
-        const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc);
-        doc.dragCreators = Docs.Create.MasonryDocument(creatorBtns, {
-            title: "drag Creators",
-            _pivotField: "author", _xMargin: 0,
-            _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-        });
-        doc.allCreators = Docs.Create.StackingDocument([doc.dragCreators as any as Doc, doc.templateButtons as any as Doc], {
-            title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
-            _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
-        });
+        const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
+        const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc);
+
+        if (doc.allCreators === undefined) {
+            doc.allCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
+                title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
+                _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+            }));
+        }
         // setup a color picker
-        const color = Docs.Create.ColorDocument({
-            title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List<string>(["dropAction", "forceActive"])
-        });
+        if (doc.colorPicker === undefined) {
+            const color = Docs.Create.ColorDocument({
+                title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List<string>(["dropAction", "forceActive"])
+            });
+            doc.colorPicker = new PrefetchProxy(color);
+        }
 
-        return Docs.Create.ButtonDocument({
-            _width: 35, _height: 25, title: "Tools", fontSize: 10, targetContainer: sidebarContainer,
-            letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-            sourcePanel: Docs.Create.StackingDocument([doc.allCreators as Doc, color], {
-                _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true
-            }),
-            onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"),
-        });
+        if (doc["tabs-button-tools"] === undefined) {
+            doc["tabs-button-tools"] = new PrefetchProxy(Docs.Create.ButtonDocument({
+                _width: 35, _height: 25, title: "Tools", fontSize: 10,
+                letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
+                sourcePanel: new PrefetchProxy(Docs.Create.StackingDocument([doc.allCreators as Doc, doc.colorPicker as Doc], {
+                    _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true
+                })) as any as Doc,
+                targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+                onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"),
+            }));
+        }
+        (doc["tabs-button-tools"] as Doc).sourcePanel; // prefetch sourcePanel
+        return doc["tabs-button-tools"] as Doc;
     }
 
-    // setup the Library button which will display the library panel.  This panel includes a collection of workspaces, documents, and recently closed views
-    static setupLibraryPanel(sidebarContainer: Doc, doc: Doc) {
+    static setupWorkspaces(doc: Doc) {
         // setup workspaces library item
-        doc.workspaces = Docs.Create.TreeDocument([], {
-            title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true,
-        });
+        if (doc.worspaces === undefined) {
+            doc.workspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true,
+            }));
+        }
         const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
         (doc.workspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
         (doc.workspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
 
-        doc.documents = Docs.Create.TreeDocument([], {
-            title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
-        });
-
+        return doc.workspaces as Doc;
+    }
+    static setupDocumentCollection(doc: Doc) {
+        if (doc.documents === undefined) {
+            doc.documents = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
+            }));
+        }
+        return doc.documents as Doc;
+    }
+    static setupRecentlyClosed(doc: Doc) {
         // setup Recently Closed library item
-        doc.recentlyClosed = Docs.Create.TreeDocument([], {
-            title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
-        });
+        if (doc.recentlyClosed === undefined) {
+            doc.recentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
+            }));
+        }
+        // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
+        PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
         const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
         (doc.recentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
         (doc.recentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
 
-        return Docs.Create.ButtonDocument({
-            _width: 50, _height: 25, title: "Library", fontSize: 10,
-            letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-            sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc, doc], {
-                title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true
-            }),
-            targetContainer: sidebarContainer,
-            onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;")
-        });
+        return doc.recentlyClosed as Doc;
+    }
+    // setup the Library button which will display the library panel.  This panel includes a collection of workspaces, documents, and recently closed views
+    static setupLibraryPanel(doc: Doc, sidebarContainer: Doc) {
+        const workspaces = CurrentUserUtils.setupWorkspaces(doc);
+        const documents = CurrentUserUtils.setupDocumentCollection(doc);
+        const recentlyClosed = CurrentUserUtils.setupRecentlyClosed(doc);
+
+        if (doc["tabs-button-library"] === undefined) {
+            doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({
+                _width: 50, _height: 25, title: "Library", fontSize: 10,
+                letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
+                sourcePanel: new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], {
+                    title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true
+                })) as any as Doc,
+                targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+                onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;")
+            }));
+        }
+        return doc["tabs-button-library"] as Doc;
     }
 
     // setup the Search button which will display the search panel.  
-    static setupSearchPanel(sidebarContainer: Doc) {
-        return Docs.Create.ButtonDocument({
-            _width: 50, _height: 25, title: "Search", fontSize: 10,
-            letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-            sourcePanel: Docs.Create.QueryDocument({ title: "search stack", }),
-            targetContainer: sidebarContainer,
-            lockedPosition: true,
-            onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel")
-        });
+    static setupSearchBtnPanel(doc: Doc, sidebarContainer: Doc) {
+        if (doc["tabs-button-search"] === undefined) {
+            doc["tabs-button-search"] = new PrefetchProxy(Docs.Create.ButtonDocument({
+                _width: 50, _height: 25, title: "Search", fontSize: 10,
+                letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
+                sourcePanel: new PrefetchProxy(Docs.Create.QueryDocument({ title: "search stack", })) as any as Doc,
+                targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+                lockedPosition: true,
+                onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel")
+            }));
+        }
+        return doc["tabs-button-search"] as Doc;
     }
 
-    // setup the list of sidebar mode buttons which determine what is displayed in the sidebar
-    static setupSidebarButtons(doc: Doc) {
-        const sidebarContainer = new Doc();
-        doc.sidebarContainer = new PrefetchProxy(sidebarContainer);
-        sidebarContainer._chromeStatus = "disabled";
-        sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()");
+    static setupSidebarContainer(doc: Doc) {
+        if (doc["tabs-panelContainer"] === undefined) {
+            const sidebarContainer = new Doc();
+            sidebarContainer._chromeStatus = "disabled";
+            sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()");
+            doc["tabs-panelContainer"] = new PrefetchProxy(sidebarContainer);
+        }
+        return doc["tabs-panelContainer"] as Doc;
+    }
 
-        doc.ToolsBtn = new PrefetchProxy(this.setupToolsPanel(sidebarContainer, doc));
-        doc.LibraryBtn = new PrefetchProxy(this.setupLibraryPanel(sidebarContainer, doc));
-        doc.SearchBtn = new PrefetchProxy(this.setupSearchPanel(sidebarContainer));
+    // setup the list of sidebar mode buttons which determine what is displayed in the sidebar
+    static async setupSidebarButtons(doc: Doc) {
+        const sidebarContainer = CurrentUserUtils.setupSidebarContainer(doc);
+        const toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer);
+        const libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer);
+        const searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer);
 
         // Finally, setup the list of buttons to display in the sidebar
-        doc.sidebarButtons = new PrefetchProxy(Docs.Create.StackingDocument([doc.SearchBtn as any as Doc, doc.LibraryBtn as any as Doc, doc.ToolsBtn as any as Doc], {
-            _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode",
-            title: "sidebar btn row stack", backgroundColor: "dimGray",
-        }));
+        if (doc["tabs-buttons"] === undefined) {
+            doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([searchBtn, libraryBtn, toolsBtn], {
+                _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode",
+                title: "sidebar btn row stack", backgroundColor: "dimGray",
+            }));
+            (toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn });
+        }
     }
 
     static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
@@ -328,85 +448,101 @@ export class CurrentUserUtils {
         _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", forceActive: true,
         dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
         backgroundColor: "black", treeViewPreventOpen: true, lockedPosition: true, _chromeStatus: "disabled", linearViewIsExpanded: true
-    })) as any as Doc;
+    })) as any as Doc
 
     static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
         ...opts,
         dropAction: "alias", removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100
-    })) as any as Doc;
+    })) as any as Doc
 
     /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
     static setupExpandingButtons(doc: Doc) {
-        doc.penBtn = CurrentUserUtils.ficon({
-            onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"),
-            author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen,  this)`), activePen: doc
-        })
-        doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
-        doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
-        doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]);
+        if (doc.penBtn === undefined) {
+            doc.penBtn = CurrentUserUtils.ficon({
+                onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"),
+                author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen,  this)`), activePen: doc
+            });
+        }
+        if (doc.undoBtn === undefined) {
+            doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
+        }
+        if (doc.redoBtn === undefined) {
+            doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
+        }
+        if (doc.expandingButtons === undefined) {
+            doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]);
+        }
     }
     // sets up the default set of documents to be shown in the Overlay layer
     static setupOverlays(doc: Doc) {
-        doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" }));
+        if (doc.overlays === undefined) {
+            doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" }));
+        }
     }
 
     // the initial presentation Doc to use
     static setupDefaultPresentation(doc: Doc) {
-        doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" }));
-        doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
+        if (doc.presentationTemplate === undefined) {
+            doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
+                title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data"
+            }));
+        }
+        if (doc.curPresentation === undefined) {
+            doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), {
+                title: "Presentation", _viewType: CollectionViewType.Stacking,
+                _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
+            });
+        }
     }
 
     static setupMobileUploads(doc: Doc) {
-        doc.optionalRightCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "New mobile uploads" }));
+        if (doc.optionalRightCollection === undefined) {
+            doc.optionalRightCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "New mobile uploads" }));
+        }
     }
 
-    static setupChildClicks(doc: Doc) {
-        const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
-            "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )",
-            { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 });
-        const onClick = Docs.Create.ScriptingDocument(undefined, { title: "onClick", "onClick-rawScript": "console.log('click')", isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200 }, "onClick");
-        const onCheckedClick = Docs.Create.ScriptingDocument(undefined,
-            { title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", "onCheckedClick-params": new List<string>(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200 }, "onCheckedClick");
-        doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" });
-        doc.clickFuncs = Docs.Create.TreeDocument([onClick, onCheckedClick], { title: "onClick funcs" });
-    }
-
-    static updateUserDocument(doc: Doc) {
-        doc.title = Doc.CurrentUserEmail;
-        new InkingControl();
-        (doc.childClickFuncs === undefined) && CurrentUserUtils.setupChildClicks(doc);
-        (doc.iconTypes === undefined) && CurrentUserUtils.setupDefaultIconTypes(doc);
-        (doc.templateDocs === undefined) && CurrentUserUtils.setupDefaultDocTemplates(doc);
-        (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc);
-        (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc);
-        (doc.expandingButtons === undefined) && CurrentUserUtils.setupExpandingButtons(doc);
-        (doc.curPresentation === undefined) && CurrentUserUtils.setupDefaultPresentation(doc);
-        (doc.sidebarButtons === undefined) && CurrentUserUtils.setupSidebarButtons(doc);
-        doc.linkDb = Docs.Prototypes.MainLinkDocument();
+    static setupClickEditorTemplates(doc: Doc) {
+        if (doc.childClickFuncs === undefined) {
+            const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
+                "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )",
+                { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 });
 
+            doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" });
+        }
         // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved.
         PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
-        // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
-        PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
-        // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved.
-        PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast));
+
+        if (doc.clickFuncs === undefined) {
+            const onClick = Docs.Create.ScriptingDocument(undefined, {
+                title: "onClick", "onClick-rawScript": "console.log('click')",
+                isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200
+            }, "onClick");
+            const onCheckedClick = Docs.Create.ScriptingDocument(undefined, {
+                title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", "onCheckedClick-params": new List<string>(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200
+            }, "onCheckedClick");
+            doc.clickFuncs = Docs.Create.TreeDocument([onClick, onCheckedClick], { title: "onClick funcs" });
+        }
         PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
-        PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
-        PromiseValue(Cast(doc.sidebarButtons, Doc)).then(stackingDoc => {
-            stackingDoc && PromiseValue(Cast(stackingDoc.data, listSpec(Doc))).then(sidebarButtons => {
-                sidebarButtons?.map((sidebarBtn, i) => {
-                    sidebarBtn && PromiseValue(Cast(sidebarBtn, Doc)).then(async btn => { // choose which item to display first in sidebar panel (i === 0,1, or 2)
-                        btn?.sourcePanel && btn.targetContainer && i === 2 && (btn.onClick as ScriptField).script.run({ this: btn });
-                    });
-                });
-            });
-        });
+
+        return doc.clickFuncs as Doc;
+    }
+
+    static async updateUserDocument(doc: Doc) {
+        new InkingControl();
+        doc.title = Doc.CurrentUserEmail;
+        doc.activePen = doc;
+        CurrentUserUtils.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
+        CurrentUserUtils.setupMobileUploads(doc);  // sets up the optional right collection of documents uploaded from mobile
+        CurrentUserUtils.setupOverlays(doc);  // documents in overlay layer 
+        CurrentUserUtils.setupExpandingButtons(doc);  // the bottom bar of font icons
+        CurrentUserUtils.setupDefaultPresentation(doc); // presentation that's initially triggered
+        await CurrentUserUtils.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
+        doc.linkDb = Docs.Prototypes.MainLinkDocument();
 
         // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
         doc.undoBtn && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc.undoBtn as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
         doc.redoBtn && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc.redoBtn as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
 
-        this.updateCreatorButtons(doc);
         return doc;
     }
 
diff --git a/src/server/database.ts b/src/server/database.ts
index 1da31c5ff..d74bd7321 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -52,9 +52,6 @@ export namespace Database {
         private currentWrites: { [id: string]: Promise<void> } = {};
         private db?: mongodb.Db;
         private onConnect: (() => void)[] = [];
-        constructor() {
-        }
-
         doConnect() {
             console.error(`\nConnecting to Mongo with URL : ${url}\n`);
             this.MongoClient.connect(url, { connectTimeoutMS: 30000, socketTimeoutMS: 30000, useUnifiedTopology: true }, (_err, client) => {
-- 
cgit v1.2.3-70-g09d2

