From ec2e780141fb41d3f70aa86b0312211e0b30257c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 10:39:14 -0400
Subject: fixed issues with pinWithView so that thumnails show the pinned area
 and navigating to the view shows the entire pinned area regardless of panel
 size.

---
 src/client/views/nodes/DocumentView.tsx | 1 +
 1 file changed, 1 insertion(+)

(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 59a402e01..26dec3bd5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -112,6 +112,7 @@ export interface DocumentViewSharedProps {
     renderDepth: number;
     Document: Doc;
     DataDoc?: Doc;
+    contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
     fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
-- 
cgit v1.2.3-70-g09d2


From 3213c24210d67af0a4b027863773ad383404a59c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 11:07:34 -0400
Subject: renamed fitToBox to fitContentsToBox.  fixed pinning multiple
 documents at same time to not bring up presBox multiple times.

---
 src/client/documents/Documents.ts                  |  3 +-
 src/client/views/DocumentButtonBar.tsx             | 21 ++----------
 src/client/views/LightboxView.tsx                  |  2 --
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/PropertiesButtons.tsx             |  2 +-
 src/client/views/PropertiesView.tsx                |  2 +-
 src/client/views/collections/CollectionMenu.tsx    |  4 +--
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 src/client/views/collections/TabDocView.tsx        | 25 +++++++--------
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 37 +++++++---------------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 .../CollectionMulticolumnView.tsx                  |  2 +-
 .../CollectionMultirowView.tsx                     |  2 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  4 +--
 src/client/views/nodes/MapBox/MapBox.tsx           |  6 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  6 ++--
 src/client/views/nodes/trails/PresBox.tsx          | 14 ++++----
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 src/fields/documentSchemas.ts                      |  6 ++--
 src/mobile/AudioUpload.tsx                         |  4 +--
 22 files changed, 60 insertions(+), 92 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index acc1e341a..144a3d9cf 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -113,7 +113,8 @@ export class DocumentOptions {
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", true);
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units", true);
     _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)", true);
-    _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
+    _fitContentsToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
+    _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
     _isPushpin?: boolean; // whether document, when clicked, toggles display of its link target
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 41224b1c5..0bbe473d7 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -204,27 +204,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="dash-tooltip">{SelectionManager.Views().length > 1 ? "Pin multiple documents to presentation" : "Pin to presentation"}</div>}>
             <div className="documentButtonBar-icon"
                 style={{ color: "white" }}
-                onClick={undoBatch(e => this.props.views().map(view => view && this.pinWithView(view.props.Document)))}>
+                onClick={(e => TabDocView.PinDoc(this.props.views().filter(v => v).map(dv => dv!.rootDoc), {pinDocView: true}))}
+             >
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>
         </Tooltip>;
     }
 
-    @undoBatch
-    @action
-    pinWithView = (targetDoc: Doc) =>  targetDoc && TabDocView.PinDoc(targetDoc, {pinDocView: true});
-
-    @computed
-    get pinWithViewButton() {
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 17, transform: 'translate(0, 1px)' }} />;
-        const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin with current view"}</div></>}>
-            <div className="documentButtonBar-icon" onClick={() => this.pinWithView(targetDoc)}>
-                {presPinWithViewIcon}
-            </div>
-        </Tooltip>;
-    }
-
     @computed
     get shareButton() {
         const targetDoc = this.view0?.props.Document;
@@ -349,9 +335,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.pinButton}
             </div>
-            {/* <div className="documentButtonBar-button">
-                {this.pinWithViewButton}
-            </div> */}
             {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.shareButton}
             </div>}
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index f67f37bfb..fcc4aea13 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -207,7 +207,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
 
     future = () => LightboxView._future;
     tourMap = () => LightboxView._tourMap;
-    fitToBox = () => LightboxView._docTarget === LightboxView.LightboxDoc;
     render() {
         let downx = 0, downy = 0;
         return !LightboxView.LightboxDoc ? (null) :
@@ -241,7 +240,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         DataDoc={undefined}
                         LayoutTemplate={LightboxView.LightboxDocTemplate}
                         addDocument={undefined}
-                        // fitContentsToDoc={this.fitToBox} // bcz: why do we want this?  when we initially open a colletion, we shrinkwrap it which allows for user navigation.  if we later encounter a collection, it's not clear to me that we want to make it either shrinkwrap or fitContents...
                         isDocumentActive={returnFalse}
                         isContentActive={returnTrue}
                         addDocTab={this.addDocTab}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 641178cd9..84a131a57 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -287,7 +287,7 @@ export class MainView extends React.Component {
                 styleProvider={DefaultStyleProvider}
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
-                fitContentsToDoc={returnTrue}
+                fitContentsToBox={returnTrue}
                 isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
                 isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                 ScreenToLocalTransform={this.headerBarScreenXf}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index f24ff09db..74055f057 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -71,7 +71,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return this.propertyToggleBtn("Lock\xA0View", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => "lock");
     }
     @computed get fitContentButton() {
-        return this.propertyToggleBtn("View All", "_fitToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
+        return this.propertyToggleBtn("View All", "_fitContentsToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
     }
     @computed get fitWidthButton() {
         return this.propertyToggleBtn("Fit\xA0Width", "_fitWidth", on => `${on ? "Don't" : "Do"} fit content to width of container`, on => "arrows-alt-h");
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index bcfd2dd56..16eb95cf4 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -300,7 +300,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     Document={layoutDoc}
                     DataDoc={this.dataDoc}
                     renderDepth={1}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 01e77f342..39f6466d6 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -252,8 +252,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     };
     _clusterCommand = {
         params: ["target"], title: "fit content",
-        script: "self.target._fitToBox = !self.target._fitToBox;",
-        immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox),
+        script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;",
+        immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox),
         initialize: emptyFunction
     };
     _fitContentCommand = {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 4f6f45d2f..7573b938a 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -135,7 +135,7 @@ export class CollectionTimeView extends CollectionSubView() {
             onClick={this.contentsDown}>
             <CollectionFreeFormView {...this.props}
                 engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }}
-                fitContentsToDoc={returnTrue}
+                fitContentsToBox={returnTrue}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f6a75e2db..5cc92a305 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -192,15 +192,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
      * Adds a document to the presentation view
      **/
     @action
-    public static PinDoc(doc: Doc, pinProps?: PinProps) {
-        //add this new doc to props.Document
+    public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) {
+        const docList = ((docs instanceof Doc) ? [docs]: docs);
+        const batch = UndoManager.StartBatch("pinning doc");
 
         // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
         const curPres = CurrentUserUtils.ActivePresentation;
-        if (curPres) {
+        curPres && docList.forEach(doc => {
             // Edge Case 1: Cannot pin document to itself
             if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const batch = UndoManager.StartBatch("pinning doc");
             const pinDoc = Doc.MakeAlias(doc);
             pinDoc.presentationTargetDoc = doc;
             pinDoc.title = doc.title + " - Slide";
@@ -279,17 +279,16 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presMovement = PresMovement.None;
             }
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
-                const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
-                if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
-                CollectionDockingView.AddSplit(curPres, "right");
-                setTimeout(() => DocumentManager.Instance.jumpToDocument(doc, false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
-            }
             PresBox.Instance?._selectedArray.clear();
             pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-            setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
-            return pinDoc;
+        });
+        if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
+            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+            if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
+            CollectionDockingView.AddSplit(curPres, "right");
+            setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
         }
+        setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
     }
 
     componentDidMount() {
@@ -572,7 +571,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     docFilters={CollectionDockingView.Instance.childDocFilters}
                     docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                     searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                 />
                 <div className="miniOverlay" onPointerDown={this.miniDown} >
                     <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 09f05f69a..8824750a3 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -796,7 +796,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             isDocumentActive={isActive}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
             hideTitle={asText}
-            fitContentsToDoc={returnTrue}
+            fitContentsToBox={returnTrue}
             hideDecorationTitle={this.props.treeView.outlineMode}
             hideResizeHandles={this.props.treeView.outlineMode}
             onClick={this.onChildClick}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b70e6ff98..ec3cf1fe8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,7 +9,7 @@ import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fiel
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
-import { createSchema, listSpec } from "../../../../fields/Schema";
+import { listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
@@ -41,6 +41,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn
 import { LightboxView } from "../../LightboxView";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
+import { FieldViewProps } from "../../nodes/FieldView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { VideoBox } from "../../nodes/VideoBox";
@@ -50,27 +51,13 @@ import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
 import { TreeViewType } from "../CollectionTreeView";
 import { CollectionViewType } from "../CollectionView";
+import { TabDocView } from "../TabDocView";
 import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { FieldView, FieldViewProps } from "../../nodes/FieldView";
-import { TabDocView } from "../TabDocView";
 
-export const panZoomSchema = createSchema({
-    _panX: "number",
-    _panY: "number",
-    _currentTimecode: "number",
-    _timecodeToShow: "number",
-    _currentFrame: "number",
-    _useClusters: "boolean",
-    _viewTransition: "string",
-    _xPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _yPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _fitToBox: "boolean",
-    scrollHeight: "number"    // this will be set when the collection is an annotation overlay for a PDF/Webpage
-});
 
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -138,20 +125,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x))
         };
     }
-    @computed get fitToContent() { return (this.props.fitContentsToDoc?.() || this.Document._fitToBox) && !this.isAnnotationOverlay; }
+    @computed get fitContentsToBox() { return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay; }
     @computed get contentBounds() { 
         const cb = Cast(this.rootDoc.contentBounds, listSpec("number"));
         return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} : 
             this.props.contentBounds?.() ?? aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); 
     }
-    @computed get nativeWidth() { return this.fitToContent ? 0 : Doc.NativeWidth(this.Document); }
-    @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); }
+    @computed get nativeWidth() { return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document); }
+    @computed get nativeHeight() { return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document); }
     @computed get cachedCenteringShiftX(): number {
-        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling;  // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedCenteringShiftY(): number {
-        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling;// shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedGetLocalTransform(): Transform {
@@ -176,8 +163,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.layoutDoc._panY = vals.bounds.cy;
         this.layoutDoc._viewScale = vals.scale;
     }
-    freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined;
-    reverseNativeScaling = () => this.fitToContent ? true : false;
+    freeformData = (force?: boolean) => !this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined;
+    reverseNativeScaling = () => this.fitContentsToBox ? true : false;
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
     panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
     zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1));
@@ -1272,7 +1259,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.pointerEvents}
             jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
-        //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
+        //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
         />;
     }
     addDocTab = action((doc: Doc, where: string) => {
@@ -1616,7 +1603,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
-        appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+        appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
         //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3fac3350a..6df2bb102 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -516,7 +516,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
 
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index e2dfb25e2..7147ae485 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -257,7 +257,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToDoc={this.props.fitContentsToDoc}
+            fitContentsToBox={this.props.fitContentsToBox}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 3010e36aa..3bda56c33 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -264,7 +264,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToDoc={this.props.fitContentsToDoc}
+            fitContentsToBox={this.props.fitContentsToBox}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index b731479a5..f45068b6a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -402,7 +402,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 <DocumentView
                     Document={this.previewDocument}
                     DataDoc={undefined}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     freezeDimensions={true}
                     dontCenter={"y"}
                     focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 26dec3bd5..2a518bb57 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -85,7 +85,7 @@ export interface DocComponentView {
     getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
     setViewSpec?: (anchor: Doc, preview: boolean) => void;  // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
+    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void;  // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
@@ -113,7 +113,7 @@ export interface DocumentViewSharedProps {
     Document: Doc;
     DataDoc?: Doc;
     contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
-    fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
+    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     suppressSetHeight?: boolean;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 9f1c019fe..5f4c17ee6 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -259,7 +259,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitToBox && this.fitBounds(this._map);
+                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -274,7 +274,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -284,7 +284,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 7d0302b26..4e431e7bd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -495,7 +495,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 // embed document when dragg marked as embed
             } else if (de.embedKey) {
                 const target = dragData.droppedDocuments[0];
-                target._fitToBox = true;
+                target._fitContentsToBox = true;
                 const node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](),
                     height: target[HeightSym](),
@@ -1557,7 +1557,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         }
     }
-    fitToBox = () => this.props.Document._fitToBox;
+    fitContentsToBox = () => this.props.Document._fitContentsToBox;
     sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
@@ -1632,7 +1632,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     ScreenToLocalTransform={this.sidebarScreenToLocal}
                     renderDepth={this.props.renderDepth + 1}
                     setHeight={this.setSidebarHeight}
-                    fitContentsToDoc={this.fitToBox}
+                    fitContentsToBox={this.fitContentsToBox}
                     noSidebar={true}
                     fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 6de04bd31..cebf9487a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -35,8 +35,8 @@ export interface PinProps {
     setPosition?: boolean;
     hidePresBox?: boolean;
     pinWithView?: PinViewProps;
-    pinDocView?: boolean;
-    panelWidth?: number;
+    pinDocView?: boolean;  // whether the current view specs of the document should be saved the pinned document
+    panelWidth?: number;  // panel width and height of the document (used to compute the bounds of the pinned view area)
     panelHeight?: number
 }
 
@@ -997,7 +997,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Method called for viewing paths which adds a single line with
      * points at the center of each document added.
-     * Design choice: When this is called it sets _fitToBox as true so the
+     * Design choice: When this is called it sets _fitContentsToBox as true so the
      * user can have an overview of all of the documents in the collection.
      * (Design needed for when documents in presentation trail are in another
      * collection)
@@ -1800,16 +1800,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
                 break;
             case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             default:
                 break;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5eff47a86..ba0193e4b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -85,7 +85,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     isContentActive={this.props.isContentActive}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
                     ScreenToLocalTransform={Transform.Identity}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index e532becb5..be39e0709 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -36,8 +36,8 @@ export const documentSchema = createSchema({
     _nativeHeight: "number",    // "
     _width: "number",           // width of document in its container's coordinate system
     _height: "number",          // "
-    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set
+    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitContentsToBox is set
+    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitContentsToBox is set
     _xMargin: "number",         // margin added on left/right of most documents to add separation from their container
     _yMargin: "number",         // margin added on top/bottom of most documents to add separation from their container
     _overflow: "string",        // sets overflow behvavior for CollectionFreeForm views
@@ -59,7 +59,7 @@ export const documentSchema = createSchema({
     borderRounding: "string",   // border radius rounding of document
     boxShadow: "string",        // the amount of shadow around the perimeter of a document
     color: "string",            // foreground color of document
-    fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
+    fitContentsToBox: "boolean",// whether freeform view contents should be zoomed/panned to fill the area of the document view box
     fontSize: "string",
     hidden: "boolean",          // whether a document should not be displayed
     isInkMask: "boolean",       // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 418464f0e..64baa351c 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -19,7 +19,7 @@ import React = require('react');
 
 @observer
 export class AudioUpload extends React.Component {
-    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
 
     /**
      * Handles the onclick functionality for the 'Restart' button
@@ -36,7 +36,7 @@ export class AudioUpload extends React.Component {
                     title: "mobile audio",
                     _width: 500,
                     _height: 100
-                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
     }
 
     /** 
-- 
cgit v1.2.3-70-g09d2


From bc662b4e7eaaa1f33f41f64ce64d60579939b971 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 10 Jun 2022 10:18:05 -0400
Subject: fixed some problems with dragging doc onto empty tab bar.  made
 topBar be just one line to save vertical space.  added a context menu to
 Dashboard

---
 src/client/util/CurrentUserUtils.ts                | 14 ++++---
 src/client/util/DragManager.ts                     |  2 +-
 .../collectionLinear/CollectionLinearView.tsx      |  1 +
 src/client/views/global/globalCssVariables.scss    |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 11 +++---
 src/client/views/topbar/TopBar.scss                |  6 +--
 src/client/views/topbar/TopBar.tsx                 | 45 ++++++++++++----------
 7 files changed, 46 insertions(+), 35 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e46bb3b3e..60bfc165b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -381,6 +381,7 @@ export class CurrentUserUtils {
                     dontUndo: true,
                     title,
                     target,
+                    dontRegisterView: true,
                     hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined,
                     _dropAction: "alias",
                     _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
@@ -399,6 +400,7 @@ export class CurrentUserUtils {
                 _chromeHidden: true,
                 backgroundColor: Colors.DARK_GRAY,
                 boxShadow: "rgba(0,0,0,0)",
+                dontRegisterView: true,
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
                 ignoreClick: true,
                 _gridGap: 0,
@@ -719,7 +721,7 @@ export class CurrentUserUtils {
             ];
             doc.dockedBtns = CurrentUserUtils.linearButtonList({
                 title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
-                linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
+                childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
             }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() }))));
         }
     }
@@ -833,12 +835,13 @@ export class CurrentUserUtils {
         });
         if (doc.contextMenuBtns === undefined) {
             doc.contextMenuBtns = CurrentUserUtils.linearButtonList(
-                { title: "menu buttons", flexGap: 0, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
+                { title: "menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
                 CurrentUserUtils.contextMenuTools(doc).map(params =>
                     !params.subMenu ?
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
                             title: params.title,
+                            childDontRegisterViews: true,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
@@ -854,6 +857,7 @@ export class CurrentUserUtils {
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
                             title: params.title,
+                            childDontRegisterViews: true,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
@@ -919,7 +923,7 @@ export class CurrentUserUtils {
                     title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
                     _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
                     _chromeHidden: true, boxShadow: "0 0",
-                    explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
+                    dontRegisterView: true, explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
                 }, sharingDocumentId + "outer", sharingDocumentId);
                 (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment;
             }
@@ -943,7 +947,7 @@ export class CurrentUserUtils {
             doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
                 title: "My Imports", _forceActive: true, buttonMenu: true, buttonMenuDoc: newImportButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
                 childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
-                explainer: "This is where documents that are Imported into Dash will go."
+                dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
             }));
         }
     }
@@ -952,7 +956,7 @@ export class CurrentUserUtils {
     static setupSearchSidebar(doc: Doc) {
         if (doc.mySearchPanel === undefined) {
             doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
-                backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true,
+                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true,
                 childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true
             })) as any as Doc;
         }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9f8c49081..c3c6c22df 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -455,7 +455,7 @@ export namespace DragManager {
             if (dragData instanceof DocumentDragData) {
                 dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction;
             }
-            if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
+            if (((e.target as any)?.className === "lm_tabs" || ((e.target as any)?.className === "lm_header" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
                 if (!startWindowDragTimer) {
                     startWindowDragTimer = setTimeout(async () => {
                         startWindowDragTimer = undefined;
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c0a33a5e0..9b1cb1601 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -153,6 +153,7 @@ export class CollectionLinearView extends CollectionSubView() {
                 PanelWidth={nested ? doc[WidthSym] : this.dimension}
                 PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
                 renderDepth={this.props.renderDepth + 1}
+                dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
                 focus={emptyFunction}
                 styleProvider={this.props.styleProvider}
                 docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index ce9cc05d6..a14634bdc 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -38,7 +38,7 @@ $small-text: 9px;
 // misc values
 $border-radius: 0.3em;
 $search-thumnail-size: 130;
-$topbar-height: 55px;
+$topbar-height: 32px;
 $antimodemenu-height: 36px;
 
 // dragged items
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1591840e6..60d16f044 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -764,7 +764,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
             !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
 
-            if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+            if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
                 !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
@@ -834,10 +834,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
             const help = cm.findByDescription("Help...");
             const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
-            !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
-            helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
-            !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
-            !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
+            helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
 
@@ -1204,6 +1204,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     @observable public docView: DocumentViewInternal | undefined | null;
 
+    showContextMenu(pageX:number, pageY:number) { return this.docView?.onContextMenu(undefined, pageX, pageY); }
     get Document() { return this.props.Document; }
     get topMost() { return this.props.renderDepth === 0; }
     get rootDoc() { return this.docView?.rootDoc || this.Document; }
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
index 6662386d4..c5b340514 100644
--- a/src/client/views/topbar/TopBar.scss
+++ b/src/client/views/topbar/TopBar.scss
@@ -22,9 +22,9 @@
             grid-auto-columns: 33.3% 33.3% 33.3%;
             align-items: center;
 
-            &:first-child {
-                  height: 20px;
-            }
+            // &:first-child {
+            //       height: 20px;
+            // }
       }
 
       .topbar-button-text {
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 57ceac2dd..5bee58ccb 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -3,14 +3,18 @@ import { Tooltip } from "@material-ui/core";
 import { action } from "mobx";
 import { observer } from "mobx-react";
 import * as React from 'react';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { AclAdmin, Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { Cast, StrCast } from '../../../fields/Types';
+import { GetEffectiveAcl } from "../../../fields/util";
 import { Utils } from '../../../Utils';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { DocumentManager } from "../../util/DocumentManager";
+import { SelectionManager } from "../../util/SelectionManager";
 import { SettingsManager } from "../../util/SettingsManager";
 import { SharingManager } from "../../util/SharingManager";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { ContextMenu } from "../ContextMenu";
 import { Borders, Colors } from "../global/globalEnums";
 import { MainView } from "../MainView";
 import "./TopBar.scss";
@@ -22,22 +26,38 @@ import "./TopBar.scss";
 @observer
 export class TopBar extends React.Component {
       navigateToHome = () => {
-            CurrentUserUtils.closeActiveDashboard();
-            CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => Doc.UserDoc().activePage = "home");
+            CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => {
+                Doc.UserDoc().activePage = "home";
+                CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
+            });
       }
       render() {
-            const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
             const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null)
             return (
                   //TODO:glr Add support for light / dark mode
                   <div style={{ pointerEvents: "all", background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
                         <div className="topbar-inner-container">
                               <div className="topbar-left">
-                                    <div className="topbar-button-text" onClick={this.navigateToHome}>Home</div>
+                                    {activeDashboard ? <div className="topbar-button-text">{Doc.CurrentUserEmail} (Placeholder)</div> : (null)}
                               </div>
                               <div className="topbar-center" >
+                                    <div className="topbar-title" onClick={() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
+                                          {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
+                                    </div>
+                                    <div className="topbar-button-icon" onClick={e => {
+                                        const dashView = DocumentManager.Instance.getDocumentView(activeDashboard);
+                                        ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
+                                        dashView?.showContextMenu(e.clientX+20, e.clientY+30);
+                                    }}>
+                                    <FontAwesomeIcon color="white" icon="bars" />
+                                    </div>
                               </div>
                               <div className="topbar-right" >
+                                    <div className="topbar-button-text" onClick={() => {SharingManager.Instance.open(undefined, activeDashboard)}}>
+                                        {/* TODO: if this is my dashboard, display share
+                                        if this is a shared dashboard, display "view original or view annotated" */}
+                                        { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original" }
+                                    </div>
                                     <div className="topbar-button-icon" onClick={() => window.open(
                                           "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
                                           <FontAwesomeIcon icon="question-circle" />
@@ -47,21 +67,6 @@ export class TopBar extends React.Component {
                                     </div>
                               </div>
                         </div>
-                        <div className="topbar-inner-container">
-                              <div className="topbar-left">
-                                    {activeDashboard ? <div className="topbar-button-text">Freeform View (Placeholder)</div> : (null)}
-                              </div>
-                              <div className="topbar-center" >
-                                    <div className="topbar-title">
-                                          {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
-                                    </div>
-                              </div>
-                              <div className="topbar-right" >
-                                    {/* TODO: if this is my dashboard, display share
-                                     if this is a shared dashboard, display "view original or view annotated" */}
-                                    <div className="topbar-button-text" onClick={() => {SharingManager.Instance.open(undefined, activeDashboard)}}>Share</div>
-                              </div>
-                        </div>
                   </div>
                         
             );
-- 
cgit v1.2.3-70-g09d2


From c0826fb904f5d2448635b5dae1fc4337fead939e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 05:31:25 -0400
Subject: lots of changes to try to cleanup CurrentUserUtils so changes can be
 made without rebuilding the DB.

---
 src/client/documents/Documents.ts                  |   10 +-
 src/client/util/CurrentUserUtils.ts                | 1216 +++++++++-----------
 src/client/util/DocumentManager.ts                 |    3 +-
 src/client/util/Scripting.ts                       |    2 +-
 src/client/views/DocComponent.tsx                  |    2 +-
 src/client/views/GlobalKeyHandler.ts               |    2 +-
 src/client/views/MainView.tsx                      |   36 +-
 src/client/views/OverlayView.tsx                   |    2 +-
 src/client/views/PropertiesView.tsx                |    5 +-
 .../views/collections/CollectionDockingView.tsx    |    2 +-
 src/client/views/collections/CollectionMenu.tsx    |    6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |    4 +-
 .../collectionLinear/CollectionLinearView.tsx      |    4 +-
 src/client/views/linking/LinkPopup.tsx             |    4 +-
 src/client/views/nodes/DocumentView.tsx            |    8 +-
 src/client/views/nodes/FilterBox.tsx               |    2 +-
 src/client/views/nodes/WebBox.tsx                  |   12 +-
 src/client/views/nodes/button/FontIconBox.tsx      |    2 +-
 src/client/views/nodes/trails/PresBox.tsx          |    8 +-
 src/fields/Doc.ts                                  |    5 +-
 src/fields/util.ts                                 |    3 +
 src/mobile/MobileInterface.tsx                     |    2 +-
 22 files changed, 629 insertions(+), 711 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5f009573e..927996af2 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -178,10 +178,15 @@ export class DocumentOptions {
     hidden?: boolean;
     _hidden?: boolean;
     pointerEvents?: string; // pointer events that the documentview should have
-    mediaState?: string; // status of media document: "pendingRecording", "recording", "paused", "playing"
+    mediaState?: string; // status of audio/video media document: "pendingRecording", "recording", "paused", "playing"
+    recording?: boolean; // whether WebCam is recording or not
     autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline.
     dontPlayLinkOnSelect?: boolean;  // whether an audio/video should start playing when a link is followed to it.
     toolTip?: string; // tooltip to display on hover
+    contextMenuFilters?: List<ScriptField>;
+    contextMenuScripts?: List<ScriptField>;
+    contextMenuLabels?: List<string>;
+    contextMenuIcons?: List<string>;
     dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
     description?: string; // added for links
     layout?: string | Doc; // default layout string for a document
@@ -238,7 +243,7 @@ export class DocumentOptions {
     isPushpin?: boolean;
     isGroup?: boolean; // whether a collection should use a grouping UI behavior
     _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
-
+    noteType?: string;
     // BACKGROUND GRID
     _backgroundGridShow?: boolean;
 
@@ -286,6 +291,7 @@ export class DocumentOptions {
     clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
+    filterBoolean ?: string;
     useCors?: boolean;
     icon?: string;
     target?: Doc; // available for use in scripts as the primary target document
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b4e18a8bb..afb1442bf 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,16 +1,17 @@
 import { computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
-import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, StrListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
+import { listSpec } from "../../fields/Schema";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, DateCast, DocCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, PromiseValue, ScriptCast, StrCast } from "../../fields/Types";
 import { ImageField, nullAudio } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
-import { Utils } from "../../Utils";
+import { OmitKeys, Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions, DocUtils } from "../documents/Documents";
 import { DocumentType } from "../documents/DocumentTypes";
@@ -42,17 +43,17 @@ interface Button {
     toolTip?: string;
     icon?: string;
     btnType?: ButtonType;
-    click?: string;
     numBtnType?: NumButtonType;
     numBtnMin?: number;
     numBtnMax?: number;
     switchToggle?: boolean;
-    script?: string;
     width?: number;
-    list?: string[];
+    btnList?: List<string>;
     ignoreClick?: boolean;
     buttonText?: string;
-    hidden?: string;
+    scripts?: { script?: string; onClick?: string; }
+    funcs?: { [key:string]: string };
+    subMenu?: Button[];
 }
 
 export let resolvedPorts: { server: number, socket: number };
@@ -73,101 +74,134 @@ export class CurrentUserUtils {
     @observable public static headerBarHeight: number = 0;
     @observable public static searchPanelWidth: number = 0;
 
-    // sets up the default User Templates - slideView,  headerView
-    static setupExperimentalTemplateButtons(doc: Doc) {
-        if (doc["template-experimental-buttons"] === undefined) {
-            const requiredTypeNameFields = [
-                {
-                    type: "slide", icon: "address-card", template: Docs.Create.MultirowDocument(
-                        [
-                            Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }),
-                            Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
-                        ],
-                        { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true }
-                    )
-                },
-                {
-                    type: "mobile", icon: "mobile", template: this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, },
-                        [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
-                        this.mobileTextContainer({},
-                            [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
-                        ]
-                    )
-                },
-            ];
-            const requiredTypes = requiredTypeNameFields.map(({ type, icon, template }) => {
-                if (doc[`template-button-${type}`] === undefined) {
-                    template.isTemplateDoc = makeTemplate(template);
-                    doc[`template-button-${type}`] = CurrentUserUtils.createToolButton({
-                        onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
-                        dragFactory: new PrefetchProxy(template) as any as Doc,
-                        title: type,
-                        icon,
-                    });
+    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, functions?:{[key:string]: string}) {
+        scripts && Object.keys(scripts).map(key => {
+            if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
+                doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name,  value:"any", scriptContext: "any" }, {"_readOnly_": true});
+            }
+        }); 
+        functions && Object.keys(functions).map(key => {
+            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+            if (ScriptCast(cfield)?.script.originalScript !== functions[key] && functions[key]) {
+                doc[key] = ComputedField.MakeFunction(functions[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
+            }
+        });
+        return doc;
+    }
+    static AssignOpts(doc:Doc|undefined, reqdOpts:DocumentOptions, items?:Doc[]) {
+        if (doc) {
+            const compareValues = (val1:any, val2:any) => {
+                if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) {
+                    return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v));
+                }
+                return val1 === val2;
+            }
+            Object.entries(reqdOpts).forEach(pair => {
+                const targetDoc = pair[0].startsWith("_") ? doc : Doc.GetProto(doc as Doc);
+                if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/,"")) ||
+                    !compareValues(pair[1], targetDoc[pair[0]])) {
+                    targetDoc[pair[0]] = pair[1];
                 }
-                return doc[`template-button-${type}`] as Doc;
             });
-
-            doc["template-experimental-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, {
-                title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true,
-                hidden: ComputedField.MakeFunction("IsNoviceMode()") as any,
-                _stayInCollection: true, _hideContextMenu: true, _forceActive: true,
-                _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
-                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true
-            }));
+            items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), "data", item));
         }
-        return doc["template-experimental-buttons"] as Doc;
+        return doc;
     }
 
-    // setup the different note type skins
-    static setupNoteTemplates(doc: Doc) {
-        if (doc["template-notes"] === undefined) {
-            const requiredTypeNameFields = [
-                { type: "Note", backgroundColor: "yellow", icon: "sticky-note" },
-                { type: "Idea", backgroundColor: "pink", icon: "lightbulb" },
-                { type: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
-            const requiredTypes = requiredTypeNameFields.map(({ type, backgroundColor, icon }) =>
-                doc[`template-note-${type}`] as Doc ??
-                (() => {
-                    const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor, system: true, icon });
-                    noteView.isTemplateDoc = makeTemplate(noteView, true, type);
-                    return doc[`template-note-${type}`] = new PrefetchProxy(noteView);
-                })());
-
-            doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument(requiredTypes, { title: "Note Layouts", _height: 75, system: true }));
-        }
+    // initializes experimental advanced template views - slideView,  headerView
+    static setupExperimentalTemplateButtons(doc: Doc, field="template-experimental-buttons") {
+        const tempDocs = DocCast(doc[field]);
+        const requiredTypeNameFields:{opts:DocumentOptions, template:() => Doc}[] = [
+            {
+                opts:{type: "slide", icon: "address-card"}, template: () => Docs.Create.MultirowDocument(
+                    [
+                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }),
+                        Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
+                    ],
+                    { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true }
+                )
+            },
+            {
+                opts:{type: "mobile", icon: "mobile"}, template: () => this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, },
+                    [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
+                    this.mobileTextContainer({},
+                        [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
+                    ]
+                )
+            },
+        ];
+        const requiredTypes = requiredTypeNameFields.map(({ opts, template }) => {
+            const docType =  DocListCast(tempDocs?.data)?.find(doc => doc.title === opts.type);
+            const reqdOpts = {
+                dragFactory: template(),
+                title: opts.type,
+                icon: opts.icon
+            };
+            const reqdScripts = {onDragStart: 'copyDragFactory(this.dragFactory)'};
+            const makeTemp = (doc:Doc) => {
+                doc.isTemplateDoc = makeTemplate(doc);
+                return doc;
+            }
+            return this.AssignScripts(!docType ? makeTemp(CurrentUserUtils.createToolButton(reqdOpts)) : this.AssignOpts(docType, reqdOpts)!, reqdScripts)!;
+        });
+
+        const reqdOpts = {
+            title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true,
+            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true,
+            _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
+        };
+        const reqdScripts = {dropConverter : "convertToButtons(dragData)"};
+        const reqdFuncs = {hidden: "IsNoviceMode()"}
+        return this.AssignScripts(!tempDocs ? 
+                (doc[field] = Docs.Create.MasonryDocument(requiredTypes, reqdOpts)) :
+                this.AssignOpts(tempDocs, reqdOpts, requiredTypes)!, 
+            reqdScripts, reqdFuncs);
+    }
+
+    /// Initializes templates that can be applied to notes
+    static setupNoteTemplates(doc: Doc, field="template-notes") {
+        const tempNotes = DocCast(doc[field]);
+        const reqdTempOpts:DocumentOptions[] = [
+            { noteType: "Note", backgroundColor: "yellow", icon: "sticky-note"},
+            { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
+            { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
+        const reqdNoteList = reqdTempOpts.map(opts => {
+            const reqdOpts = {...opts, title: "text", system: true};
+            const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
+            const makeTemp = (doc:Doc, noteType?:string) => {
+                doc.isTemplateDoc = makeTemplate(doc, true, noteType??"Note");
+                return doc;
+            }
+            return this.AssignOpts(noteType, reqdOpts) ?? makeTemp(Docs.Create.TextDocument("",reqdOpts), opts.noteType);
+        });
 
-        return doc["template-notes"] as Doc;
+        const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true };
+        return this.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
     }
 
-    // creates Note templates, and initial "user" templates
+    /// Initializes collection of templates for notes and click functions
     static setupDocTemplates(doc: Doc) {
-        if (doc.templateDocs === undefined) {
-            doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([
-                CurrentUserUtils.setupNoteTemplates(doc),
-                CurrentUserUtils.setupExperimentalTemplateButtons(doc),
-                CurrentUserUtils.setupClickEditorTemplates(doc)
-            ], {
-                title: "template layouts", _xMargin: 0, system: true,
-                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
-            }));
-        }
+        const templates = [
+            CurrentUserUtils.setupNoteTemplates(doc),
+            CurrentUserUtils.setupClickEditorTemplates(doc)
+        ];
+        const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
+        const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
+        return this.AssignScripts(this.AssignOpts(DocCast(doc.templateDocs), reqdOpts, templates) ?? (doc.templateDocs = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
-    static setupDefaultIconTemplates(doc: Doc) {
-        const templateIconsDoc = Cast(doc["template-icons"], Doc, null);
+    static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
+        const templateIconsDoc = DocCast(doc[field]);
 
         const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc) => {
             const iconFieldName = "icon" + (type ? "_" + type : "");
             if (!templateIconsDoc?.[iconFieldName]) {
                 const template = MakeTemplate(iconTemplate(), true, iconFieldName, templateField);
                 if (templateIconsDoc) {
-                    templateIconsDoc[iconFieldName] = new PrefetchProxy(template);
-                    Doc.AddDocToList(templateIconsDoc, "data", template);
-                } else {
-                    return template;
+                    templateIconsDoc[iconFieldName] = template;
                 }
+                return template;
             }
         };
         const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" });
@@ -182,46 +216,57 @@ export class CurrentUserUtils {
         const fontBox = () => Docs.Create.FontIconDocument({
             _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript()
         });
-        if (!templateIconsDoc) {
-            const newIconsList = [
-                makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
-                makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
-                makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
-                makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
-                makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
-                makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
-                makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-                makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-                makeIconTemplate(DocumentType.BUTTON, "data", fontBox),
-                //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
-                makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })),
-                // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
-            ].filter(d => d).map(d => d!);
-
-            doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true });
-        }
+        const iconTemplates = [
+            makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
+            makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
+            makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
+            makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
+            makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
+            makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
+            makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
+            makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
+            makeIconTemplate(DocumentType.BUTTON, "data", fontBox),
+            //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
+            makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })),
+            // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
+        ].filter(d => d).map(d => d!);
+
+        const reqdOpts = { title: "icon templates", _height: 75, system: true };
+        this.AssignOpts(templateIconsDoc, reqdOpts, iconTemplates) ?? (doc[field] = Docs.Create.TreeDocument(iconTemplates, reqdOpts));
     }
 
+    /// initalizes the set of default versions of most document types
     static creatorBtnDescriptors(doc: Doc): {
-        title: string, toolTip: string, icon: string, drag?: string, ignoreClick?: boolean,
-        click?: string, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean, clickFactory?: Doc
+        title: string, toolTip: string, icon: string,  ignoreClick?: boolean, dragFactory?: Doc, 
+        backgroundColor?: string,  clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string},
     }[] {
         const standardOps = () => ({ _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List<string>(["system"]) });
-        if (doc.emptyPresentation === undefined) {
-            doc.emptyPresentation = Docs.Create.PresDocument({ ...standardOps(), title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
-        }
-        if (doc.emptyCollection === undefined) {
-            doc.emptyCollection = Docs.Create.FreeformDocument([], { ...standardOps(), title: "freeform", _width: 150, _height: 100 });
-        }
-        if (doc.emptyPane === undefined) {
-            doc.emptyPane = Docs.Create.FreeformDocument([], { ...standardOps(), title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 });
-        }
-        if (doc.emptySlide === undefined) {
-            doc.emptySlide = Docs.Create.TreeDocument([], {
-                ...standardOps(), title: ComputedField.MakeFunction('self.text?.Text') as any, _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, "dragFactory-count": undefined,
-                allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white"
-            });
-        }
+        const emptyThings:{key:string,  // the field name where the empty thing will be stored
+                          opts:DocumentOptions,   // the document options that are required for the empty thing
+                          funcs?:{[key:string]: any},  // computed fields that are rquired for the empth thing
+                          creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist
+              }[] = [
+            {key: "emptyPresentation", creator: Docs.Create.PresDocument,  opts: { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }},
+            {key: "emptyCollection", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts: { title: "freeform", _width: 150, _height: 100 }},
+            {key: "emptyPane", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts :  { title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }},
+            {key: "emptySlide", creator: (opts) => Docs.Create.TreeDocument([], opts), funcs: {title: 'self.text?.Text'}, opts: {
+                _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, 
+                "dragFactory-count": undefined,  allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white"
+            }},
+            {key: "emptyComparison", creator: Docs.Create.ComparisonDocument, opts: { title: "Comparer", _width: 300, _height: 300 }},
+            {key: "emptyScript", creator: (opts) => Docs.Create.ScriptingDocument(undefined, opts), opts:  { title: "script", _width: 200, _height: 250, }},
+            {key: "emptyScreenshot", creator: Docs.Create.ScreenshotDocument, opts: { title: "empty screenshot", _width: 400, _height: 200 }},
+            {key: "emptyWebCam", creator: (opts) => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
+            {key: "emptyAudio", creator: (opts) => Docs.Create.AudioDocument(nullAudio, opts), opts: { title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, }},
+            {key: "emptyNote", creator: (opts) => Docs.Create.TextDocument("", opts), opts:  { title: "text note", _width: 200, _autoHeight: true }},
+            {key: "emptyButton", creator: Docs.Create.ButtonDocument, opts: { title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }},
+            {key: "emptyWebpage", creator: (opts) => Docs.Create.WebDocument("http://www.bing.com/", opts), opts: { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, }},
+            {key: "emptyMap", creator: (opts) =>  Docs.Create.MapDocument([], opts), opts: { title: "map", _showSidebar: true, _width: 800, _height: 600, }}
+        ];
+
+        emptyThings.forEach(thing => 
+            this.AssignScripts(this.AssignOpts(DocCast(doc[thing.key]), {...standardOps(), ...thing.opts}) ?? (doc[thing.key] = thing.creator({...standardOps(), ...thing.opts})), thing.funcs))
+        
         if (doc.emptyHeader === undefined) {
             const json = {
                 doc: {
@@ -242,196 +287,124 @@ export class CurrentUserUtils {
                 selection: { type: "text", anchor: 1, head: 1 },
                 storedMarks: []
             };
-            const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
-                ...standardOps(), title: "text", _height: 70,
-                _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,
-            }, "header");
             const headerBtnHgt = 10;
-            headerTemplate[DataSym].layout =
+            const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
+                ...standardOps(), title: "text", _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,
+                layout:
                 "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
                 `    <FormattedTextBox {...props} dontScale='true' fieldKey={'text'}  height='calc(100% - ${headerBtnHgt}px - {this._headerHeight||0}px)'/>` +
                 "    <FormattedTextBox {...props} dontScale='true' fieldKey={'header'}  dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize||9}px' height='{(this._headerHeight||0)}px' background='{this._headerColor || MySharedDocs().userColor||`lightGray`}' />" +
                 `    <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' background='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(0,this._height-30),this._headerHeight===0?50:0)) + (this._autoHeightMargins=this._headerHeight ? this._headerHeight+${headerBtnHgt}:0)’}  >Metadata</HTMLdiv>` +
-                "</HTMLdiv>";
+                "</HTMLdiv>"
+            }, "header");
 
             // "<div style={'height:100%'}>" +
             // "    <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" +
             // "    <FormattedTextBox {...props} fieldKey={'text'} position='absolute' top='{(this._headerHeight)*scale}px' height='calc({100/scale}% - {this._headerHeight}px)'/>" +
             // "</div>";
-            (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView");
+            Doc.GetProto(headerTemplate).isTemplateDoc = makeTemplate(Doc.GetProto(headerTemplate), true, "headerView");
             doc.emptyHeader = headerTemplate;
         }
-        if (doc.emptyComparison === undefined) {
-            doc.emptyComparison = Docs.Create.ComparisonDocument({ ...standardOps(), title: "Comparer", _width: 300, _height: 300 });
-        }
-        if (doc.emptyScript === undefined) {
-            doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { ...standardOps(), title: "script", _width: 200, _height: 250, });
-        }
-        if (doc.emptyScreenshot === undefined) {
-            doc.emptyScreenshot = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "empty screenshot", _width: 400, _height: 200 });
-        }
-        if (doc.emptyWall === undefined) {
-            doc.emptyWall = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            (doc.emptyWall as Doc).recording = true;
-        }
-        if (doc.emptyAudio === undefined) {
-            doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, });
-        }
-        if (doc.emptyNote === undefined) {
-            doc.emptyNote = Docs.Create.TextDocument("", { ...standardOps(), title: "text note", _width: 200, _autoHeight: true });
-        }
-        if (doc.emptyButton === undefined) {
-            doc.emptyButton = Docs.Create.ButtonDocument({ ...standardOps(), title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, });
-        }
-        if (doc.emptyWebpage === undefined) {
-            doc.emptyWebpage = Docs.Create.WebDocument("http://www.bing.com/", { ...standardOps(), title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, });
-        }
-        if (doc.emptyMap === undefined) {
-            doc.emptyMap = Docs.Create.MapDocument([], { ...standardOps(), title: "map", _showSidebar: true, _width: 800, _height: 600, });
-        }
-        if (doc.activeMobileMenu === undefined) {
-            this.setupActiveMobileMenu(doc);
-        }
+        
         return [
-            { toolTip: "Tap to create a note in a new pane, drag for a note", title: "Note", icon: "sticky-note", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyNote as Doc, noviceMode: true, clickFactory: doc.emptyNote as Doc, },
-            { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, },
-            { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
-            { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc },
-            { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
-            { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc },
-            { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc },
-            { toolTip: "Tap to create an audio recorder in a overlay pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openInOverlay(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
-            { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc },
-            // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
-            { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc },
-            { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
-            { toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc },
-            { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
-            { toolTip: "Tap to create a map in the new pane, drag for a map", title: "Map", icon: "map-marker-alt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyMap as Doc, noviceMode: true }
-        ];
-
+            { toolTip: "Tap or drag to create a note",            title: "Note", icon: "sticky-note",   dragFactory: doc.emptyNote as Doc,        scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create a collection",      title: "Col", icon: "folder",         dragFactory: doc.emptyCollection as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, clickFactory: doc.emptyPane as Doc, },
+            { toolTip: "Tap or drag to create a webpage",         title: "Web", icon: "globe-asia",     dragFactory: doc.emptyWebpage as Doc,     scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create a comparison box",  title: "Compare", icon: "columns",    dragFactory: doc.emptyComparison as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone",   dragFactory: doc.emptyAudio as Doc,       scripts: {onClick: 'openInOverlay(copyDragFactory(this.dragFactory))',onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create a map",             title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc,         scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create a custom note",     title: "Custom", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc,  scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'},  },
+            { toolTip: "Tap or drag to create a progressive slide",title: "Slide", icon: "file",        dragFactory: doc.emptySlide as Doc,       scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a screen grabber",  title: "Grab", icon: "photo-video",   dragFactory: doc.emptyScreenshot as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a button",          title: "Button", icon: "bolt",        dragFactory: doc.emptyButton as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs:{ hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a mobile view",     title: "Phone", icon: "mobile",       dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)',     onDragStart: 'this.dragFactory'},                             funcs: {hidden: 'IsNoviceMode()'} },
+            { toolTip: "Toggle a Calculator REPL",                title: "repl", icon: "calculator",                                              scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } },
+         // { toolTip: "Tap or drag to create a presentation",    title: "Trails", icon: "pres-trail",  dragFactory: doc.emptyPresentation as Doc,scripts: {onClick: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', onDragStart: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`}, funcs: {hidden: 'IsNoviceMode()'} },
+             ];
     }
 
-    // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
-    static setupCreatorButtons(doc: Doc) {
-        let alreadyCreatedButtons: string[] = [];
-        const dragCreatorSet = Cast(doc.myItemCreators, Doc, null);
-        if (dragCreatorSet) {
-            alreadyCreatedButtons = DocListCast(dragCreatorSet.data).map(d => StrCast(d.title));
-        }
-        const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title));
-        const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, backgroundColor, dragFactory, noviceMode, clickFactory }) => Docs.Create.FontIconDocument({
-            _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35,
-            icon,
-            title,
-            toolTip,
-            btnType: ButtonType.ToolButton,
-            ignoreClick,
-            _dropAction: "alias",
-            onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined,
-            onClick: click ? ScriptField.MakeScript(click) : undefined,
-            backgroundColor: backgroundColor ? backgroundColor : Colors.DARK_GRAY,
-            color: Colors.WHITE,
-            _hideContextMenu: true,
-            _removeDropProperties: new List<string>(["_stayInCollection"]),
-            _stayInCollection: true,
-            dragFactory,
-            clickFactory,
-            hidden: !noviceMode ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined,
-            system: true,
-        }));
+    /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
+    static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc {
+        const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
+            const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
+            const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["backgroundColor"]).omit,
+                _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, _dropAction: "alias",
+                btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
+                _removeDropProperties: new List<string>(["_stayInCollection"]),
+                };
+            return this.AssignScripts(this.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
+        });
 
-        if (dragCreatorSet === undefined) {
-            doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
-                title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true,
-                _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true
-            }));
-        } else {
-            creatorBtns.forEach(nb => Doc.AddDocToList(doc.myItemCreators as Doc, "data", nb));
-        }
-        return doc.myItemCreators as Doc;
+        const reqdOpts = {
+            title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true,
+            _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
+        };
+        const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
+        return this.AssignScripts(this.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
     }
 
-    static menuBtnDescriptions(doc: Doc) {
-        const badgeValue = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)")
+    /// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
+    static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] {
+        const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())";
         return [
-            { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
-            { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
-            { title: "Files", target: Cast(doc.myFilesystem, Doc, null), icon: "folder-open", click: 'selectMainMenu(self)' },
-            { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
-            { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
-            { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
-            { title: "Shared  with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue},
-            { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
-            { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
+            { title: "Dashboards",      target: CurrentUserUtils.MyDashboards,     icon: "desktop",     scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Search",          target: CurrentUserUtils.MySearcher,       icon: "search",      scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Files",           target: CurrentUserUtils.MyFilesystem,     icon: "folder-open", scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Tools",           target: CurrentUserUtils.MyTools,          icon: "wrench",      scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} },
+            { title: "Imports",         target: CurrentUserUtils.MyImports,        icon: "upload",      scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Recently Closed", target: CurrentUserUtils.MyRecentlyClosed, icon: "archive",     scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Shared with me",  target: CurrentUserUtils.MySharedDocs,     icon: "users",       scripts:{onClick: 'selectMainMenu(self)'}, funcs:{badgeValue:badgeValue}},
+            { title: "Trails",          target: CurrentUserUtils.MyTrails,         icon: "pres-trail",  scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "User Doc",        target: CurrentUserUtils.MyUserDocView,    icon: "address-card",scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} },
         ];
     }
 
-    static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
-        if (doc.menuStack === undefined) {
-            await this.setupLinkDocs(doc, linkDatabaseId);
-            await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
-            const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) =>
-                Docs.Create.FontIconDocument({
-                    icon,
-                    btnType: ButtonType.MenuButton,
-                    _stayInCollection: true,
-                    _hideContextMenu: true,
-                    _chromeHidden: true,
-                    system: true,
-                    dontUndo: true,
-                    title,
-                    target,
-                    dontRegisterView: true,
-                    hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined,
-                    _dropAction: "alias",
-                    _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
-                    _width: 60,
-                    _height: 60,
-                    badgeValue,
-                    onClick: ScriptField.MakeScript(click, { scriptContext: "any" })
-                })
-            );
-
-            doc.searchBtn = menuBtns.find(btn => btn.title === "Search");
-
-            doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, {
-                title: "menuItemPanel",
-                childDropAction: "alias",
-                _chromeHidden: true,
-                backgroundColor: Colors.DARK_GRAY,
-                boxShadow: "rgba(0,0,0,0)",
-                dontRegisterView: true,
-                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-                ignoreClick: true,
-                _gridGap: 0,
-                _yMargin: 0,
-                _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
-            }));
-        }
-        return doc.menuStack as Doc;
+    /// setup the left sidebar container and panels that can be displayed within it
+    static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
+        this.AssignOpts(DocCast(doc[field]), {}) ?? (doc[field] = ((doc:Doc) => {doc.system = true; return doc;})(new Doc()));
+        CurrentUserUtils.setupSearcher(doc, "mySearcher");
+        CurrentUserUtils.setupToolsBtnPanel(doc, "myTools");
+        CurrentUserUtils.setupImportSidebar(doc, "myImports");
+        CurrentUserUtils.setupDashboards(doc, "myDashboards");
+        CurrentUserUtils.setupTrails(doc, "myTrails");
+        CurrentUserUtils.setupFilesystem(doc, "myFilesystem");
+        CurrentUserUtils.setupRecentlyClosedDocs(doc, "myRecentlyClosed");
+        CurrentUserUtils.setupUserDocView(doc, "myUserDocView");
     }
 
+    /// Initializes the left sidebar menu buttons and the panels they open up
+    static setupLeftSidebarMenu(doc: Doc, field="myLeftSidebarMenu") {
+        this.setupLeftSidebarPanel(doc); // the tools/panels opened up by the menu buttons
+        const myLeftSidebarMenu = DocCast(doc[field]);
+        const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => {
+            const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
+            const reqdBtnOpts:DocumentOptions = {
+                title, icon, target, btnType: ButtonType.MenuButton, system: true, dontUndo: true,  dontRegisterView: true,
+                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, _dropAction: "alias",
+                _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
+            };
+            return this.AssignScripts(this.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
+        });
 
-    // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
-    static setupActiveMobileMenu(doc: Doc) {
-        if (doc.activeMobileMenu === undefined) {
-            doc.activeMobileMenu = this.setupMobileMenu();
-        }
-        return doc.activeMobileMenu as Doc;
+        const reqdStackOpts:DocumentOptions ={
+            title: "menuItemPanel", childDropAction: "alias", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
+            _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
+        };
+        const reqdScripts = { dropConverter: "convertToButtons(dragData)" }
+        return this.AssignScripts(this.AssignOpts(myLeftSidebarMenu, reqdStackOpts, menuBtns) ?? (doc[field] = Docs.Create.StackingDocument(menuBtns, reqdStackOpts)), reqdScripts);
     }
 
-    // Sets up mobileMenu stacking document
-    static setupMobileMenu() {
-        const menu = new PrefetchProxy(Docs.Create.StackingDocument(this.setupMobileButtons(), {
-            _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,
-        }));
-        return menu;
+    // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
+    static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") {
+        const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,};
+        this.AssignOpts(DocCast(doc[field]), reqdOpts, this.setupMobileButtons()) ?? (doc[field] = Docs.Create.StackingDocument(this.setupMobileButtons(), reqdOpts));
     }
 
-    // SEts up mobile buttons for inside mobile menu
+    // Sets up mobile buttons for inside mobile menu
     static setupMobileButtons(doc?: Doc, buttons?: string[]) {
+        return [];
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
             { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " },
             { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
@@ -533,375 +506,329 @@ export class CurrentUserUtils {
         });
     }
 
-    static setupLibrary(userDoc: Doc) {
-        return CurrentUserUtils.setupDashboards(userDoc);
-    }
-
-    // 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 setupToolsBtnPanel(doc: Doc) {
-        // setup a masonry view of all he creators
-        const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc);
-        const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc);
-
-        doc.myCreators = doc.myCreators ?? new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
-            title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true,
-            _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true,
-        }));
-        if (!DocListCast(doc.myCreators).includes(creatorBtns) || !DocListCast(doc.myCreators).includes(templateBtns)) Doc.GetProto(doc.myCreators as Doc).data = new List<Doc>([creatorBtns, templateBtns]);
-
-        doc.myColorPicker = doc.myColorPicker ?? new PrefetchProxy(Docs.Create.ColorDocument({
-            title: "color picker", _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List<string>(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true
-        }));
-
-        doc.myTools = doc.myTools ?? new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc], {
-            title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-            system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0",
-        })) as any as Doc;
-        if (!DocListCast(doc.myTools).includes(doc.myCreators as Doc)) Doc.GetProto(doc.myTools as Doc).data = new List<Doc>([doc.myCreators as Doc]);
-    }
-
-    static async setupDashboards(doc: Doc) {
-        // setup dashboards library item
-        await doc.myDashboards;
-        if (doc.myDashboards === undefined) {
-            const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
-            const newDashboardButton: Doc = Docs.Create.FontIconDocument({ onClick: newDashboard, _forceActive: true, toolTip: "Create new dashboard", _stayInCollection: true, _hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
-            doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, freezeChildren: "remove|add",
-                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newDashboardButton,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true,
-                explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
-            }));
-            const toggleDarkTheme = ScriptField.MakeScript(`this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`);
-            const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-            const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
-            const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`);
-            const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
-            const developerFilter = ScriptField.MakeFunction('!IsNoviceMode()');
-            // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]);
-            // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]);
-            // (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "user-friends", "times"]);
-            (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleDarkTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
-            (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
-            (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "chalkboard", "tv", "camera", "users", "times"]);
-            (doc.myDashboards as any as Doc).childContextMenuFilters = new List<ScriptField>([undefined as any, developerFilter, developerFilter, developerFilter, undefined as any, undefined as any]);
-        }
-        return doc.myDashboards as any as Doc;
+    /// Search option on the left side button panel
+    static setupSearcher(doc: Doc, field:string) {
+        const reqdOpts:DocumentOptions = {
+            dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", 
+            _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true,
+        };
+        this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.SearchDocument(reqdOpts));
     }
 
-    static setupPresentations(doc: Doc) {
-        if (doc.myTrails === undefined) {
-            const newTrail = ScriptField.MakeScript(`createNewPresentation()`);
-            const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
-            doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "My Trails", _showTitle: "title", _height: 100,
-                treeViewHideTitle: true, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newTrailButton,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
-                explainer: "All of the trails that you have created will appear here."
-            }));
-            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newTrail!]);
-            (doc.myTrails as any as Doc).contextMenuLabels = new List<string>(["Create New Trail"]);
-            (doc.myTrails as any as Doc).childContextMenuIcons = new List<string>(["plus"]);
-        }
-        return doc.myTrails as any as Doc;
+    /// Initializes the panel of draggable tools that is opened from the left sidebar.
+    static setupToolsBtnPanel(doc: Doc, field:string) {
+        const myTools = DocCast(doc[field]);
+        const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined);
+        //const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc);
+        const reqdToolOps:DocumentOptions = {
+            title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
+            _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
+        };
+        this.AssignOpts(myTools, reqdToolOps, [creatorBtns, /*templateBtns*/]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, /*templateBtns*/], reqdToolOps));
     }
 
-    static async setupFilesystem(doc: Doc) {
-        await doc.myFilesystem;
-        if (doc.myFilesystem === undefined) {
-            doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
-            // doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true });
-            const newFolder = ScriptField.MakeFunction(`makeTopLevelFolder()`, { scriptContext: "any" })!;
-            const newFolderButton: Doc = Docs.Create.FontIconDocument({
-                onClick: newFolder, _forceActive: true, toolTip: "Create new folder",
-                _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30,
-                buttonText: "New folder", icon: "folder-plus", system: true
-            });
-            doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], {
-                title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
-                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
-                explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
-            }));
-            (doc.myFilesystem as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder]);
-            (doc.myFilesystem as any as Doc).contextMenuLabels = new List<string>(["Create new folder"]);
-            (doc.myFilesystem as any as Doc).childContextMenuIcons = new List<string>(["plus"]);
+    /// initializes the left sidebar dashboard pane
+    static setupDashboards(doc: Doc, field:string) {
+        var myDashboards = DocCast(doc[field]);
+
+        const newDashboard = `createNewDashboard(Doc.UserDoc())`;
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
+             title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
+        const reqdBtnScript = {onClick: newDashboard,}
+        const newDashboardButton = this.AssignScripts(this.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+       
+        const reqdOpts:DocumentOptions = {
+            title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true,
+            targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true,  treeViewTruncateTitleWidth: 150, ignoreClick: true, 
+            buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "alias",
+            _showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
+            contextMenuLabels: new List<string>(["Create New Dashboard"]),
+            contextMenuIcons: new List<string>(["plus"]),
+            childContextMenuLabels: new List<string>(["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]),// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
+            childContextMenuIcons: new List<string>(["chalkboard", "tv", "camera", "users", "times"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
+            explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
+        };
+        myDashboards = this.AssignOpts(myDashboards, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+        const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`;
+        const contextMenuScripts = [newDashboard];
+        const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters
+        const childContextMenuFilters =  ['!IsNoviceMode()', '!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts
+        if (Cast(myDashboards.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) {
+            myDashboards.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
+        } 
+        if (Cast(myDashboards.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
+            myDashboards.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
+        } 
+        if (Cast(myDashboards.childContextMenuFilters, listSpec(ScriptField), null)?.length !== childContextMenuFilters.length) {
+            myDashboards.childContextMenuFilters = new List<ScriptField>(childContextMenuFilters.map(script => !script ? script: ScriptField.MakeFunction(script)!));
         }
-        return doc.myFilesystem as any as Doc;
     }
 
-    static setupRecentlyClosedDocs(doc: Doc) {
-        if (doc.myRecentlyClosedDocs === undefined) {
-            const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
-            const clearDocsButton: Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true });
-            doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "My Recently Closed", _showTitle: "title", buttonMenu: true, buttonMenuDoc: clearDocsButton, childHideLinkButton: true,
-                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
-                explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
-
-            }));
-            (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-            (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Empty recently closed"]);
-            (doc.myRecentlyClosedDocs as any as Doc).contextMenuIcons = new List<string>(["trash"]);
-
+    /// initializes the left sidebar Trails pane
+    static setupTrails(doc: Doc, field:string) {
+        var myTrails = DocCast(doc[field]);
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30,  _stayInCollection: true, _hideContextMenu: true, 
+            title: "New trail",  toolTip: "Create new trail", btnType: ButtonType.ClickButton, buttonText: "New trail", icon: "plus", system: true };
+        const reqdBtnScript = {onClick: `createNewPresentation()`};
+        const newTrailButton = this.AssignScripts(this.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+        
+        const reqdOpts:DocumentOptions = {
+            title: "My Trails", _showTitle: "title", _height: 100,
+            treeViewHideTitle: true, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+            treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newTrailButton,
+            contextMenuIcons: new List<string>(["plus"]),
+            contextMenuLabels: new List<string>(["Create New Trail"]),
+            _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
+            explainer: "All of the trails that you have created will appear here."
+        };
+        myTrails = this.AssignOpts(myTrails, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([],reqdOpts ));
+        const contextMenuScripts = [reqdBtnScript.onClick];
+        if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) {
+            myTrails.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
         }
     }
 
-    static setupFilterDocs(doc: Doc) {
-        // setup Filter item
-        if (doc.currentFilter === undefined) {
-            doc.currentFilter = Docs.Create.FilterDocument({
-                title: "Unnamed Filter", _height: 150,
-                treeViewHideTitle: true, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true, childDropAction: "none",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, _autoHeight: true, _fitWidth: true
-            });
-            const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
-            (doc.currentFilter as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-            (doc.currentFilter as Doc).contextMenuLabels = new List<string>(["Clear All"]);
-            (doc.currentFilter as Doc).filterBoolean = "AND";
+    /// initializes the left sidebar File system pane
+    static setupFilesystem(doc: Doc, field:string) {
+        var myFilesystem = DocCast(doc[field]);
+        const reqdOrphansOpts:DocumentOptions = { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true };
+        this.AssignOpts(DocCast(doc.myFileOrphans), reqdOrphansOpts) ?? (doc.myFileOrphans = Docs.Create.TreeDocument([], reqdOrphansOpts));
+        
+        const newFolder = `makeTopLevelFolder()`;
+        const newFolderOpts: DocumentOptions = {
+            _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30,
+            title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true
+        };
+        const newFolderScript = { onClick: newFolder};
+        const newFolderButton = this.AssignScripts(this.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
+        
+        const reqdOpts:DocumentOptions = { _showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
+            title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", system: true,
+            isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, boxShadow: "0 0", childDontRegisterViews: true, 
+            treeViewTruncateTitleWidth: 150, ignoreClick: true, childDropAction: "alias",
+            childContextMenuLabels: new List<string>(["Create new folder"]),
+            childContextMenuIcons: new List<string>(["plus"]),
+            explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
+        };
+        myFilesystem = this.AssignOpts(myFilesystem, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([DocCast(doc.myFileOrphans)], reqdOpts));
+        const childContextMenuScripts = [newFolder];
+        if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
+            myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
         }
     }
 
-    static setupUserDoc(doc: Doc) {
-        if (doc.myUserDoc === undefined) {
-            doc.treeViewOpen = true;
-            doc.treeViewExpandedView = "fields";
-            doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
-                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
-            })) as any as Doc;
+    /// initializes the panel displaying docs that have been recently closed
+    static setupRecentlyClosedDocs(doc: Doc, field:string) {
+        const reqdOpts:DocumentOptions = { _showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
+            title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "alias", system: true,
+            treeViewTruncateTitleWidth: 150, ignoreClick: true,  boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
+            contextMenuLabels: new List<string>(["Empty recently closed"]),
+            contextMenuIcons:new List<string>(["trash"]),
+            explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
+        };
+        const recentlyClosed = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+
+        const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
+        const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, 
+            title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", system: true,
+            toolTip: "Empty recently closed",};
+        const clearBtnsScripts = {onClick: clearAll("self.target")}
+        const clearDocsButton = this.AssignScripts(
+                this.AssignOpts(DocCast(recentlyClosed?.clearDocsBtn), clearBtnsOpts) ?? (recentlyClosed.clearDocsBtn = Docs.Create.FontIconDocument(clearBtnsOpts)),
+                clearBtnsScripts);
+
+        if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton;
+        
+        if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
+            recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
         }
     }
 
-    static setupSidebarContainer(doc: Doc) {
-        if (doc.sidebar === undefined) {
-            const sidebarContainer = new Doc();
-            sidebarContainer.system = true;
-            doc.sidebar = new PrefetchProxy(sidebarContainer);
-        }
-        return doc.sidebar as Doc;
+    /// creates a new, empty filter doc
+    static createFilterDoc() {
+        const clearAll = `getProto(self).data = new List([])`;
+        const reqdOpts:DocumentOptions = {
+            _lockedPosition: true,  _autoHeight: true, _fitWidth: true, _height: 150, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true, 
+            title: "Unnamed Filter", filterBoolean: "AND", boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
+            childDropAction: "none", treeViewHideTitle: true, treeViewTruncateTitleWidth: 150, 
+            childContextMenuLabels: new List<string>(["Clear All"]),
+            childContextMenuScripts: new List<ScriptField>([ScriptField.MakeFunction(clearAll)!]),
+        };
+        return Docs.Create.FilterDocument(reqdOpts);
     }
 
-    // setup the list of sidebar mode buttons which determine what is displayed in the sidebar
-    static async setupSidebarButtons(doc: Doc) {
-        CurrentUserUtils.setupSidebarContainer(doc);
-        CurrentUserUtils.setupToolsBtnPanel(doc);
-        CurrentUserUtils.setupImportSidebar(doc);
-        CurrentUserUtils.setupDashboards(doc);
-        CurrentUserUtils.setupPresentations(doc);
-        CurrentUserUtils.setupFilesystem(doc);
-        CurrentUserUtils.setupRecentlyClosedDocs(doc);
-        CurrentUserUtils.setupUserDoc(doc);
+    /// initializes the left sidebar panel view of the UserDoc
+    static setupUserDocView(doc: Doc, field:string) {
+        const reqdOpts:DocumentOptions = {
+            _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
+            boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
+            treeViewHideTitle: true, treeViewTruncateTitleWidth: 150
+        };
+        if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" })
+        this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([doc], reqdOpts));
     }
 
-    static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
-        ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
+    static linearButtonList = (title: string, opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
+        title, ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
         dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
         _lockedPosition: true, system: true, flexDirection: "row"
-    })) as any as Doc
+    })
 
-    static createToolButton = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
+    static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
         btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["_dropAction", "_hideContextMenu", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts,
-    })) 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 setupDockedButtons(doc: Doc) {
-        if (doc.dockedBtns === undefined) {
-            const dockBtn = (opts: DocumentOptions) => CurrentUserUtils.createToolButton({ _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...opts });
-            const btnDescs = [
-                { title: "undo", opts: () => ({ icon: "undo-alt", onClick: ScriptField.MakeScript("undo()"), toolTip: "Click to undo" }) },
-                { title: "redo", opts: () => ({ icon: "redo-alt", onClick: ScriptField.MakeScript("redo()"), toolTip: "Click to redo" }) }
-            ];
-            doc.dockedBtns = CurrentUserUtils.linearButtonList({
-                title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
-                childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
-            }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() }))));
+    })
+
+    /// initializes the required buttons in the expanding button menu at the bottom of the Dash window
+    static setupDockedButtons(doc: Doc, field="myDockedBtns") {
+        const dockedBtns = DocCast(doc[field]);
+        const dockBtn = (title:string, onClick: string, opts: DocumentOptions) => {
+            const btn = this.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === title), opts) ?? 
+                            CurrentUserUtils.createToolButton({title, ...opts});
+            this.AssignScripts(btn, {onClick})
+            return btn;
         }
+        const btnDescs = [// 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
+            { title: "undo", click: "undo()", opts: { icon: "undo-alt", toolTip: "Click to undo" }},
+            { title: "redo", click:"redo()", opts: { icon: "redo-alt", toolTip: "Click to redo" }}
+        ];
+        const btns = btnDescs.map(desc => dockBtn(desc.title, desc.click, {_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}));
+        const dockBtnsReqdOpts = {
+            title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
+            childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
+        };
+        reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
+        reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
+        return this.AssignOpts(dockedBtns, dockBtnsReqdOpts, btns) ?? (doc[field] = CurrentUserUtils.linearButtonList("dockedBtns", dockBtnsReqdOpts, btns));
     }
 
-    static textTools(doc: Doc) {
-        const tools: Button[] =
-            [
-                {
-                    title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
-                    list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia",
-                        "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"],
-                    script: 'setFont(value, _readOnly_)'
-                },
-                { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' },
-                { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' },
-                { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' },
-                { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' },
-                { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' },
-                { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' },
-                { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' },
-
-                // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
-                // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
-                // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
-                { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
-                { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
-                { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
-                { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", click: 'toggleNoAutoLinkAnchor(_readOnly_)' },
-            ];
-        return tools;
+    static textTools():Button[] {
+        return [
+            {
+                title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
+                btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]),
+                scripts :{script : 'setFont(value, _readOnly_)'}
+            },
+            { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'} },
+            { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts:{script: '{ return setFontColor(value, _readOnly_); }'}},
+            { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} },
+            { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} },
+            { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline",scripts: {onClick:'{ return toggleUnderline(_readOnly_);}'} },
+            { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", scripts: {onClick: '{ return setBulletList("bullet", _readOnly_);}'} },
+            { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", scripts: {onClick: '{ return setBulletList("decimal", _readOnly_);}'} },
+
+            // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}},
+            // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", scripts: {onClick:: 'toggleSuperscript()'}},
+            // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", scripts: {onClick:: 'toggleSubscript()'}},
+            { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", scripts: {onClick:'{ return setAlignment("left", _readOnly_);}' }},
+            { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", scripts: {onClick:'{ return setAlignment("center", _readOnly_);}'} },
+            { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} },
+            { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}},
+        ];
     }
 
-    static inkTools(doc: Doc) {
-        const tools: Button[] = [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", click: 'setActiveInkTool("pen", _readOnly_)' },
-            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' },
-            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' },
-            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' },
-            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' },
+    static inkTools():Button[] {
+        return [
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveInkTool("pen", _readOnly_);}' }},
+            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveInkTool("write", _readOnly_);}'} },
+            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveInkTool("eraser", _readOnly_);}' }},
+            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveInkTool("highlighter")'} },
+            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveInkTool("circle", _readOnly_);}'} },
             // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' },
-            { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" },
-            { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' },
-            { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveInkTool("line", _readOnly_);}' }},
+            { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: {script: "{ return setFillColor(value, _readOnly_);}"} },
+            { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'} },
+            { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} },
         ];
-        return tools;
     }
 
-    static schemaTools(doc: Doc) {
-        const tools: Button[] =
-            [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", click: 'toggleSchemaPreview(_readOnly_)', }];
-        return tools;
+    static schemaTools():Button[] {
+        return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{onClick:'toggleSchemaPreview(_readOnly_)'}, }];
     }
 
-    static webTools(doc: Doc) {
-        const tools: Button[] =
-            [
-                { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' },
-                { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' },
-                //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' },
-                { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' },
-            ];
-
-        return tools;
+    static webTools() {
+        return [
+            { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' },
+            { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' },
+            //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' },
+            { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' },
+        ];
     }
 
-    static contextMenuTools(doc: Doc) {
+    static contextMenuTools():Button[] {
         return [
             {
                 title: "Perspective", toolTip: "View", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
-                list: [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree,
-                CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn,
-                CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
-                CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
-                CollectionViewType.Grid],
-                script: 'setView(value, _readOnly_)',
+                btnList: new List<string>([CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree,
+                    CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn,
+                    CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
+                    CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
+                    CollectionViewType.Grid]),
+                scripts: {script: 'setView(value, _readOnly_)'},
             }, // Always show
-            { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame(_readOnly_)', icon: "chevron-left", hidden: 'IsNoviceMode()' },
-            { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame(_readOnly_)', icon: "chevron-right", hidden: 'IsNoviceMode()' },
-            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' }, // Only when a document is selected
-            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()', },
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
+            { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton,icon: "chevron-left",  scripts:{onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'} },
+            { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, icon: "chevron-right", scripts:{onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}},
+            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: 'selectedDocumentType()' }}, // Only when a document is selected
+            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: 'selectedDocumentType()'} },
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: 'selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
-            { title: "Text", type: "textTools", subMenu: CurrentUserUtils.textTools(doc), expanded: 'selectedDocumentType("rtf")' }, // Always available
-            { title: "Ink", type: "inkTools", subMenu: CurrentUserUtils.inkTools(doc), expanded: 'selectedDocumentType("ink")' }, // Always available
-            { title: "Web", type: "webTools", subMenu: CurrentUserUtils.webTools(doc), hidden: 'selectedDocumentType("web")' }, // Only when Web is selected
-            { title: "Schema", type: "schemaTools", subMenu: CurrentUserUtils.schemaTools(doc), hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected
+            { title: "Text", icon: "text",  subMenu: CurrentUserUtils.textTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available
+            { title: "Ink", icon: "ink", subMenu: CurrentUserUtils.inkTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available
+            { title: "Web", icon: "web", subMenu: CurrentUserUtils.webTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.WEB}")`, hidden: `!selectedDocumentType("${DocumentType.WEB}")`} }, // Only when Web is selected
+            { title: "Schema", icon: "schema", subMenu: CurrentUserUtils.schemaTools(), funcs: { linearViewIsExpanded: `selectedDocumentType(undefined, "${CollectionViewType.Schema}")`, hidden: `!selectedDocumentType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected
         ];
     }
 
-    // Sets up the default context menu buttons
-    static setupContextMenuButtons(doc: Doc) {
-        const btnFunc = (params: Button) => Docs.Create.FontIconDocument({
-            title: params.title, icon: params.icon, toolTip: params.toolTip, color: Colors.WHITE, system: true, dontUndo: true, ignoreClick: params.ignoreClick,
+    /// initializes a context menu button for the top bar context menu
+    static setupContextMenuButton(params:Button, btnDoc?:Doc) {
+        const reqdOpts:DocumentOptions = {
+            title: params.title, btnType: params.btnType, icon: params.icon, toolTip: params.toolTip, ignoreClick: params.ignoreClick, 
+            numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax,_nativeHeight: 30, btnList: params.btnList,
+            backgroundColor: params.scripts?.onClick ? undefined: "transparent", ///  a bit hacky. if an onClick is specified, then we assume we assume a toggle use onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
             _nativeWidth: params.width ? params.width : 30,
-            _nativeHeight: 30,
             _width: params.width ? params.width : 30,
             _height: 30,
-            btnType: params.btnType,
-            numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax,
-            btnList: new List<string>(params.list),
+            color: Colors.WHITE, system: true, dontUndo: true, 
             _stayInCollection: true,
             _hideContextMenu: true,
             _lockedPosition: true,
             _dropAction: "alias",
             _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
-            script: params.script ? ScriptField.MakeScript(params.script, { value: "any" }) : undefined,
-            backgroundColor: params.click ? ComputedField.MakeFunction(params.click) as any : "transparent",
-            onClick: params.click ? ScriptField.MakeScript(params.click, { scriptContext: "any" }, { _readOnly_: false }) : undefined,
-            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
-        });
-        if (doc.contextMenuBtns === undefined) {
-            doc.contextMenuBtns = CurrentUserUtils.linearButtonList(
-                { title: "menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
-                CurrentUserUtils.contextMenuTools(doc).map(params =>
-                    !params.subMenu ?
-                        btnFunc(params) :
-                        CurrentUserUtils.linearButtonList({
-                            title: params.title,
-                            childDontRegisterViews: true,
-                            linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
-                            linearViewExpandable: true, icon: params.title, _height: 30,
-                            linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
-                            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
-                        }, params.subMenu.map(btnFunc))));
-        } else {
-            const menuBtnList = DocListCast((doc.contextMenuBtns as Doc).data);
-            let prev = "";
-            CurrentUserUtils.contextMenuTools(doc).forEach(params => {
-                const menuBtnDoc = menuBtnList.find(doc => doc.title === params.title);
-                if (!menuBtnDoc) {
-                    const newMenuBtnDoc = !params.subMenu ?
-                        btnFunc(params) :
-                        CurrentUserUtils.linearButtonList({
-                            title: params.title,
-                            childDontRegisterViews: true,
-                            linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
-                            linearViewExpandable: true, icon: params.title, _height: 30,
-                            linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
-                            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
-                        }, params.subMenu.map(btnFunc));
-                    const after = menuBtnList.find(doc => doc.title === prev);
-                    Doc.AddDocToList(doc.contextMenuBtns as Doc, "data", newMenuBtnDoc, after, false, !after);
-                }
-                const subMenuBtnList = menuBtnDoc?.data ? DocListCast(menuBtnDoc.data) : undefined;
-                if (menuBtnDoc && subMenuBtnList && params.subMenu && DocListCast(doc.data).length !== subMenuBtnList.length) {
-                    let prevSub = "";
-                    params.subMenu.forEach(sub => {
-                        if (!subMenuBtnList.find(doc => doc.title === sub.title)) {
-                            const newSubMenuBtnDoc = btnFunc(sub);
-                            const after = subMenuBtnList.find(doc => doc.title === prevSub);
-                            Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub);
-                        }
-                        prevSub = params.title;
-                    });
-                }
-                prev = params.title;
-            });
+        };
+        const reqdFuncs:{[key:string]:any} = {
+            ...params.funcs,
+            backgroundColor: params.scripts?.onClick /// a bit hacky.  if onClick is set, then we assume it returns a color value when queried with '_readOnly_'.  This will be true for toggle buttons, but not generally
         }
+        return this.AssignScripts(this.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
     }
 
-    // sets up the default set of documents to be shown in the Overlay layer
-    static setupOverlays(doc: Doc) {
-        if (doc.myOverlayDocs === undefined) {
-            doc.myOverlayDocs = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true }));
-        }
+    /// Initializes all the default buttons for the top bar context menu
+    static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
+        const ctxtMenuBtnsDoc = DocCast(doc[field]);
+        const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
+            const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
+            if (!params.subMenu) {
+                return this.setupContextMenuButton(params, menuBtnDoc);
+            } else {
+                const reqdSubMenuOpts = { title: params.title, icon: params.icon, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true,
+                    linearViewSubMenu: true, linearViewExpandable: true, };
+                const reqdSubMenuFuncs:{[key:string]:any} = { ...params.funcs};
+                return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ??
+                        CurrentUserUtils.linearButtonList("submenu", reqdSubMenuOpts, params.subMenu.map(sub =>
+                            this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title))
+                        )), undefined, reqdSubMenuFuncs);
+            }
+        });
+        const reqdCtxtOpts = { title: "menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
+        return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts) ?? (doc[field] = CurrentUserUtils.linearButtonList("contextMenuButtons", reqdCtxtOpts, ctxtMenuBtns));
     }
 
-    // the initial presentation Doc to use
-    static setupDefaultPresentation(doc: Doc) {
-        if (doc["template-presentation"] === undefined) {
-            doc["template-presentation"] = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
-                title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _fitWidth: true, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true
-            }));
-        }
+    /// collection of documents rendered in the overlay layer above all tabs and other UI
+    static setupOverlays(doc: Doc, field = "myOverlayDocs") {
+        const reqdOpts = { title: "overlay documents", backgroundColor: "#aca3a6", system: true };
+        return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.FreeformDocument([], reqdOpts));
     }
-
-    // Sharing sidebar is where shared documents are contained
+    
+    /// The database of all links on all documents
     static async setupLinkDocs(doc: Doc, linkDatabaseId: string) {
         if (doc.myLinkDatabase === undefined) {
             let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId);
@@ -915,6 +842,8 @@ export class CurrentUserUtils {
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
         }
     }
+
+    /// Shared documents option on the left side button panel
     // A user's sharing document is where all documents that are shared to that user are placed.  
     // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field
     // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents
@@ -941,37 +870,26 @@ export class CurrentUserUtils {
         };
         
         const sharedDocs = Docs.newAccount ? undefined : DocCast(doc.mySharedDocs) ?? DocCast(await DocServer.GetRefField(sharingDocumentId + "outer"));
-        if (!(sharedDocs instanceof Doc)) {
-            doc.mySharedDocs = new PrefetchProxy(
-                Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId));
-        } else {
-            Object.entries(sharedRequiredDocOpts).forEach(pair => {
-                const targetDoc = pair[0].startsWith("_") ? sharedDocs as Doc : Doc.GetProto(sharedDocs as Doc);
-                targetDoc[pair[0]] = pair[1];
-            });
-        }
+        return this.AssignOpts(DocCast(sharedDocs), sharedRequiredDocOpts) ?? 
+            (doc.mySharedDocs = Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId));
     }
 
-    // Import sidebar is where shared documents are contained
-    static setupImportSidebar(doc: Doc) {
-        if (doc.myImportDocs === undefined) {
-            const newImportButton: Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", system: true });
-            doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
-                title: "My Imports", _forceActive: true, buttonMenu: true, buttonMenuDoc: newImportButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
-                childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
-                dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
-            }));
-        }
-    }
-
-    // Search sidebar is where searches within the document are performed
-    static setupSearchSidebar(doc: Doc) {
-        if (doc.mySearchPanel === undefined) {
-            doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
-                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true,
-                childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true
-            })) as any as Doc;
-        }
+    /// Import option on the left side button panel 
+    static setupImportSidebar(doc: Doc, field:string) {  
+        const reqdOpts:DocumentOptions = {
+            title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _showTitle: "title", 
+            _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
+            childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
+            dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
+        };
+        const myImports = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.StackingDocument([], reqdOpts));
+    
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
+            _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
+            buttonText: "Import", icon: "upload", system: true };
+        const reqdBtnScripts = { onClick: "importDocument()" };
+        const newImportBtn = this.AssignOpts(DocCast(myImports.buttonMenuDoc), reqdBtnOpts) ?? (myImports.buttonMenuDoc = Docs.Create.FontIconDocument(reqdBtnOpts));
+        this.AssignScripts(newImportBtn, reqdBtnScripts);
     }
 
     static setupClickEditorTemplates(doc: Doc) {
@@ -1033,8 +951,8 @@ export class CurrentUserUtils {
             }, { fireImmediately: true });
         // Document properties on load
         doc.system = true;
-        doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
         doc.title = Doc.CurrentUserEmail;
+        doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
         doc._raiseWhenDragged = true;
         doc._showLabel = true;
         doc._showMenuLabel = true;
@@ -1053,29 +971,22 @@ export class CurrentUserUtils {
         doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false);
         doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
         doc.noviceMode = BoolCast(doc.noviceMode, true);
-        doc.savedFilters = new List<Doc>();
+        !doc.savedFilters && (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
         doc.myPublishedDocs = doc.myPublishedDocs ?? new List<Doc>();
-        doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true });
+        doc.myHeaderBar = doc.myHeaderBar?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        await this.setupLinkDocs(doc, linkDatabaseId);
+        await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
-        this.setupImportSidebar(doc); // sets up the import sidebar
-        this.setupSearchSidebar(doc); // sets up the search sidebar
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
-        this.setupOverlays(doc);  // documents in overlay layer
-        this.setupContextMenuButtons(doc); // set up context menu buttons
+        this.setupOverlays(doc);  // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard
+        this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
         this.setupDockedButtons(doc);  // the bottom bar of font icons
-        await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
-        await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId);
+        this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
         if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument();
 
-        setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered
-
-        // 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["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
-        doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
-
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
         if (doc.activeDashboard instanceof Doc) {
@@ -1227,7 +1138,7 @@ export class CurrentUserUtils {
 
     public static async snapshotDashboard(userDoc: Doc) {
         const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
-        Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
+        Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
         CurrentUserUtils.openDashboard(userDoc, copy);
     }
 
@@ -1277,15 +1188,24 @@ export class CurrentUserUtils {
         return tbox;
     }
 
-    public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); }
-    public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); }
-    public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }
-    public static get MyHeaderBarDoc() { return Cast(Doc.UserDoc().myHeaderBarDoc, Doc, null); }
-    public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
-    public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
-    public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
-    public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); }
-    public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); }
+    public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); }
+    public static get MyDockedBtns() { return DocCast(Doc.UserDoc().myDockedBtns); }
+    public static get MySearcher() { return DocCast(Doc.UserDoc().mySearcher); }
+    public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); }
+    public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); }
+    public static get MyTools() { return DocCast(Doc.UserDoc().myTools); }
+    public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); }
+    public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); }
+    public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); }
+    public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); }
+    public static get MyTrails() { return DocCast(Doc.UserDoc().myTrails); }
+    public static get MyImports() { return DocCast(Doc.UserDoc().myImports); }
+    public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
+    public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
+    public static get MyOverlayDocs() { return DocListCast(DocCast(Doc.UserDoc().myOverlayDocs)?.data); }
+    public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
+    public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
+    public static get EmptyPane() { return DocCast(Doc.UserDoc().emptyPane); }
     public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
     @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
 }
@@ -1298,7 +1218,7 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
         view && SelectionManager.SelectView(view, false);
     }
 });
-ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); }, "document containing all shared Docs");
+ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
 ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
 ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
@@ -1329,15 +1249,15 @@ ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colTy
         const parentDoc: Doc = Cast(selected.context, Doc, null);
         selected = parentDoc;
     }
-    if (selected && docType && selected.type === docType) return false;
-    else if (selected && colType && selected.viewType === colType) return false;
-    else if (selected && !colType && !docType) return false;
-    else return true;
+    if (selected && docType && selected.type === docType) return true;
+    else if (selected && colType && selected.viewType === colType) return true;
+    else if (selected && !colType && !docType) return true;
+    else return false;
 });
 ScriptingGlobals.add(function makeTopLevelFolder() {
     const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
     TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined };
-    return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder);
+    return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder);
 });
 ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
     if (readOnly) return;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index a82f99d5a..0435cd535 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -10,6 +10,7 @@ import { CollectionView } from '../views/collections/CollectionView';
 import { LightboxView } from '../views/LightboxView';
 import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView';
 import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
+import { CurrentUserUtils } from './CurrentUserUtils';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
 
@@ -296,7 +297,7 @@ export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
         Doc.linkFollowHighlight(dv?.props.Document, false);
     }
     else {
-        const context = doc.context !== Doc.UserDoc().myFilesystem && Cast(doc.context, Doc, null);
+        const context = doc.context !== CurrentUserUtils.MyFilesystem && Cast(doc.context, Doc, null);
         const showDoc = context || doc;
         const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc;
 
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 223b0268c..3791bec73 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -223,7 +223,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
         paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
     }
     const paramString = paramList.join(", ");
-    const body = addReturn ? `return ${script};` : script;
+    const body = addReturn && !script.startsWith("{ return") ? `return ${script};` : script;
     const reqTypes = requiredType ? `: ${requiredType}` : '';
     const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
     host.writeFile("file.ts", funcScript);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 8486b3d62..be5927497 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -141,7 +141,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 const toRemove = value.filter(v => docs.includes(v));
 
                 if (toRemove.length !== 0) {
-                    const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc;
+                    const recent = CurrentUserUtils.MyRecentlyClosed;
                     toRemove.forEach(doc => {
                         leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
                         Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 767efdbc2..16d7c9a23 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -235,7 +235,7 @@ export class KeyManager {
                 if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) {
                     SelectionManager.Views()[0].ComponentView?.search?.("", false, false);
                 } else {
-                    const searchBtn = Doc.UserDoc().searchBtn as Doc;
+                    const searchBtn = CurrentUserUtils.MySearcher;
                     if (searchBtn) {
                         MainView.Instance.selectMenu(searchBtn);
                     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5fd76c388..7e3916b3f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -10,7 +10,7 @@ import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { PromiseValue, StrCast } from '../../fields/Types';
+import { DocCast, PromiseValue, StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
@@ -79,7 +79,7 @@ export class MainView extends React.Component {
     @observable private _dashUIWidth: number = 0;   // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
     @observable private _dashUIHeight: number = 0;  // height of entire main dashboard region including top menu buttons
     @observable private _panelContent: string = "none";
-    @observable private _sidebarContent: any = this.userDoc?.sidebar;
+    @observable private _sidebarContent: any = CurrentUserUtils.MyLeftSidebarPanel;
     @observable private _leftMenuFlyoutWidth: number = 0;
 
     @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
@@ -92,7 +92,7 @@ export class MainView extends React.Component {
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
     @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
-    @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; }
+    @computed private get headerBarDoc() { return CurrentUserUtils.MyHeaderBar; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
 
     headerBarDocWidth = () => this.mainDocViewWidth();
@@ -249,24 +249,8 @@ export class MainView extends React.Component {
 
     @action
     createNewFolder = async () => {
-        if (!await this.userDoc.myFilesystem) {
-            this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
-            const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
-            const newFolderButton: Doc = Docs.Create.FontIconDocument({
-                onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder",
-                btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true
-            });
-            this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], {
-                title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
-                explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
-            }));
-        }
         const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
-        Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
+        Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder);
     }
 
     @observable _exploreMode = false;
@@ -397,7 +381,7 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -423,7 +407,7 @@ export class MainView extends React.Component {
     @computed get leftMenuPanel() {
         return <div key="menu" className="mainView-leftMenuPanel">
             <DocumentView
-                Document={Doc.UserDoc().menuStack as Doc}
+                Document={CurrentUserUtils.MyLeftSidebarMenu}
                 DataDoc={undefined}
                 addDocument={undefined}
                 addDocTab={this.addDocTabFunc}
@@ -531,9 +515,9 @@ export class MainView extends React.Component {
         this._leftMenuFlyoutWidth = 0;
     });
 
-    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
+    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(CurrentUserUtils.MyDockedBtns, "data", doc), true);
     moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
-    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
+    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(CurrentUserUtils.MyDockedBtns, "data", doc), true);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
@@ -543,9 +527,9 @@ export class MainView extends React.Component {
 
     @computed get docButtons() {
         return !(this.userDoc.dockedBtns instanceof Doc) ? (null) :
-            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !this.userDoc.dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
+            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
                 <CollectionLinearView
-                    Document={this.userDoc.dockedBtns}
+                    Document={CurrentUserUtils.MyDockedBtns}
                     DataDoc={undefined}
                     fieldKey={"data"}
                     dropAction={"alias"}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index ab5dc74c9..bdb3bb857 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -158,7 +158,7 @@ export class OverlayView extends React.Component {
     }.bind(this));
 
     @computed get overlayDocs() {
-        return CurrentUserUtils.OverlayDocs?.map(d => {
+        return CurrentUserUtils.MyOverlayDocs?.map(d => {
             let offsetx = 0, offsety = 0;
             const dref = React.createRef<HTMLDivElement>();
             const onPointerMove = action((e: PointerEvent, down: number[]) => {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 16eb95cf4..2cf334ae1 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -931,7 +931,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * If it doesn't exist, it creates it.
      */
     checkFilterDoc() {
-        if (!this.selectedDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.selectedDoc);
+        if (!this.selectedDoc.currentFilter) this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
     }
 
     /**
@@ -944,8 +944,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         this.selectedDoc._docRangeFilters = new List<string>();
         (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
         (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-        this.selectedDoc.currentFilter = undefined;
-        CurrentUserUtils.setupFilterDocs(this.selectedDoc);
+        this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
     }
 
     /**
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index d2687df17..1ebbe08ef 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -416,7 +416,7 @@ export class CollectionDockingView extends CollectionSubView() {
     }
 
     tabDestroyed = (tab: any) => {
-        Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
+        Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, "data", tab.DashDoc);
         Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
         const dview = CollectionDockingView.Instance.props.Document;
         const fieldKey = CollectionDockingView.Instance.props.fieldKey;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 39f6466d6..1c25421f5 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -99,7 +99,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
     }
 
     @computed get contMenuButtons() {
-        const selDoc = Doc.UserDoc().contextMenuBtns;
+        const selDoc = CurrentUserUtils.MyContextMenuBtns;
         return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
             <CollectionLinearView
                 Document={selDoc}
@@ -268,8 +268,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                  self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
         immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
         initialize: (button: Doc) => {
-            button['target-docFilters'] = (Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) instanceof ObjectField ?
-                ObjectField.MakeCopy((Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) as any as ObjectField) : undefined;
+            button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) instanceof ObjectField ?
+                ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) as any as ObjectField) : undefined;
             button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined;
         },
     };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 52e99f26b..c07e44fcc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -985,7 +985,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.MyOverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -1031,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
             }
         }
-        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.OverlayDocs.includes(this.Document)) {
+        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.MyOverlayDocs.includes(this.Document)) {
             this._viewTransition = panTime;
             const scale = this.getLocalTransform().inverse().Scale;
             const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 9b1cb1601..c31267e87 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -207,7 +207,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     }}>
                     {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
                 </div>
-                {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.DockedBtns ? null :
+                {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null :
                     <span className="bottomPopup-background" style={{ pointerEvents: "all" }}
                         onPointerDown={e => e.stopPropagation()} >
                         <span className="bottomPopup-text" >
@@ -226,7 +226,7 @@ export class CollectionLinearView extends CollectionSubView() {
                             </span>
                         </Tooltip>
                     </span>}
-                {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.DockedBtns ? (null) :
+                {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? (null) :
                     <span className="bottomPopup-background">
                         <span className="bottomPopup-text">
                             Currently playing:
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 4b33ef8ae..a6f6bd35f 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -68,8 +68,8 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         className="linkPopup-searchBox searchBox-input" /> */}
 
                     <SearchBox
-                        Document={CurrentUserUtils.MySearchPanelDoc}
-                        DataDoc={CurrentUserUtils.MySearchPanelDoc}
+                        Document={CurrentUserUtils.MySearcher}
+                        DataDoc={CurrentUserUtils.MySearcher}
                         linkFrom={linkDoc}
                         linkSearch={true}
                         fieldKey="data"
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 60d16f044..e2b37d2b4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -327,7 +327,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._downX = touch.clientX;
             this._downY = touch.clientY;
             if (!e.nativeEvent.cancelBubble) {
-                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
+                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
                 this.removeEndListeners();
@@ -341,7 +341,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.cancelBubble && this.props.isDocumentActive?.()) {
             this.removeMoveListeners();
         }
-        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
@@ -563,7 +563,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 !this.Document.ignoreClick &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+                !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
                 e.stopPropagation();
                 // don't preventDefault anymore.  Goldenlayout, PDF text selection and RTF text selection all need it to go though
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault();
@@ -581,7 +581,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.cancelBubble) return;
         if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
                     document.removeEventListener("pointermove", this.onPointerMove);
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index e3708d7b9..5add09653 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -35,7 +35,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     constructor(props: Readonly<FieldViewProps>) {
         super(props);
         const targetDoc = FilterBox.targetDoc;
-        if (targetDoc && !targetDoc.currentFilter) CurrentUserUtils.setupFilterDocs(targetDoc);
+        if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = CurrentUserUtils.createFilterDoc();
     }
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); }
 
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 10974ca03..967158cbf 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -156,20 +156,22 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         runInAction(() => {
             this._annotationKeySuffix = () => this._urlHash + "-annotations";
+            const reqdFuncs:{[key:string]: string} = {};
             // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
-            this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`);
-            this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`);
+            reqdFuncs[this.fieldKey + "-annotations"] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`;
+            reqdFuncs[this.fieldKey + "-sidebar"] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`;
+            CurrentUserUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
         });
-        reaction(() => this.props.isSelected() || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
+        reaction(() => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
             async (selected) => {
                 if (selected) {
                     this._webPageHasBeenRendered = true;
-                } else if ((!this.props.isContentActive() || SnappingManager.GetIsDragging()) &&   // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
+                } else if ((!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) &&   // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
                     !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
                     LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
                     this.updateThumb();
                 }
-            }, { fireImmediately: this.props.isSelected() || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegree(this.props.Document) ? true : false) });
+            }, { fireImmediately: this.props.isSelected(true) || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegreeUnmemoized(this.props.Document) ? true : false) });
 
         this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight,
             autoHeight => {
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 97e6eddfe..f29dfe489 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
                         {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
                         {menuLabel}
-                        <FontIconBadge value={ScriptCast(this.Document.badgeValue,null)?.script.run({self:this.Document}).result} />
+                        <FontIconBadge value={Cast(this.Document.badgeValue,"string",null)} />
                     </div >
                 );
                 break;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 1dd6fef9b..52b03b0a5 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -623,7 +623,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     updateMinimize = async () => {
-        if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+        if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
             Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, "right");
@@ -868,7 +868,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
                 break;
             case "Escape":
-                if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
+                if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
                 else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; }
                 else this.layoutDoc.presStatus = "edit";
                 if (this._presTimer) clearTimeout(this._presTimer);
@@ -2511,7 +2511,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
         const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
         const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
-        return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ?
+        return CurrentUserUtils.MyOverlayDocs.includes(this.rootDoc) ?
             <div className="miniPres" onClick={e => e.stopPropagation()}>
                 <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                     <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
@@ -2536,7 +2536,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div >
             :
-            <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.OverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
+            <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7b72787d1..94be286f5 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -941,6 +941,9 @@ export namespace Doc {
     export function isBrushedHighlightedDegree(doc: Doc) {
         return Doc.IsHighlighted(doc) ? DocBrushStatus.highlighted : Doc.IsBrushedDegree(doc);
     }
+    export function isBrushedHighlightedDegreeUnmemoized(doc: Doc) {
+        return Doc.IsHighlighted(doc) ? DocBrushStatus.highlighted : Doc.IsBrushedDegreeUnmemoized(doc);
+    }
 
     export class DocBrush {
         BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
@@ -980,7 +983,7 @@ export namespace Doc {
     export function SearchQuery(): string { return manager._searchQuery; }
     export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); }
     export function UserDoc(): Doc { return manager._user_doc; }
-    export function SharingDoc(): Doc { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); }
+    export function SharingDoc(): Doc { return CurrentUserUtils.MySharedDocs; }
     export function LinkDBDoc(): Doc { return Cast(Doc.UserDoc().myLinkDatabase, Doc, null); }
     export function SetUserDoc(doc: Doc) { return (manager._user_doc = doc); }
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index d1e565774..37b9be31b 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -88,6 +88,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
             delete target.__fields[prop];
         } else {
             target.__fieldKeys && (target.__fieldKeys[prop] = true);
+            // if (target.__fields[prop] !== value) {
+            //     console.log("ASSIGN " + prop + " " + value);
+            // }
             target.__fields[prop] = value;
         }
         //if (typeof value === "object" && !(value instanceof ObjectField)) debugger;
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 78ec706d7..c5c6fb688 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -68,7 +68,7 @@ export class MobileInterface extends React.Component {
 
     constructor(props: Readonly<{}>) {
         super(props);
-        this._library = CurrentUserUtils.setupLibrary(Doc.UserDoc()); // to access documents in Dash Web
+        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc()); // to access documents in Dash Web
         MobileInterface.Instance = this;
     }
 
-- 
cgit v1.2.3-70-g09d2


From b292a055401af6236e0537cfad603016d77a535a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 13:40:07 -0400
Subject: a bunch of changes to make some userDoc() field accesses more
 typesafe by going through CurrentUserUtils.  Some normalization of
 naming/functionality in CurrentUserUtils

---
 src/client/apis/youtube/YoutubeBox.tsx             |   2 +-
 src/client/documents/Documents.ts                  |   6 +-
 src/client/util/CurrentUserUtils.ts                | 118 +++++++++++----------
 src/client/util/SettingsManager.tsx                |  16 +--
 src/client/util/SharingManager.tsx                 |   4 +-
 src/client/views/DashboardView.tsx                 |   4 +-
 src/client/views/DocComponent.tsx                  |   6 +-
 src/client/views/DocumentButtonBar.tsx             |   2 +-
 src/client/views/GestureOverlay.tsx                |  12 +--
 src/client/views/GlobalKeyHandler.ts               |   8 +-
 src/client/views/InkStrokeProperties.ts            |   4 +-
 src/client/views/InkTranscription.tsx              |   2 +-
 src/client/views/InkingStroke.tsx                  |   2 +-
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/OverlayView.tsx                   |   8 +-
 src/client/views/PropertiesButtons.tsx             |   4 +-
 src/client/views/PropertiesView.tsx                |  86 ++++++++-------
 src/client/views/TemplateMenu.tsx                  |  11 +-
 .../views/collections/CollectionDockingView.tsx    |  22 ++--
 src/client/views/collections/CollectionMenu.tsx    |  35 +++---
 .../views/collections/CollectionStackingView.tsx   |   2 +-
 .../CollectionStackingViewFieldColumn.tsx          |   4 +-
 .../views/collections/CollectionTreeView.tsx       |   8 +-
 src/client/views/collections/CollectionView.tsx    |  14 +--
 src/client/views/collections/TabDocView.tsx        |   2 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  30 +++---
 .../collections/collectionFreeForm/MarqueeView.tsx |   6 +-
 .../collectionSchema/CollectionSchemaHeaders.tsx   |   2 +-
 src/client/views/nodes/AudioBox.tsx                |   5 +-
 src/client/views/nodes/ColorBox.tsx                |   3 +-
 src/client/views/nodes/DocumentView.tsx            |  40 +++----
 src/client/views/nodes/FilterBox.tsx               |  47 ++++----
 src/client/views/nodes/ImageBox.tsx                |   4 +-
 src/client/views/nodes/LabelBox.tsx                |   2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   4 +-
 src/client/views/nodes/VideoBox.tsx                |   6 +-
 src/client/views/nodes/WebBox.tsx                  |   6 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  35 +++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |  12 +--
 .../views/nodes/formattedText/RichTextMenu.tsx     |   1 -
 src/client/views/nodes/trails/PresBox.tsx          |  30 +++---
 src/client/views/nodes/trails/PresElementBox.tsx   |  16 +--
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 src/client/views/topbar/TopBar.tsx                 |  10 +-
 src/client/views/webcam/DashWebRTCVideo.tsx        |   2 +-
 src/fields/Doc.ts                                  |  18 ++--
 src/fields/util.ts                                 |   2 +-
 src/mobile/MobileInterface.tsx                     |  20 ++--
 49 files changed, 360 insertions(+), 335 deletions(-)

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

diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index c5ff2db68..e14dc60b4 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -351,7 +351,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
         return (
             <>
                 <div className={classname}  >
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 927996af2..f96bffb3c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -687,7 +687,7 @@ export namespace Docs {
             ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
             !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
-                !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc);
+                !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, dataDoc);
 
             updateCachedAcls(dataDoc);
             updateCachedAcls(viewDoc);
@@ -802,7 +802,7 @@ export namespace Docs {
             I.rotation = 0;
             I.data = new InkField(points);
             I.creationDate = new DateField;
-            I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            I["acl-Public"] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             I["acl-Override"] = "None";
             I.links = ComputedField.MakeFunction("links(self)");
             I[Initializing] = false;
@@ -1204,7 +1204,7 @@ export namespace DocUtils {
             created = Docs.Create.RecordingDocument((field).url.href, resolved);
             layout = RecordingBox.LayoutString;
         } else if (field instanceof InkField) {
-            created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
+            created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
             layout = InkingStroke.LayoutString;
         } else if (field instanceof List && field[0] instanceof Doc) {
             created = Docs.Create.StackingDocument(DocListCast(field), resolved);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index afb1442bf..7a38886fe 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -74,16 +74,16 @@ export class CurrentUserUtils {
     @observable public static headerBarHeight: number = 0;
     @observable public static searchPanelWidth: number = 0;
 
-    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, functions?:{[key:string]: string}) {
+    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, funcs?:{[key:string]: string}) {
         scripts && Object.keys(scripts).map(key => {
             if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
                 doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name,  value:"any", scriptContext: "any" }, {"_readOnly_": true});
             }
         }); 
-        functions && Object.keys(functions).map(key => {
+        funcs && Object.keys(funcs).map(key => {
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            if (ScriptCast(cfield)?.script.originalScript !== functions[key] && functions[key]) {
-                doc[key] = ComputedField.MakeFunction(functions[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
+            if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) {
+                doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
             }
         });
         return doc;
@@ -180,14 +180,14 @@ export class CurrentUserUtils {
     }
 
     /// Initializes collection of templates for notes and click functions
-    static setupDocTemplates(doc: Doc) {
+    static setupDocTemplates(doc: Doc, field="myTemplates") {
         const templates = [
             CurrentUserUtils.setupNoteTemplates(doc),
             CurrentUserUtils.setupClickEditorTemplates(doc)
         ];
         const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
         const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
-        return this.AssignScripts(this.AssignOpts(DocCast(doc.templateDocs), reqdOpts, templates) ?? (doc.templateDocs = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts);
+        return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, templates) ?? (doc[field] = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
@@ -329,7 +329,7 @@ export class CurrentUserUtils {
     static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc {
         const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
-            const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["backgroundColor"]).omit,
+            const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit,
                 _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, _dropAction: "alias",
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
@@ -562,6 +562,7 @@ export class CurrentUserUtils {
         if (Cast(myDashboards.childContextMenuFilters, listSpec(ScriptField), null)?.length !== childContextMenuFilters.length) {
             myDashboards.childContextMenuFilters = new List<ScriptField>(childContextMenuFilters.map(script => !script ? script: ScriptField.MakeFunction(script)!));
         }
+        return myDashboards;
     }
 
     /// initializes the left sidebar Trails pane
@@ -728,13 +729,13 @@ export class CurrentUserUtils {
 
     static inkTools():Button[] {
         return [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveInkTool("pen", _readOnly_);}' }},
-            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveInkTool("write", _readOnly_);}'} },
-            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveInkTool("eraser", _readOnly_);}' }},
-            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveInkTool("highlighter")'} },
-            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveInkTool("circle", _readOnly_);}'} },
-            // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveInkTool("line", _readOnly_);}' }},
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveTool("pen", _readOnly_);}' }},
+            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveTool("write", _readOnly_);}'} },
+            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveTool("eraser", _readOnly_);}' }},
+            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} },
+            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveTool("circle", _readOnly_);}'} },
+            // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveTool("square")' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveTool("line", _readOnly_);}' }},
             { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: {script: "{ return setFillColor(value, _readOnly_);}"} },
             { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'} },
             { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} },
@@ -941,41 +942,39 @@ export class CurrentUserUtils {
     }
 
     static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
-        if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument();
-        await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
-        reaction(() => DateCast((doc.globalGroupDatabase as Doc)["data-lastModified"]),
+        doc.globalGroupDatabase ?? (doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument());
+        await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
+        reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]),
             async () => {
-                const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
+                const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
                 SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
             }, { fireImmediately: true });
         // Document properties on load
-        doc.system = true;
-        doc.title = Doc.CurrentUserEmail;
-        doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
-        doc._raiseWhenDragged = true;
-        doc._showLabel = true;
-        doc._showMenuLabel = true;
-        doc.textAlign = StrCast(doc.textAlign, "left");
-        doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
-        doc.activeInkWidth = Number(StrCast(doc.activeInkWidth, "1"));
-        doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
-        doc.activeFillColor = StrCast(doc.activeFillColor, "");
-        doc.activeArrowStart = StrCast(doc.activeArrowStart, "");
-        doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "");
-        doc.activeDash = StrCast(doc.activeDash, "0");
-        doc.fontSize = StrCast(doc.fontSize, "12px");
-        doc.fontFamily = StrCast(doc.fontFamily, "Arial");
-        doc.fontColor = StrCast(doc.fontColor, "black");
-        doc.fontHighlight = StrCast(doc.fontHighlight, "");
-        doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false);
-        doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
-        doc.noviceMode = BoolCast(doc.noviceMode, true);
-        !doc.savedFilters && (doc.savedFilters = new List<Doc>());
+        doc.system ?? (doc.system = true);
+        doc.title ?? (doc.title = Doc.CurrentUserEmail);
+        Doc.noviceMode ?? (Doc.noviceMode = true);
+        doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true);
+        doc._showLabel ?? (doc._showLabel = true);
+        doc._showMenuLabel ?? (doc._showMenuLabel = true);
+        doc.textAlign ?? (doc.textAlign = "left");
+        doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");;
+        doc.activeInkWidth ?? (doc.activeInkWidth = 1);
+        doc.activeInkBezier ?? (doc.activeInkBezier = "0");
+        doc.activeFillColor ?? (doc.activeFillColor = "");
+        doc.activeArrowStart ?? (doc.activeArrowStart = "");
+        doc.activeArrowEnd ?? (doc.activeArrowEnd = "");
+        doc.activeDash ?? (doc.activeDash == "0");
+        doc.fontSize ?? (doc.fontSize = "12px");
+        doc.fontFamily ?? (doc.fontFamily = "Arial");
+        doc.fontColor ?? (doc.fontColor = "black");
+        doc.fontHighlight ?? (doc.fontHighlight = "");
+        doc.defaultAclPrivate ?? (doc.defaultAclPrivate = false);
+        doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myPublishedDocs = doc.myPublishedDocs ?? new List<Doc>();
-        doc.myHeaderBar = doc.myHeaderBar?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        doc.myPublishedDocs ?? (doc.myPublishedDocs = new List<Doc>());
+        doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
         await this.setupLinkDocs(doc, linkDatabaseId);
         await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
@@ -985,7 +984,7 @@ export class CurrentUserUtils {
         this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
-        if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument();
+        doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument());
 
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
@@ -1022,7 +1021,7 @@ export class CurrentUserUtils {
                     const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
                     Docs.newAccount &&(userDoc.activePage = "home");
                     const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
-                    (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
+                    (await DocListCastAsync(Doc.LinkDBDoc()?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
                         const a1 = await Cast(link?.anchor1, Doc, null);
                         const a2 = await Cast(link?.anchor2, Doc, null);
                     });
@@ -1097,7 +1096,7 @@ export class CurrentUserUtils {
                     }
                 }
             } else if (input.files && input.files.length !== 0) {
-                const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null);
+                const importDocs = CurrentUserUtils.MyImports;
                 const disposer = OverlayView.ShowSpinner();
                 DocListCastAsync(importDocs.data).then(async list => {
                     const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {});
@@ -1115,9 +1114,10 @@ export class CurrentUserUtils {
     }
 
     public static CaptureDashboardThumbnail() {
+        const activeDashboard = CurrentUserUtils.ActiveDashboard;
         const docView = CollectionDockingView.Instance.props.DocumentView?.();
         const content = docView?.ContentDiv;
-        if (docView && content) {
+        if (docView && content && activeDashboard) {
             const _width = Number(getComputedStyle(content).width.replace("px",""));
             const _height = Number(getComputedStyle(content).height.replace("px",""));
             return CollectionFreeFormView.UpdateIcon(
@@ -1130,20 +1130,22 @@ export class CurrentUserUtils {
                     const proto = Cast(img.proto, Doc, null)!;
                     proto["data-nativeWidth"] = _width;
                     proto["data-nativeHeight"] = _height;
-                    Doc.GetProto(CurrentUserUtils.ActiveDashboard).thumb = img;
+                    Doc.GetProto(activeDashboard).thumb = img;
                 });
         }
 
     }
 
     public static async snapshotDashboard(userDoc: Doc) {
-        const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
-        Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
-        CurrentUserUtils.openDashboard(userDoc, copy);
+        if (CurrentUserUtils.ActiveDashboard) {
+            const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
+            Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
+            CurrentUserUtils.openDashboard(userDoc, copy);
+        }
     }
 
     public static closeActiveDashboard = () => {
-          Doc.UserDoc().activeDashboard = undefined;
+        CurrentUserUtils.ActiveDashboard = undefined;
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
@@ -1195,6 +1197,8 @@ export class CurrentUserUtils {
     public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); }
     public static get MyTools() { return DocCast(Doc.UserDoc().myTools); }
     public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); }
+    public static get MyFileOrphans() { return DocCast(Doc.UserDoc().myFileOrphans); }
+    public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); }
     public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); }
     public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); }
     public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); }
@@ -1202,12 +1206,16 @@ export class CurrentUserUtils {
     public static get MyImports() { return DocCast(Doc.UserDoc().myImports); }
     public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
     public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
-    public static get MyOverlayDocs() { return DocListCast(DocCast(Doc.UserDoc().myOverlayDocs)?.data); }
+    public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); }
     public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
+    public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; }
     public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
+    public static set ActivePresentation(val) { Doc.UserDoc().activePresentation = val; }
+    public static get ActivePage() { return StrCast(Doc.UserDoc().activePage); }
+    public static set ActivePage(val) { Doc.UserDoc().activePage = val; }
     public static get EmptyPane() { return DocCast(Doc.UserDoc().emptyPane); }
-    public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
-    @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
+    public static set ActiveTool(tool: InkTool) { Doc.UserDoc().activeTool = tool; }
+    public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; }
 }
 
 ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
@@ -1219,7 +1227,7 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
     }
 });
 ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
-ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode");
+ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
 ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
 ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called");
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 6a26dfdc7..382274462 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -38,7 +38,7 @@ export class SettingsManager extends React.Component<{}> {
     @observable activeTab = "Accounts";
 
     @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
-    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard.colorScheme; }
+    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme; }
 
     constructor(props: {}) {
         super(props);
@@ -59,7 +59,7 @@ export class SettingsManager extends React.Component<{}> {
         }
     }
 
-    @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.UserDoc().noviceMode = (e.currentTarget as any)?.value === "Novice");
+    @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.noviceMode = (e.currentTarget as any)?.value === "Novice");
     @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value ? "title" : undefined);
     @undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
     @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
@@ -78,19 +78,21 @@ export class SettingsManager extends React.Component<{}> {
     @undoBatch
     @action
     changeColorScheme = action((e: React.ChangeEvent) => {
+        const activeDashboard= CurrentUserUtils.ActiveDashboard;
+        if (!activeDashboard) return;
         const scheme: ColorScheme = (e.currentTarget as any).value;
         switch (scheme) {
             case ColorScheme.Light:
-                CurrentUserUtils.ActiveDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+                activeDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" });
                 break;
             case ColorScheme.Dark:
-                CurrentUserUtils.ActiveDashboard.colorScheme = ColorScheme.Dark;
+                activeDashboard.colorScheme = ColorScheme.Dark;
                 addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" });
                 break;
             case ColorScheme.System: default:
                 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
-                    CurrentUserUtils.ActiveDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+                    activeDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 });
                 break;
         }
@@ -254,7 +256,7 @@ export class SettingsManager extends React.Component<{}> {
             <div className="tab-column">
                 <div className="tab-column-title">Modes</div>
                 <div className="tab-column-content">
-                    <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.UserDoc().noviceMode ? "Novice" : "Developer"}>
+                    <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.noviceMode ? "Novice" : "Developer"}>
                         <option key={"Novice"} value={"Novice"}> Novice </option>
                         <option key={"Developer"} value={"Developer"}> Developer</option>
                     </select>
@@ -270,7 +272,7 @@ export class SettingsManager extends React.Component<{}> {
                     <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
                     <div className="default-acl">
                         <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)}
-                            onChange={action(() => Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
+                            onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
                         <div className="acl-text">Default access private</div>
                     </div>
                 </div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index f439d4488..d77633b8d 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -369,7 +369,7 @@ export class SharingManager extends React.Component<{}> {
         const dropdownValues: string[] = Object.values(SharingPermissions);
         if (!uniform) dropdownValues.unshift("-multiple-");
         if (override) dropdownValues.unshift("None");
-        return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
+        return dropdownValues.filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
         (
             <option key={permission} value={permission}>
                 {permission}
@@ -680,7 +680,7 @@ export class SharingManager extends React.Component<{}> {
                         </div>
 
                         <div className="acl-container">
-                            {Doc.UserDoc().noviceMode ? (null) :
+                            {Doc.noviceMode ? (null) :
                                 <div className="layoutDoc-acls">
                                     <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
                                 </div>}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index efc1644fe..9a0f25fe3 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -34,8 +34,8 @@ export class DashboardView extends React.Component {
 
       clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
             if (e.detail === 2) {
-                  Doc.UserDoc().activeDashboard = dashboard;
-                  Doc.UserDoc().activePage = "dashboard";
+                  CurrentUserUtils.ActiveDashboard = dashboard;
+                  CurrentUserUtils.ActivePage = "dashboard";
             }
       }
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index be5927497..169bd3873 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -65,7 +65,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
 
         isContentActive = (outsideReaction?: boolean) => (
             this.props.isContentActive?.() === false ? false :
-                (CurrentUserUtils.SelectedTool !== InkTool.None ||
+                (CurrentUserUtils.ActiveTool !== InkTool.None ||
                     (this.props.isContentActive?.() || this.props.Document.forceActive ||
                         this.props.isSelected(outsideReaction) ||
                         this.props.rootSelected(outsideReaction)) ? true : undefined))
@@ -200,7 +200,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                     if (effectiveAcl === AclAugment) {
                         added.map(doc => {
-                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
+                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
@@ -213,7 +213,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
 
-                            inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
+                            CurrentUserUtils.ActiveDashboard && inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                         });
                         const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                         if (annoDocs instanceof List) annoDocs.push(...added);
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 0bbe473d7..9b8f7238d 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -347,7 +347,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.menuButton}
             </div>
-            {/* {Doc.UserDoc().noviceMode ? (null) : <div className="documentButtonBar-button">
+            {/* {Doc.noviceMode ? (null) : <div className="documentButtonBar-button">
                 {this.moreButton}
             </div>} */}
         </div>;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 6afe64868..e960f5cca 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -129,7 +129,7 @@ export class GestureOverlay extends Touchable {
             // and this seems to be the only way of differentiating pen and touch on touch events
             if (pt.radiusX > 1 && pt.radiusY > 1) {
                 InkTranscription.Instance.createInkGroup();
-                Doc.UserDoc().activeInkTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
         }
@@ -498,18 +498,18 @@ export class GestureOverlay extends Touchable {
             setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
                 if (doubleTap) {
                     InkTranscription.Instance.createInkGroup();
-                    CurrentUserUtils.SelectedTool = InkTool.None;
+                    CurrentUserUtils.ActiveTool = InkTool.None;
                     return;
                 }
             }));
         }
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                CurrentUserUtils.SelectedTool = InkTool.Write;
+                CurrentUserUtils.ActiveTool = InkTool.Write;
             }
             this._points.push({ X: e.clientX, Y: e.clientY });
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
-            // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+            // if (CurrentUserUtils.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
         }
     }
 
@@ -606,7 +606,7 @@ export class GestureOverlay extends Touchable {
                 this._points = [];
                 if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
                     this.InkShape = "";
-                    Doc.UserDoc().activeInkTool = InkTool.None;
+                    CurrentUserUtils.ActiveTool = InkTool.None;
                 }
             }
             // if we're not drawing in a toolglass try to recognize as gesture
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 16d7c9a23..f5122df3f 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -57,7 +57,7 @@ export class KeyManager {
 
     public handle = action((e: KeyboardEvent) => {
         if (e.key?.toLowerCase() === "shift") DocumentDecorations.Instance.AddToSelection = true;
-        //if (!Doc.UserDoc().noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
+        //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
         const keyname = e.key && e.key.toLowerCase();
         this.handleGreedy(keyname);
 
@@ -112,7 +112,7 @@ export class KeyManager {
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
                 InkStrokeProperties.Instance._controlButton = false;
-                CurrentUserUtils.SelectedTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 DragManager.CompleteWindowDrag?.(true);
                 var doDeselect = true;
                 if (SnappingManager.GetIsDragging()) {
@@ -241,9 +241,9 @@ export class KeyManager {
                     }
                 }
                 break;
-            case "e": CurrentUserUtils.SelectedTool = InkTool.Eraser;
+            case "e": CurrentUserUtils.ActiveTool = InkTool.Eraser;
                 break;
-            case "p": CurrentUserUtils.SelectedTool = InkTool.Pen;
+            case "p": CurrentUserUtils.ActiveTool = InkTool.Pen;
                 break;
             case "o":
                 const target = SelectionManager.Docs().lastElement();
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 0449da483..471ad09e9 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -25,8 +25,8 @@ export class InkStrokeProperties {
 
     constructor() {
         InkStrokeProperties._Instance = this;
-        reaction(() => this._controlButton, button => button && (CurrentUserUtils.SelectedTool = InkTool.None));
-        reaction(() => CurrentUserUtils.SelectedTool, tool => (tool !== InkTool.None) && (this._controlButton = false));
+        reaction(() => this._controlButton, button => button && (CurrentUserUtils.ActiveTool = InkTool.None));
+        reaction(() => CurrentUserUtils.ActiveTool, tool => (tool !== InkTool.None) && (this._controlButton = false));
     }
 
     /**
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 8ab54918c..487bbcd00 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -254,7 +254,7 @@ export class InkTranscription extends React.Component {
  */
 createInkGroup() {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index aa5a815ac..35d89d2b1 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -345,7 +345,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             onClick: (e: React.MouseEvent) => this._handledClick && e.stopPropagation(),
             onContextMenu: () => {
                 const cm = ContextMenu.Instance;
-                !Doc.UserDoc().noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
+                !Doc.noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
                 cm?.addItem({ description: "Toggle Mask", event: () => InkingStroke.toggleMask(this.rootDoc), icon: "paint-brush" });
                 cm?.addItem({ description: "Edit Points", event: action(() => InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton), icon: "paint-brush" });
             }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7e3916b3f..791c64630 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -381,7 +381,7 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === CurrentUserUtils.MyDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -656,7 +656,7 @@ export class MainView extends React.Component {
                             <GestureOverlay> {this.mainDashboardArea}   </GestureOverlay>
                         </>;
                     case "home":  return <DashboardView/>;
-                } })(StrCast(Doc.UserDoc().activePage))
+                } })(CurrentUserUtils.ActivePage)
             }
             
             <PreviewCursor />
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index bdb3bb857..598fff29a 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import * as React from "react";
 import ReactLoading from 'react-loading';
-import { Doc, WidthSym, HeightSym } from "../../fields/Doc";
+import { Doc, WidthSym, HeightSym, DocListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast } from "../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../Utils";
@@ -149,7 +149,7 @@ export class OverlayView extends React.Component {
     }
 
     removeOverlayDoc = (doc: Doc | Doc[]) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc));
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc));
         return true;
     }
     
@@ -158,7 +158,7 @@ export class OverlayView extends React.Component {
     }.bind(this));
 
     @computed get overlayDocs() {
-        return CurrentUserUtils.MyOverlayDocs?.map(d => {
+        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).map(d => {
             let offsetx = 0, offsety = 0;
             const dref = React.createRef<HTMLDivElement>();
             const onPointerMove = action((e: PointerEvent, down: number[]) => {
@@ -172,7 +172,7 @@ export class OverlayView extends React.Component {
                     dragData.dropAction = "move";
                     dragData.removeDocument = (doc: Doc | Doc[]) => {
                         const docs = (doc instanceof Doc) ? [doc] : doc;
-                        docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocs, Doc, null), "data", d));
+                        docs.forEach(d => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, d));
                         return true;
                     };
                     dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 74055f057..9c6d9a108 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -200,7 +200,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     {list}
                 </div>
             </div>
-            {Doc.UserDoc().noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
+            {Doc.noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
         </div>;
     }
     @computed
@@ -229,7 +229,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isTree = this.selectedDoc?._viewType === CollectionViewType.Tree;
         const isTabView = this.selectedTabView;
         const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => <div className="propertiesButtons-button" style={style}> {ele} </div>;
-        const isNovice = Doc.UserDoc().noviceMode;
+        const isNovice = Doc.noviceMode;
         return !this.selectedDoc ? (null) :
             <div className="propertiesButtons">
                 {toggle(this.titleButton)}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 2cf334ae1..90c86fa18 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -11,7 +11,7 @@ import { Id } from "../../fields/FieldSymbols";
 import { InkField } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { ComputedField } from "../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast, DocCast } from "../../fields/Types";
 import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from "../../fields/util";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils";
 import { DocumentType } from "../documents/DocumentTypes";
@@ -340,7 +340,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     changePermissions = (e: any, user: string) => {
-        const docs = SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+        const docs = SelectionManager.Views().length < 2 ? (this.selectedDoc ? [this.selectedDoc]:[]) : SelectionManager.Views().map(docView => docView.props.Document);
         SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
     }
 
@@ -355,7 +355,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             value={permission}
             onChange={e => this.changePermissions(e, user)}>
             {dropdownValues.filter(permission =>
-                !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
+                !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
                     <option key={permission} value={permission}>  {permission}  </option>)}
         </select>;
     }
@@ -419,7 +419,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // all selected docs
         const docs = SelectionManager.Views().length < 2 ?
-            [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc[DataSym]]
+            [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]]
             : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]);
 
         const target = docs[0];
@@ -907,7 +907,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             {!this.openSharing ? (null) :
                 <div className="propertiesView-sharing-content">
                     <div className="propertiesView-buttonContainer">
-                        {!Doc.UserDoc().noviceMode ? (<div className="propertiesView-acls-checkbox">
+                        {!Doc.noviceMode ? (<div className="propertiesView-acls-checkbox">
                             <Checkbox
                                 color="primary"
                                 onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)}
@@ -931,42 +931,46 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * If it doesn't exist, it creates it.
      */
     checkFilterDoc() {
-        if (!this.selectedDoc.currentFilter) this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (!this.selectedDoc?.currentFilter) this.selectedDoc!.currentFilter = CurrentUserUtils.createFilterDoc();
     }
 
     /**
      * Creates a new currentFilter for this.filterDoc, 
      */
     createNewFilterDoc = () => {
-        const currentDocFilters = this.selectedDoc._docFilters;
-        const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
-        this.selectedDoc._docFilters = new List<string>();
-        this.selectedDoc._docRangeFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
-        (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-        this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (this.selectedDoc) {
+            const currentDocFilters = this.selectedDoc._docFilters;
+            const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
+            this.selectedDoc._docFilters = new List<string>();
+            this.selectedDoc._docRangeFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+            (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+            this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        }
     }
 
     /**
      * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter
      */
     updateFilterDoc = (doc: Doc) => {
-        if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
-        const savedDocFilters = doc._docFiltersList;
-        const currentDocFilters = this.selectedDoc._docFilters;
-        this.selectedDoc._docFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
-        this.selectedDoc.currentFilter = doc;
-        doc._docFiltersList = new List<string>();
-        this.selectedDoc._docFilters = savedDocFilters;
-
-        const savedDocRangeFilters = doc._docRangeFiltersList;
-        const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
-        this.selectedDoc._docRangeFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-        this.selectedDoc.currentFilter = doc;
-        doc._docRangeFiltersList = new List<string>();
-        this.selectedDoc._docRangeFilters = savedDocRangeFilters;
+        if (this.selectedDoc) {
+            if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
+            const savedDocFilters = doc._docFiltersList;
+            const currentDocFilters = this.selectedDoc._docFilters;
+            this.selectedDoc._docFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+            this.selectedDoc.currentFilter = doc;
+            doc._docFiltersList = new List<string>();
+            this.selectedDoc._docFilters = savedDocFilters;
+
+            const savedDocRangeFilters = doc._docRangeFiltersList;
+            const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
+            this.selectedDoc._docRangeFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+            this.selectedDoc.currentFilter = doc;
+            doc._docRangeFiltersList = new List<string>();
+            this.selectedDoc._docRangeFilters = savedDocRangeFilters;
+        }
     }
 
     @computed get filtersSubMenu() {
@@ -1059,13 +1063,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
                 </div>
             </div>
-            {!Doc.UserDoc().noviceMode && this.openFields ? <div className="propertiesView-fields-checkbox">
+            {!Doc.noviceMode && this.openFields ? <div className="propertiesView-fields-checkbox">
                 {this.fieldsCheckbox}
                 <div className="propertiesView-fields-checkbox-text">Layout</div>
             </div> : null}
             {!this.openFields ? (null) :
                 <div className="propertiesView-fields-content">
-                    {Doc.UserDoc().noviceMode ? this.noviceFields : this.expandedField}
+                    {Doc.noviceMode ? this.noviceFields : this.expandedField}
                 </div>}
         </div>;
     }
@@ -1122,7 +1126,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     handleDescriptionChange = action((value: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.description = value;
             this.description = value;
             return true;
@@ -1131,7 +1135,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     handleLinkRelationshipChange = action((value: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.linkRelationship = value;
             this.relationship = value;
             return true;
@@ -1188,7 +1192,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     changeFollowBehavior = action((follow: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.followLinkLocation = follow;
             return true;
         }
@@ -1219,18 +1223,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     toggleAnchor = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove))));
     }
 
     toggleArrow = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow))));
     }
 
     toggleZoomToTarget1 = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor1).followLinkZoom = !DocCast(this.selectedDoc.anchor1).followLinkZoom))));
     }
     toggleZoomToTarget2 = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor2).followLinkZoom = !DocCast(this.selectedDoc.anchor2).followLinkZoom))));
     }
 
     @computed
@@ -1238,7 +1242,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return <input
             autoComplete={"off"}
             id="link_relationship_input"
-            value={StrCast(this.selectedDoc.linkRelationship)}
+            value={StrCast(this.selectedDoc?.linkRelationship)}
             onKeyDown={this.onRelationshipKey}
             onBlur={this.onSelectOutRelationship}
             onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
@@ -1252,7 +1256,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return <input
             autoComplete={"off"}
             id="link_description_input"
-            value={StrCast(this.selectedDoc.description)}
+            value={StrCast(this.selectedDoc?.description)}
             onKeyDown={this.onDescriptionKey}
             onBlur={this.onSelectOutDesc}
             onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
@@ -1273,7 +1277,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     // }
 
     render() {
-        const isNovice = BoolCast(Doc.UserDoc().noviceMode);
+        const isNovice = Doc.noviceMode;
         if (!this.selectedDoc && !this.isPres) {
             return <div className="propertiesView" style={{ width: this.props.width }}>
                 <div className="propertiesView-title" style={{ width: this.props.width }}>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 636f7042f..689ee4fc1 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -15,6 +15,7 @@ import { DocumentView } from "./nodes/DocumentView";
 import { DefaultStyleProvider } from "./StyleProvider";
 import './TemplateMenu.scss';
 import React = require("react");
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
 
 @observer
 class TemplateToggle extends React.Component<{ template: string, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
@@ -111,22 +112,22 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const firstDoc = this.props.docViews[0].props.Document;
         const templateName = StrCast(firstDoc.layoutKey, "layout").replace("layout_", "");
         const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
-        const addedTypes = Doc.UserDoc().noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
+        const addedTypes = Doc.noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
         const layout = Doc.Layout(firstDoc);
         const templateMenu: Array<JSX.Element> = [];
         this.props.templates?.forEach((checked, template) =>
             templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
         templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
-        !Doc.UserDoc().noviceMode && templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={!layout._chromeHidden} toggle={this.toggleChrome} />);
+        !Doc.noviceMode && templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={!layout._chromeHidden} toggle={this.toggleChrome} />);
         addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template));
         this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push(
             <OtherToggle key={template} name={template} checked={templateName === template} toggle={e => this.toggleLayout(e, template)} />));
         return <ul className="template-list" style={{ display: "block" }}>
-            {Doc.UserDoc().noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
+            {Doc.noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
             {templateMenu}
-            {Doc.UserDoc().noviceMode ? (null) : <CollectionTreeView
-                Document={Doc.UserDoc().templateDocs as Doc}
+            {Doc.noviceMode ? (null) : <CollectionTreeView
+                Document={CurrentUserUtils.MyTemplates}
                 CollectionView={undefined}
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1ebbe08ef..5f36a7a51 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -368,7 +368,7 @@ export class CollectionDockingView extends CollectionSubView() {
             }
         }
         if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
-            ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+            ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             e.stopPropagation();
         }
     }
@@ -433,8 +433,8 @@ export class CollectionDockingView extends CollectionSubView() {
 
     stackCreated = (stack: any) => {
         stack.header?.element.on('mousedown', (e: any) => {
-            if (e.target === stack.header?.element[0] && e.button === 2) {
-                const dashboard= CurrentUserUtils.ActiveDashboard;
+            const dashboard = CurrentUserUtils.ActiveDashboard;
+            if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
                 dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`,
@@ -462,12 +462,14 @@ export class CollectionDockingView extends CollectionSubView() {
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
                 const dashboard = CurrentUserUtils.ActiveDashboard;
-                dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
-                const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
-                });
-                this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
-                CollectionDockingView.AddSplit(docToAdd, "", stack);
+                if (dashboard) {
+                    dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
+                    const docToAdd = Docs.Create.FreeformDocument([], {
+                        _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
+                    });
+                    this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
+                    CollectionDockingView.AddSplit(docToAdd, "", stack);
+                }
             }));
     }
 
@@ -480,6 +482,6 @@ ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(
     "opens up document in a lightbox", "(doc: any)");
 ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
     "opens up document in tab on right side of the screen", "(doc: any)");
-ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); },
+ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); },
     "opens up document in screen overlay layer", "(doc: any)");
 ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 1c25421f5..9b1bb5b97 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -268,17 +268,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                  self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
         immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
         initialize: (button: Doc) => {
-            button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) instanceof ObjectField ?
-                ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) as any as ObjectField) : undefined;
-            button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined;
+            const activeDash = CurrentUserUtils.ActiveDashboard;
+            if (activeDash) {
+                button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ?
+                    ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined;
+                button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined;
+            }
         },
     };
 
-    @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
-    @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
-    @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
-    @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
-    @computed get _doc_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
+    @computed get _freeform_commands() { return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
+    @computed get _stacking_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
+    @computed get _masonry_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
+    @computed get _schema_commands() { return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
+    @computed get _doc_commands() { return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
     @computed get _tree_commands() { return undefined; }
     private get _buttonizableCommands() {
         switch (this.props.type) {
@@ -467,7 +470,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     @action
     startRecording = () => {
         const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
-        //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+        //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
         CollectionDockingView.AddSplit(doc, "right");
     }
 
@@ -691,8 +694,8 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
     @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; }
     @action primCreated() {
         if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
-            if (CurrentUserUtils.SelectedTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
-            CurrentUserUtils.SelectedTool = InkTool.None;
+            if (CurrentUserUtils.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
+            CurrentUserUtils.ActiveTool = InkTool.None;
             this._selectedPrimitive = this._shapePrims.length;
             SetActiveArrowStart("none");
             SetActiveArrowEnd("none");
@@ -734,11 +737,11 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
             if (this._selectedPrimitive !== i) {
                 this._selectedPrimitive = i;
                 if (this._title[i] === "highlighter") {
-                    CurrentUserUtils.SelectedTool = InkTool.Highlighter;
+                    CurrentUserUtils.ActiveTool = InkTool.Highlighter;
                     GestureOverlay.Instance.SavedColor = ActiveInkColor();
                     SetActiveInkColor("rgba(245, 230, 95, 0.75)");
                 } else {
-                    CurrentUserUtils.SelectedTool = InkTool.Pen;
+                    CurrentUserUtils.ActiveTool = InkTool.Pen;
                 }
                 SetActiveArrowStart(this._head[i]);
                 SetActiveArrowEnd(this._end[i]);
@@ -747,7 +750,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                 GestureOverlay.Instance.InkShape = this._shapePrims[i];
             } else {
                 this._selectedPrimitive = this._shapePrims.length;
-                CurrentUserUtils.SelectedTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 SetActiveArrowStart("");
                 SetActiveArrowEnd("");
                 GestureOverlay.Instance.InkShape = "";
@@ -840,7 +843,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                         {this.widthPicker}
                         {this.colorPicker}
                         {this.fillPicker}
-                        {Doc.UserDoc().noviceMode || this.props.isDoc ? (null) :
+                        {Doc.noviceMode || this.props.isDoc ? (null) :
                             <>
                                 <Tooltip key="back" title={<div className="dash-tooltip">Back Frame</div>} placement="bottom">
                                     <div className="backKeyframe" onClick={this.prevKeyframe}>
@@ -879,7 +882,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
         const val = value.toLowerCase();
         const docs = DocListCast(this.document[this.props.fieldKey]);
 
-        if (Doc.UserDoc().noviceMode) {
+        if (Doc.noviceMode) {
             if (docs instanceof Doc) {
                 const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
                     key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index dddae4a34..277fcd59c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -598,7 +598,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             <>
                 {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
                     {buttonMenu ? this.buttonMenu : null}
-                    {Doc.UserDoc().noviceMode && noviceExplainer ?
+                    {Doc.noviceMode && noviceExplainer ?
                         <div className="documentExplanation">
                             {noviceExplainer}
                         </div>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 7f96217b8..f3a798143 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -224,8 +224,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                     }
                 }, icon: "compress-arrows-alt"
             }));
-        !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" });
-        !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
+        !Doc.noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" });
+        !Doc.noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
         ContextMenu.Instance.setDefaultItem("::", (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = "";
             const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e809bfbce..ba72fb7b9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -67,7 +67,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; }
     @computed get outlineMode() { return this.doc.treeViewType === TreeViewType.outline; }
     @computed get fileSysMode() { return this.doc.treeViewType === TreeViewType.fileSystem; }
-    @computed get dashboardMode() { return this.doc === Doc.UserDoc().myDashboards; }
+    @computed get dashboardMode() { return this.doc === CurrentUserUtils.MyDashboards; }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
 
@@ -76,7 +76,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
     @observable _isAnyChildContentActive = false;
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
-    isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None ||
+    isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.ActiveTool !== InkTool.None ||
         (this.props.isContentActive?.() || this.props.Document.forceActive ||
             this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
             this.props.rootSelected(outsideReaction)) ? true : false)
@@ -164,7 +164,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             const layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: "Make tree state " + (this.doc.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
@@ -286,7 +286,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     @computed get noviceExplainer() {
-        return !Doc.UserDoc().noviceMode || !this.rootDoc.explainer ? (null) :
+        return !Doc.noviceMode || !this.rootDoc.explainer ? (null) :
             <div className="documentExplanation"> {this.rootDoc.explainer} </div>;
     }
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 965f0a352..4b5c5e3fb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -160,8 +160,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
         subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
         subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" });
-        !Doc.UserDoc().noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
-        !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
+        !Doc.noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
+        !Doc.noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
         subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
 
         if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
@@ -184,16 +184,16 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
             const options = cm.findByDescription("Options...");
             const optionItems = options && "subitems" in options ? options.subitems : [];
-            !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null;
+            !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null;
             if (this.rootDoc.childLayout instanceof Doc) {
                 optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" });
             }
             if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) {
                 optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" });
             }
-            !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
+            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
 
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 optionItems.push({
                     description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
                 });
@@ -210,7 +210,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
 
-            if (!Doc.UserDoc().noviceMode && !this.rootDoc.annotationOn) {
+            if (!Doc.noviceMode && !this.rootDoc.annotationOn) {
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
                 const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }];
@@ -230,7 +230,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
             }
 
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 const more = cm.findByDescription("More...");
                 const moreItems = more && "subitems" in more ? more.subitems : [];
                 moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) });
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 022b9fa24..70db121d1 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -277,7 +277,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
         });
         if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
-            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+            const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []);
             if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
             CollectionDockingView.AddSplit(curPres, "right");
             setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 59dc5671b..704b8989a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -114,7 +114,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey :
             this.props.treeView.dashboardMode ? this.fieldKey :
                 this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : // for displaying 
-                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
+                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
     }
 
     @computed get doc() { return this.props.document; }
@@ -560,8 +560,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : "";
         const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : "";
         const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases";
-        const fields = () => Doc.UserDoc().noviceMode ? "" : "fields";
-        const layout = (Doc.UserDoc().noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+        const fields = () => Doc.noviceMode ? "" : "fields";
+        const layout = (Doc.noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
         return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c07e44fcc..ffe146ae4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -458,7 +458,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
                 !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
                 !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                switch (CurrentUserUtils.SelectedTool) {
+                switch (CurrentUserUtils.ActiveTool) {
                     case InkTool.Highlighter:
                         break;
                     // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
@@ -497,7 +497,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.addMoveListeners();
                     this.removeEndListeners();
                     this.addEndListeners();
-                    if (CurrentUserUtils.SelectedTool === InkTool.None) {
+                    if (CurrentUserUtils.ActiveTool === InkTool.None) {
                         this._lastX = pt.pageX;
                         this._lastY = pt.pageY;
                         e.preventDefault();
@@ -519,9 +519,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
-                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
+                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
-                if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+                if (CurrentUserUtils.ActiveTool === InkTool.Write) {
                     this.unprocessedDocs.push(inkDoc);
                     CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
                 }
@@ -711,7 +711,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onPointerMove = (e: PointerEvent): void => {
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
             if (this.tryDragCluster(e, this._hitCluster)) {
@@ -834,7 +834,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
             if (myTouches[0]) {
-                if (CurrentUserUtils.SelectedTool === InkTool.None) {
+                if (CurrentUserUtils.ActiveTool === InkTool.None) {
                     if (this.tryDragCluster(e, this._hitCluster)) {
                         e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
                         e.preventDefault();
@@ -985,7 +985,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.MyOverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -1031,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
             }
         }
-        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.MyOverlayDocs.includes(this.Document)) {
+        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) {
             this._viewTransition = panTime;
             const scale = this.getLocalTransform().inverse().Scale;
             const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
@@ -1629,7 +1629,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearance = ContextMenu.Instance.findByDescription("Appearance...");
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
-        !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
+        !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
         appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
         //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
@@ -1640,27 +1640,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         // this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" });
 
-        !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
+        !Doc.noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
         !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-        !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
-        !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
 
         const options = ContextMenu.Instance.findByDescription("Options...");
         const optionItems = options && "subitems" in options ? options.subitems : [];
-        !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode &&
+        !this.props.isAnnotationOverlay && !Doc.noviceMode &&
             optionItems.push({ description: (this._showAnimTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this._showAnimTimeline = !this._showAnimTimeline), icon: "eye" });
         this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
         }
         !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
         const mores = ContextMenu.Instance.findByDescription("More...");
         const moreItems = mores && "subitems" in mores ? mores.subitems : [];
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
             moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) });
         }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 051da795f..b62020a04 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -330,7 +330,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     onClick = (e: React.MouseEvent): void => {
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            if (CurrentUserUtils.SelectedTool === InkTool.None) {
+            if (CurrentUserUtils.ActiveTool === InkTool.None) {
                 if (!(e.nativeEvent as any).marqueeHit) {
                     (e.nativeEvent as any).marqueeHit = true;
                     if (!this.props.trySelectCluster(e.shiftKey)) {
@@ -367,7 +367,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
             Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
-            !this.props.isAnnotationOverlay && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(doc));
+            !this.props.isAnnotationOverlay && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc));
             doc._panX = doc._panY = 0;
             return doc;
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
@@ -643,7 +643,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         return <div className="marqueeView"
             style={{
                 overflow: StrCast(this.props.Document._overflow),
-                cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
+                cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
             }}
 
             onDragOver={e => e.preventDefault()}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
index 0875c80b3..9653f2808 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
@@ -321,7 +321,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
         const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"];
         const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
         const showKeys = new Set<string>();
-        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.UserDoc().noviceMode ||
+        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.noviceMode ||
             whitelistKeys.includes(key)
             || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null);
         return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm));
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 669622455..8e24fce11 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -348,11 +348,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             Doc.GetProto(newDoc).recordingSource = this.dataDoc;
             Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
             Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-            const overlayDoc = Doc.UserDoc().myOverlayDocs as Doc;
-            if (DocListCast(overlayDoc[Doc.LayoutFieldKey(overlayDoc)]).includes(this.rootDoc)) {
+            if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc)) {
                 newDoc.x = this.rootDoc.x;
                 newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-                Doc.AddDocToList(overlayDoc, undefined, newDoc);
+                Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, newDoc);
             } else {
                 this.props.addDocument?.(newDoc);
             }
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index d975baf9b..6d05fca5e 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -22,7 +22,6 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     static switchColor(color: ColorState) {
-        // Doc.UserDoc().backgroundColor = Utils.colorString(color); // bcz: this can't go here ... needs a proper home in the settings panel
         SetActiveInkColor(color.hex);
 
         SelectionManager.Views().map(view => {
@@ -49,7 +48,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }} >
 
             <SketchPicker
-                onChange={c => CurrentUserUtils.SelectedTool === InkTool.None && ColorBox.switchColor(c)}
+                onChange={c => CurrentUserUtils.ActiveTool === InkTool.None && ColorBox.switchColor(c)}
                 color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())}
                 presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986',
                     '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e2b37d2b4..4d84a8ad2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -327,7 +327,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._downX = touch.clientX;
             this._downY = touch.clientY;
             if (!e.nativeEvent.cancelBubble) {
-                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
+                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
                 this.removeEndListeners();
@@ -341,7 +341,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.cancelBubble && this.props.isDocumentActive?.()) {
             this.removeMoveListeners();
         }
-        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
@@ -543,9 +543,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     });
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.SelectedTool === InkTool.Eraser) return;
+        if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.ActiveTool === InkTool.Eraser) return;
         // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
-        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) {
+        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) {
             if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
                 e.stopPropagation();
                 if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
@@ -563,7 +563,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 !this.Document.ignoreClick &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+                !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
                 e.stopPropagation();
                 // don't preventDefault anymore.  Goldenlayout, PDF text selection and RTF text selection all need it to go though
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault();
@@ -579,9 +579,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     onPointerMove = (e: PointerEvent): void => {
         if (e.cancelBubble) return;
-        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return;
+        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
                     document.removeEventListener("pointermove", this.onPointerMove);
@@ -697,7 +697,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
-        if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) {
+        if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) {
             e.preventDefault();
             e.stopPropagation();
             //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
@@ -747,8 +747,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
             const appearance = cm.findByDescription("UI Controls...");
             const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
-            !Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
-            !Doc.UserDoc().noviceMode && appearanceItems.push({
+            !Doc.noviceMode && templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
+            !Doc.noviceMode && appearanceItems.push({
                 description: "Add a Field", event: () => {
                     const alias = Doc.MakeAlias(this.rootDoc);
                     alias.layout = FormattedTextBox.LayoutString("newfield");
@@ -765,7 +765,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
 
             if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
-                !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
+                !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
 
@@ -778,8 +778,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 }
                 !zorders && cm.addItem({ description: "ZOrder...", noexpand: true, subitems: zorderItems, icon: "compass" });
 
-                !Doc.UserDoc().noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-                !Doc.UserDoc().noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
+                !Doc.noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
+                !Doc.noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
                 this.props.CollectionFreeFormDocumentView && onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
 
                 if (!this.Document.annotationOn) {
@@ -803,7 +803,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
 
             const funcs: ContextMenuProps[] = [];
-            if (!Doc.UserDoc().noviceMode && this.layoutDoc.onDragStart) {
+            if (!Doc.noviceMode && this.layoutDoc.onDragStart) {
                 funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
                 funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
                 funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
@@ -813,8 +813,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const more = cm.findByDescription("More...");
             const moreItems = more && "subitems" in more ? more.subitems : [];
             if (!Doc.IsSystem(this.rootDoc)) {
-                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.UserDoc().noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
-                if (!Doc.UserDoc().noviceMode) {
+                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
+                if (!Doc.noviceMode) {
                     moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
                     moreItems.push({ description: `${this.Document._chromeHidden ? "Show" : "Hide"} Chrome`, event: () => this.Document._chromeHidden = !this.Document._chromeHidden, icon: "project-diagram" });
 
@@ -835,9 +835,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const help = cm.findByDescription("Help...");
             const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
             helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
+            !Doc.noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+            !Doc.noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+            !Doc.noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
 
@@ -860,7 +860,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive() === false ? false : (
-            CurrentUserUtils.SelectedTool !== InkTool.None ||
+            CurrentUserUtils.ActiveTool !== InkTool.None ||
             SnappingManager.GetIsDragging() ||
             this.rootSelected() ||
             this.props.Document.forceActive ||
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 5add09653..17b57cb3b 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -8,7 +8,7 @@ import { List } from "../../../fields/List";
 import { RichTextField } from "../../../fields/RichTextField";
 import { listSpec } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast, DocCast } from "../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -84,7 +84,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return "data";
     }
     @computed static get targetDocChildren() {
-        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard.data);
+        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard?.data);
     }
 
     @observable _loaded = false;
@@ -115,7 +115,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
         const keys = new Set<string>(noviceFields);
         this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
-        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.UserDoc().noviceMode).sort();
+        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.noviceMode).sort();
     }
 
 
@@ -167,23 +167,25 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeFilterDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false;
     public removeFilter = (filterName: string) => {
         const targetDoc = FilterBox.targetDoc;
-        const filterDoc = targetDoc.currentFilter as Doc;
-        const attributes = DocListCast(filterDoc.data);
-        const found = attributes.findIndex(doc => doc.title === filterName);
-        if (found !== -1) {
-            (filterDoc.data as List<Doc>).splice(found, 1);
-            const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
-            if (docFilter) {
-                let index: number;
-                while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
-                    docFilter.splice(index, 1);
+        if (targetDoc) {
+            const filterDoc = targetDoc.currentFilter as Doc;
+            const attributes = DocListCast(filterDoc.data);
+            const found = attributes.findIndex(doc => doc.title === filterName);
+            if (found !== -1) {
+                (filterDoc.data as List<Doc>).splice(found, 1);
+                const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
+                if (docFilter) {
+                    let index: number;
+                    while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                        docFilter.splice(index, 1);
+                    }
                 }
-            }
-            const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
-            if (docRangeFilters) {
-                let index: number;
-                while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
-                    docRangeFilters.splice(index, 3);
+                const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
+                if (docRangeFilters) {
+                    let index: number;
+                    while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                        docRangeFilters.splice(index, 3);
+                    }
                 }
             }
         }
@@ -194,6 +196,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     facetClick = (facetHeader: string) => {
         const { targetDoc, targetDocChildren } = FilterBox;
+        if (!targetDoc) return;
         const found = this.activeAttributes.findIndex(doc => doc.title === facetHeader);
         if (found !== -1) {
             this.removeFilter(facetHeader);
@@ -267,7 +270,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     changeBool = (e: any) => {
-        (FilterBox.targetDoc.currentFilter as Doc).filterBoolean = e.currentTarget.value;
+        FilterBox.targetDoc && (DocCast(FilterBox.targetDoc.currentFilter).filterBoolean = e.currentTarget.value);
     }
 
     /**
@@ -322,7 +325,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * Changes the title of the filterDoc
      */
     onTitleValueChange = (val: string) => {
-        this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc.title}`;
+        this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc?.title}`;
         return true;
     }
 
@@ -373,7 +376,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             </div>
 
             <div className="filterBox-select-bool">
-                <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc.currentFilter as Doc)?.filterBoolean)}>
+                <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc?.currentFilter as Doc)?.filterBoolean)}>
                     <option value="AND" key="AND">AND</option>
                     <option value="OR" key="OR">OR</option>
                 </select>
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 8c27b3508..60eb48114 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -179,7 +179,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
             funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? "Dynamic Res" : "Full Res"}`, event: this.resolution, icon: "expand-arrows-alt" });
             funcs.push({ description: "Copy path", event: () => Utils.CopyText(this.choosePath(field.url)), icon: "expand-arrows-alt" });
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
 
                 const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers...");
@@ -357,7 +357,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index b0b050cea..b58a9affb 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -52,7 +52,7 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
     get paramsDoc() { return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; }
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        !Doc.UserDoc().noviceMode && funcs.push({
+        !Doc.noviceMode && funcs.push({
             description: "Clear Script Params", event: () => {
                 const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
                 params?.map(p => this.paramsDoc[p] = undefined);
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 5f4c17ee6..0b7264d79 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -471,7 +471,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
@@ -570,7 +570,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     moveDocument={this.moveDocument}
         //     addDocument={this.sidebarAddDocument}
         //     childPointerEvents={true}
-        //     pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
+        //     pointerEvents={CurrentUserUtils.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return <div className="mapBox" ref={this._ref}>
             {/*console.log(apiKey)*/}
             {/* <LoadScript
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b14a1f0f6..ddfbf50df 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -428,7 +428,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
       // renders the video and audio
       @computed get content() {
             const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-            const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
+            const interactive = CurrentUserUtils.ActiveTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
             const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
             return !field ? <div key="loading">Loading</div> :
                   <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply" }}>
@@ -484,7 +484,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                   this._disposers.youtubeReactionDisposer?.();
                   this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
                   this._disposers.youtubeReactionDisposer = reaction(
-                        () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
+                        () => CurrentUserUtils.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
                         (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
             };
             if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
@@ -707,7 +707,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
       // starts marquee selection
       marqueeDown = (e: React.PointerEvent) => {
-            if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+            if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.ActiveTool)) {
                   setupMoveUpEvents(this, e, action(e => {
                         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                         this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 967158cbf..d14af49ea 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -536,7 +536,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
         if (!cm.findByDescription("Options...")) {
-            !Doc.UserDoc().noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
+            !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
             funcs.push({
                 description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => {
                     this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts;
@@ -561,7 +561,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
@@ -678,7 +678,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
 
     @computed get content() {
-        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
+        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
         return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
             onKeyDown={e => e.stopPropagation()}
             style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index f29dfe489..3af6a3d51 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,6 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
+import { StringIterator } from 'lodash';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -70,7 +71,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); };
 
     specificContextMenu = (): void => {
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             const cm = ContextMenu.Instance;
             cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" });
             cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" });
@@ -269,7 +270,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         // Get items to place into the list
         const list = this.buttonList.map((value) => {
-            if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
+            if (Doc.noviceMode && !noviceList.includes(value)) {
                 return;
             }
             return <div className="list-item" key={`${value}`}
@@ -567,8 +568,8 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    if (checkResult && selected) {
-        if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
+    if (checkResult) {
+        if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE;
         return "transparent";
     }
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
@@ -710,7 +711,7 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 
 export function checkInksToGroup() {
     // console.log("getting here to inks group");
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
@@ -723,7 +724,7 @@ export function checkInksToGroup() {
 
 export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
@@ -785,38 +786,38 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
 
 
 /** INK
- *      setActiveInkTool
+ *      setActiveTool
  *      setStrokeWidth
  *      setStrokeColor
  **/
 
-ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) {
     InkTranscription.Instance?.createInkGroup();
     if (checkResult) {
-        return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
+        return ((CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
             Colors.MEDIUM_BLUE : "transparent";
     }
     if (["circle", "square", "line"].includes(tool)) {
         if (GestureOverlay.Instance.InkShape === tool) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             GestureOverlay.Instance.InkShape = InkTool.None;
         } else {
-            Doc.UserDoc().activeInkTool = InkTool.Pen;
+            CurrentUserUtils.ActiveTool = InkTool.Pen;
             GestureOverlay.Instance.InkShape = tool;
         }
     } else if (tool) { // pen or eraser
-        if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
-        } else if (tool == "write") {
+        if (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance.InkShape) {
+            CurrentUserUtils.ActiveTool = InkTool.None;
+        } else if (tool == InkTool.Write) {
             // console.log("write mode selected - create groupDoc here!", tool)
-            Doc.UserDoc().activeInkTool = tool;
+            CurrentUserUtils.ActiveTool = tool;
             GestureOverlay.Instance.InkShape = "";
         } else {
-            Doc.UserDoc().activeInkTool = tool;
+            CurrentUserUtils.ActiveTool = tool as any;
             GestureOverlay.Instance.InkShape = "";
         }
     } else {
-        Doc.UserDoc().activeInkTool = InkTool.None;
+        CurrentUserUtils.ActiveTool = InkTool.None;
     }
 });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 16a523b40..90199618b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -648,7 +648,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const highlighting: ContextMenuProps[] = [];
         const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others", "Bold Text"];
         const expertHighlighting = [...noviceHighlighting, "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"];
-        (Doc.UserDoc().noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
+        (Doc.noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
             highlighting.push({
                 description: (FormattedTextBox._globalHighlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
                     e.stopPropagation();
@@ -663,11 +663,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }));
 
         const uicontrols: ContextMenuProps[] = [];
-        !Doc.UserDoc().noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" });
+        !Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" });
         uicontrols.push({ description: !this.Document._noSidebar ? "Hide Sidebar Handle" : "Show Sidebar Handle", event: () => this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar, icon: "expand-arrows-alt" });
         uicontrols.push({ description: `${this.layoutDoc._showAudio ? "Hide" : "Show"} Dictation Icon`, event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
         uicontrols.push({ description: "Show Highlights...", noexpand: true, subitems: highlighting, icon: "hand-point-right" });
-        !Doc.UserDoc().noviceMode && uicontrols.push({
+        !Doc.noviceMode && uicontrols.push({
             description: "Broadcast Message", event: () => DocServer.GetRefField("rtfProto").then(proto =>
                 proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), icon: "expand-arrows-alt"
         });
@@ -677,7 +677,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
         // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
-        !Doc.UserDoc().noviceMode && appearanceItems.push({
+        !Doc.noviceMode && appearanceItems.push({
             description: "Make Default Layout", event: () => {
                 if (!this.layoutDoc.isTemplateDoc) {
                     const title = StrCast(this.rootDoc.title);
@@ -1636,7 +1636,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     noSidebar={true}
                     fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
-        return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.SelectedTool !== InkTool.None ? "-inking" : "")}
+        return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.ActiveTool !== InkTool.None ? "-inking" : "")}
             style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
             {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
         </div>;
@@ -1647,7 +1647,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const active = this.props.isContentActive();
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
-        const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
+        const interactive = (CurrentUserUtils.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9bc2e5628..98343a261 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -67,7 +67,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         runInAction(() => {
             RichTextMenu.Instance = this;
             this._canFade = false;
-            //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
             this.Pinned = true;
         });
     }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 52b03b0a5..0d2cffc2c 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -134,7 +134,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
     constructor(props: any) {
         super(props);
-        if (Doc.UserDoc().activePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
+        if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
         if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
             Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
                 title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
@@ -178,16 +178,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.layoutDoc._gridGap = 0;
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
-        DocListCastAsync((Doc.UserDoc().myTrails as Doc).data).then(pres =>
-            !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myTrails as Doc, "data", this.rootDoc));
+        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres =>
+            !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", this.rootDoc));
         this._disposers.selection = reaction(() => SelectionManager.Views(),
             views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation());
     }
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
-        if (pres) Doc.UserDoc().activePresentation = pres;
-        else Doc.UserDoc().activePresentation = this.rootDoc;
+        if (pres) CurrentUserUtils.ActivePresentation = pres;
+        else CurrentUserUtils.ActivePresentation = this.rootDoc;
         document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
         document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = true;
@@ -623,9 +623,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     updateMinimize = async () => {
-        if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
-            Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+            Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, "right");
         } else {
             this.layoutDoc.presStatus = PresStatus.Edit;
@@ -635,7 +635,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.rootDoc.y = pt[1] + 10;
             this.rootDoc._height = 30;
             this.rootDoc._width = 248;
-            Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+            Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
             this.props.removeDocument?.(this.layoutDoc);
         }
     }
@@ -732,7 +732,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
+    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
         (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     /**
@@ -868,7 +868,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
                 break;
             case "Escape":
-                if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
+                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) { this.updateMinimize(); }
                 else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; }
                 else this.layoutDoc.presStatus = "edit";
                 if (this._presTimer) clearTimeout(this._presTimer);
@@ -2247,7 +2247,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel";
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
+        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
         const activeColor = Colors.LIGHT_BLUE;
         const inactiveColor = Colors.WHITE;
         return (mode === CollectionViewType.Carousel3D) ? (null) : (
@@ -2305,7 +2305,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     value={mode}>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>Tree</option>
-                    {Doc.UserDoc().noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
+                    {Doc.noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
                 </select>}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
                     <span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}>
@@ -2508,10 +2508,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
+        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
         const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
         const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
-        return CurrentUserUtils.MyOverlayDocs.includes(this.rootDoc) ?
+        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ?
             <div className="miniPres" onClick={e => e.stopPropagation()}>
                 <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                     <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
@@ -2536,7 +2536,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div >
             :
-            <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
+            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }} >
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 50df00612..1a2f4b93f 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -309,7 +309,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
       @computed get recordingIsInOverlay() {
             let isInOverlay = false
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides === this.rootDoc) {
                         isInOverlay = true
                         return
@@ -319,9 +319,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
       }
 
       removeAllRecordingInOverlay = () => {
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides === this.rootDoc) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                        Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
                   }
             })
       }
@@ -339,7 +339,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.removeAllRecordingInOverlay()
             if (activeItem.recording) {
                   // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
 
             }
       }
@@ -348,15 +348,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
       @action
       startRecording = (activeItem: Doc) => {
             // Remove every recording that already exists in overlay view
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides !== null) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                        Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
                   }
             })
 
             if (activeItem.recording) {
                   // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
 
             } else {
                   // if we dont have any recording
@@ -376,7 +376,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                   // make recording box appear in the bottom right corner of the screen
                   recording.x = window.innerWidth - recording[WidthSym]() - 20;
                   recording.y = window.innerHeight - recording[HeightSym]() - 20;
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording);
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, recording);
             }
       }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0ded1bb3c..2869d4f2d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -340,7 +340,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
             this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
         }
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             this.props.select(false);
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0db3950a2..1de0c0b10 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -27,12 +27,12 @@ import "./TopBar.scss";
 export class TopBar extends React.Component {
       navigateToHome = () => {
             CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => {
-                Doc.UserDoc().activePage = "home";
+                CurrentUserUtils.ActivePage = "home";
                 CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
             });
       }
       render() {
-            const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null)
+            const activeDashboard = CurrentUserUtils.ActiveDashboard;
             return (
                   //TODO:glr Add support for light / dark mode
                   <div style={{ pointerEvents: "all", background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
@@ -44,11 +44,11 @@ export class TopBar extends React.Component {
                                     }}>{Doc.CurrentUserEmail}</div> : (null)}
                               </div>
                               <div className="topbar-center" >
-                                    <div className="topbar-title" onClick={() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
+                                    <div className="topbar-title" onClick={() => activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
                                           {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
                                     </div>
                                     <div className="topbar-button-icon" onClick={e => {
-                                        const dashView = DocumentManager.Instance.getDocumentView(activeDashboard);
+                                        const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard);
                                         ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
                                         ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
                                             const batch = UndoManager.StartBatch("snapshot");
@@ -69,7 +69,7 @@ export class TopBar extends React.Component {
                                     <div className="topbar-button-text" onClick={() => {SharingManager.Instance.open(undefined, activeDashboard)}}>
                                         {/* TODO: if this is my dashboard, display share
                                         if this is a shared dashboard, display "view original or view annotated" */}
-                                        { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original" }
+                                        { CurrentUserUtils.ActiveDashboard && (Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original") }
                                     </div>
                                     <div className="topbar-button-icon" onClick={() => window.open(
                                           "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index e0d328c89..6c0c9b301 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -71,7 +71,7 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
             </div >;
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
 
         return (
             <>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 94be286f5..74213652b 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -21,7 +21,7 @@ import { FieldId, RefField } from "./RefField";
 import { RichTextField } from "./RichTextField";
 import { listSpec } from "./Schema";
 import { ComputedField, ScriptField } from "./ScriptField";
-import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField";
 import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util";
 import JSZip = require("jszip");
@@ -233,6 +233,10 @@ export class Doc extends RefField {
     }
 
     private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
+    public static get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode); }
+    public static set noviceMode(val) { Doc.UserDoc().noviceMode = val; }
+    public static get defaultAclPrivate() { return Doc.UserDoc().defaultAclPrivate; }
+    public static set defaultAclPrivate(val) { Doc.UserDoc().defaultAclPrivate = val; }
     public static CurrentUserEmail: string = "";
     public static get CurrentUserEmailNormalized() { return normalizeEmail(Doc.CurrentUserEmail); }
     public async [HandleUpdate](diff: any) {
@@ -805,7 +809,7 @@ export namespace Doc {
             Doc.AddDocToList(Doc.GetProto(copy)[DataSym], "aliases", copy);
         }
         copy.context = undefined;
-        Doc.UserDoc().defaultAclPrivate && (copy["acl-Public"] = "Not Shared");
+        Doc.defaultAclPrivate && (copy["acl-Public"] = "Not Shared");
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
         }
@@ -861,7 +865,7 @@ export namespace Doc {
             const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
             target.layoutKey = targetKey;
             applied && (Doc.GetProto(applied).type = templateDoc.type);
-            Doc.UserDoc().defaultAclPrivate && (applied["acl-Public"] = "Not Shared");
+            Doc.defaultAclPrivate && (applied["acl-Public"] = "Not Shared");
             return applied;
         }
         return undefined;
@@ -1234,19 +1238,19 @@ export namespace Doc {
 
     export function isDocPinned(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        const curPres = CurrentUserUtils.ActivePresentation;
         return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
     }
 
     export function copyDragFactory(dragFactory: Doc) {
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
-        ndoc && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(ndoc));
+        ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc));
         if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
             dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
             Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
         }
 
-        if (ndoc) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc);
+        if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc);
 
         return ndoc;
     }
@@ -1448,7 +1452,7 @@ ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a docum
 ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
 ScriptingGlobals.add(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
 ScriptingGlobals.add(function activePresentationItem() {
-    const curPres = Doc.UserDoc().activePresentation as Doc;
+    const curPres = CurrentUserUtils.ActivePresentation;
     return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
 });
 ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 37b9be31b..8fb35981b 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -145,7 +145,7 @@ export function inheritParentAcls(parent: Doc, child: Doc) {
     const dataDoc = parent[DataSym];
     for (const key of Object.keys(dataDoc)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        const permission = (key === "acl-Public" && Doc.UserDoc().defaultAclPrivate) ? AclPrivate : dataDoc[key];
+        const permission = (key === "acl-Public" && Doc.defaultAclPrivate) ? AclPrivate : dataDoc[key];
         key.startsWith("acl") && distributeAcls(key, permission, child);
     }
 }
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index c5c6fb688..fe8100997 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -51,7 +51,7 @@ library.add(...[faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, fa
 @observer
 export class MobileInterface extends React.Component {
     static Instance: MobileInterface;
-    private _library: Promise<Doc>;
+    private _library: Doc;
     private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc());
     @observable private _sidebarActive: boolean = false; //to toggle sidebar display
     @observable private _imageUploadActive: boolean = false; //to toggle image upload
@@ -68,7 +68,7 @@ export class MobileInterface extends React.Component {
 
     constructor(props: Readonly<{}>) {
         super(props);
-        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc()); // to access documents in Dash Web
+        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), "myDashboards"); // to access documents in Dash Web
         MobileInterface.Instance = this;
     }
 
@@ -76,7 +76,7 @@ export class MobileInterface extends React.Component {
     componentDidMount = () => {
         // if the home menu is in list view -> adjust the menu toggle appropriately
         this._menuListView = this._homeDoc._viewType === "stacking" ? true : false;
-        CurrentUserUtils.SelectedTool = InkTool.None; // ink should intially be set to none
+        CurrentUserUtils.ActiveTool = InkTool.None; // ink should intially be set to none
         Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
         AudioBox.Enabled = true;
 
@@ -125,7 +125,7 @@ export class MobileInterface extends React.Component {
      * Method called when 'Library' button is pressed on the home screen
      */
     switchToLibrary = async () => {
-        this._library.then(library => this.switchCurrentView(library));
+        this.switchCurrentView(this._library);
         runInAction(() => this._homeMenu = false);
         this.toggleSidebar();
     }
@@ -140,7 +140,7 @@ export class MobileInterface extends React.Component {
         // Case 1: Parent document is 'dashboards'
         if (doc === Cast(this._library, Doc) as Doc) {
             this.dashboards = null;
-            this._library.then(library => this.switchCurrentView(library));
+            this.switchCurrentView(this._library);
             // Case 2: Parent document is the 'home' menu (root node)
         } else if (doc === Cast(this._homeDoc, Doc) as Doc) {
             this._homeMenu = true;
@@ -179,7 +179,7 @@ export class MobileInterface extends React.Component {
     @action
     returnMain = () => {
         this._parents = [this._homeDoc];
-        this._library.then(library => this.switchCurrentView(library));
+        this.switchCurrentView(this._library);
         this._homeMenu = false;
         this.dashboards = null;
     }
@@ -419,10 +419,10 @@ export class MobileInterface extends React.Component {
         button.style.color = this._ink ? "black" : "white";
 
         if (!this._ink) {
-            CurrentUserUtils.SelectedTool = InkTool.Pen;
+            CurrentUserUtils.ActiveTool = InkTool.Pen;
             this._ink = true;
         } else {
-            CurrentUserUtils.SelectedTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             this._ink = false;
         }
     }
@@ -514,7 +514,7 @@ export class MobileInterface extends React.Component {
             return <div className="docButton"
                 title={Doc.isDocPinned(this._activeDoc) ? "Unpin from presentation" : "Pin to presentation"}
                 style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }}
-                onClick={e => TabDocView.PinDoc(this._activeDoc, { unpin: isPinned })}>
+                onClick={e => TabDocView.PinDoc(this._activeDoc)}>
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>;
         } else return (null);
@@ -573,7 +573,7 @@ export class MobileInterface extends React.Component {
     // For setting up the presentation document for the home menu
     @action
     setupDefaultPresentation = () => {
-        const presentation = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        const presentation = CurrentUserUtils.ActivePresentation;
 
         if (presentation) {
             this.switchCurrentView(presentation);
-- 
cgit v1.2.3-70-g09d2


From 3e22a44461095d95062e3b809b525a0a87342ac3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 22:18:42 -0400
Subject: more cleanup to get rid of unecessary references to Doc.UserDoc().

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/CurrentUserUtils.ts                | 48 +++++++++++++---------
 src/client/util/DragManager.ts                     |  4 +-
 src/client/util/History.ts                         |  2 +-
 src/client/util/SettingsManager.tsx                | 19 ++++-----
 src/client/util/SnappingManager.ts                 |  4 ++
 src/client/views/DashboardView.tsx                 |  2 +-
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/MainView.tsx                      | 21 ++++------
 .../views/collections/CollectionDockingView.tsx    |  4 +-
 src/client/views/collections/CollectionMenu.tsx    |  4 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  4 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 30 +++++++-------
 .../nodes/button/colorDropdown/ColorDropdown.tsx   |  3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +-
 src/client/views/nodes/trails/PresBox.tsx          | 11 +----
 src/client/views/topbar/TopBar.tsx                 |  2 +-
 src/mobile/MobileInterface.tsx                     |  2 +-
 22 files changed, 93 insertions(+), 89 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e36103938..73b756a8d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -662,7 +662,7 @@ export namespace Docs {
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_");
 
             dataProps["acl-Override"] = "None";
-            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps.system = viewProps.system;
             dataProps.isPrototype = true;
@@ -679,7 +679,7 @@ export namespace Docs {
             const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
 
             const viewFirstProps: { [id: string]: any } = {};
-            viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             viewFirstProps["acl-Override"] = "None";
             viewFirstProps.author = Doc.CurrentUserEmail;
             const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1d5a9fa09..9b5fc5b98 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -181,7 +181,12 @@ export class CurrentUserUtils {
 
     /// Initializes collection of templates for notes and click functions
     static setupDocTemplates(doc: Doc, field="myTemplates") {
+        const preEleOpts:DocumentOptions = {
+            title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
+        };
+        this.AssignOpts(DocCast(doc.presElement), preEleOpts) ?? (doc.presElement= Docs.Create.PresElementBoxDocument(preEleOpts));
         const templates = [
+            DocCast(doc.presElement),
             CurrentUserUtils.setupNoteTemplates(doc),
             CurrentUserUtils.setupClickEditorTemplates(doc)
         ];
@@ -323,8 +328,7 @@ export class CurrentUserUtils {
             { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
             { toolTip: "Tap or drag to create a mobile view",     title: "Phone", icon: "mobile",       dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)',     onDragStart: 'this.dragFactory'},                             funcs: {hidden: 'IsNoviceMode()'} },
             { toolTip: "Toggle a Calculator REPL",                title: "repl", icon: "calculator",                                              scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } },
-         // { toolTip: "Tap or drag to create a presentation",    title: "Trails", icon: "pres-trail",  dragFactory: doc.emptyPresentation as Doc,scripts: {onClick: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', onDragStart: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`}, funcs: {hidden: 'IsNoviceMode()'} },
-             ];
+          ];
     }
 
     /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
@@ -533,7 +537,7 @@ export class CurrentUserUtils {
     static setupDashboards(doc: Doc, field:string) {
         var myDashboards = DocCast(doc[field]);
 
-        const newDashboard = `createNewDashboard(Doc.UserDoc())`;
+        const newDashboard = `createNewDashboard()`;
         const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
              title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
         const reqdBtnScript = {onClick: newDashboard,}
@@ -830,6 +834,11 @@ export class CurrentUserUtils {
         const reqdOpts = { title: "overlay documents", backgroundColor: "#aca3a6", system: true };
         return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.FreeformDocument([], reqdOpts));
     }
+
+    static setupPublished(doc:Doc, field = "myPublishedDocs") {
+        const reqdOpts = { title: "published docs", backgroundColor: "#aca3a6", system: true };
+        return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+    }
     
     /// The database of all links on all documents
     static async setupLinkDocs(doc: Doc, linkDatabaseId: string) {
@@ -958,7 +967,6 @@ export class CurrentUserUtils {
         Doc.noviceMode ?? (Doc.noviceMode = true);
         doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true);
         doc._showLabel ?? (doc._showLabel = true);
-        doc._showMenuLabel ?? (doc._showMenuLabel = true);
         doc.textAlign ?? (doc.textAlign = "left");
         doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");;
         doc.activeInkWidth ?? (doc.activeInkWidth = 1);
@@ -975,17 +983,17 @@ export class CurrentUserUtils {
         doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myPublishedDocs ?? (doc.myPublishedDocs = new List<Doc>());
         doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
         await this.setupLinkDocs(doc, linkDatabaseId);
         await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
-        this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard
+        this.setupPublished(doc);  // sets up the list doc of all docs that have been published (meaning that they can be auto-linked by typing their title into another text box)
         this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
+        this.setupDocTemplates(doc); // sets up the template menu of templates
         doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument());
 
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
@@ -1037,7 +1045,8 @@ export class CurrentUserUtils {
 
     public static _urlState: HistoryUtil.DocUrl;
 
-    public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => {
+    public static openDashboard = (doc: Doc, fromHistory = false) => {
+        const userDoc =  Doc.UserDoc();
         CurrentUserUtils.MainDocId = doc[Id];
         if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) {
             Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc);
@@ -1045,7 +1054,7 @@ export class CurrentUserUtils {
 
         if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
             !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
-            userDoc ? (userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
+            userDoc ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
         }
         const state = CurrentUserUtils._urlState;
         if (state.sharing === true && !userDoc) {
@@ -1138,11 +1147,11 @@ export class CurrentUserUtils {
 
     }
 
-    public static async snapshotDashboard(userDoc: Doc) {
+    public static async snapshotDashboard() {
         if (CurrentUserUtils.ActiveDashboard) {
             const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
             Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
-            CurrentUserUtils.openDashboard(userDoc, copy);
+            CurrentUserUtils.openDashboard(copy);
         }
     }
 
@@ -1150,9 +1159,9 @@ export class CurrentUserUtils {
         CurrentUserUtils.ActiveDashboard = undefined;
     }
 
-    public static createNewDashboard = async (userDoc: Doc, id?: string) => {
-        const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
-        const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
+    public static createNewDashboard = (id?: string) => {
+        const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
+        const dashboards = CurrentUserUtils.MyDashboards;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
@@ -1172,10 +1181,10 @@ export class CurrentUserUtils {
         dashboardDoc.data = new List<Doc>(dashboardTabs);
         dashboardDoc["pane-count"] = 1;
 
-        userDoc.activePresentation = presentation;
+        CurrentUserUtils.ActivePresentation = presentation;
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
-      //   CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
+      //   CurrentUserUtils.openDashboard(dashboardDoc);
     }
 
     public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
@@ -1209,6 +1218,7 @@ export class CurrentUserUtils {
     public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
     public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
     public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); }
+    public static get MyPublishedDocs() { return DocCast(Doc.UserDoc().myPublishedDocs); }
     public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
     public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; }
     public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
@@ -1230,8 +1240,8 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
 ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
 ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
-ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called");
+ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
+ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(); }, "creates a new dashboard when called");
 ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)");
@@ -1240,7 +1250,7 @@ ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.In
 ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
     const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
     if (dashboards && dashboards.length > 1) {
-        if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboards.find(doc => doc !== dashboard)!);
+        if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!);
         Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard);
     }
 },
@@ -1248,7 +1258,7 @@ ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
 ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
     const dashboardAlias = Doc.MakeAlias(dashboard);
     Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias);
-    CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias);
+    CurrentUserUtils.openDashboard(dashboardAlias);
 },
     "adds Dashboard to set of Dashboards");
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index dbfba8992..2b62945af 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -6,7 +6,7 @@ import { PrefetchProxy } from "../../fields/Proxy";
 import { listSpec } from "../../fields/Schema";
 import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { ScriptField } from "../../fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
 import { emptyFunction, Utils } from "../../Utils";
 import { Docs, DocUtils } from "../documents/Documents";
 import * as globalCssVariables from "../views/global/globalCssVariables.scss";
@@ -72,6 +72,8 @@ export namespace DragManager {
     export let StartWindowDrag: Opt<((e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void)>;
     export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;
 
+    export function GetRaiseWhenDragged() { return BoolCast(Doc.UserDoc()._raiseWhenDragged); }
+    export function SetRaiseWhenDragged(val:boolean) { Doc.UserDoc()._raiseWhenDragged = val }
     export function Root() {
         const root = document.getElementById("root");
         if (!root) {
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 632348306..7dcff9c56 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -197,7 +197,7 @@ export namespace HistoryUtil {
             await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
         }
         if (field instanceof Doc) {
-            CurrentUserUtils.openDashboard(Doc.UserDoc(), field, true);
+            CurrentUserUtils.openDashboard(field, true);
         }
     }
 
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 382274462..22e33ab1e 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -10,7 +10,9 @@ import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager
 import { DocServer } from "../DocServer";
 import { Networking } from "../Network";
 import { MainViewModal } from "../views/MainViewModal";
+import { FontIconBox } from "../views/nodes/button/FontIconBox";
 import { CurrentUserUtils } from "./CurrentUserUtils";
+import { DragManager } from "./DragManager";
 import { GroupManager } from "./GroupManager";
 import "./SettingsManager.scss";
 import { undoBatch } from "./UndoManager";
@@ -155,19 +157,14 @@ export class SettingsManager extends React.Component<{}> {
                 <div className="preferences-check">Show full toolbar</div>
             </div>
             <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._raiseWhenDragged = !Doc.UserDoc()._raiseWhenDragged}
-                    checked={BoolCast(Doc.UserDoc()._raiseWhenDragged)} />
+                <input type="checkbox" onChange={e => DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged())}
+                    checked={DragManager.GetRaiseWhenDragged()} />
                 <div className="preferences-check">Raise on drag</div>
             </div>
             <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._showLabel = !Doc.UserDoc()._showLabel}
-                    checked={BoolCast(Doc.UserDoc()._showLabel)} />
-                <div className="preferences-check">Show tool button labels</div>
-            </div>
-            <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._showMenuLabel = !Doc.UserDoc()._showMenuLabel}
-                    checked={BoolCast(Doc.UserDoc()._showMenuLabel)} />
-                <div className="preferences-check">Show menu button labels</div>
+                <input type="checkbox" onChange={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())}
+                    checked={FontIconBox.GetShowLabels()} />
+                <div className="preferences-check">Show button labels</div>
             </div>
         </div>;
     }
@@ -271,7 +268,7 @@ export class SettingsManager extends React.Component<{}> {
                 <div className="tab-column-content">
                     <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
                     <div className="default-acl">
-                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)}
+                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.defaultAclPrivate)}
                             onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
                         <div className="acl-text">Default access private</div>
                     </div>
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 069f81d38..057843c68 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -1,5 +1,6 @@
 import { observable, action, runInAction } from "mobx";
 import { computedFn } from "mobx-utils";
+import { Doc } from "../../fields/Doc";
 
 export namespace SnappingManager {
 
@@ -30,6 +31,9 @@ export namespace SnappingManager {
     export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
     export function GetIsDragging() { return manager.IsDragging; }
 
+    export function SetShowSnapLines(show: boolean) { runInAction(() => Doc.UserDoc().showSnapLines = show); }
+    export function GetShowSnapLines() { return Doc.UserDoc().showSnapLines; }
+
     /// bcz; argh!! TODO;   These do not belong here, but there were include order problems with leaving them in util.ts
     // need to investigate further what caused the mobx update problems and move to a better location.
     const getCachedGroupByNameCache = computedFn(function (name: string) { return manager.cachedGroups.includes(name); }, true);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 9a0f25fe3..a126218c4 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -28,7 +28,7 @@ export class DashboardView extends React.Component {
 
       newDashboard = async () => {
             const batch = UndoManager.StartBatch("new dash");
-            await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+            await CurrentUserUtils.createNewDashboard();
             batch.end();
       }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 18cf785b9..4247501bb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -87,10 +87,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                         if (titleFieldKey === "title") {
                               d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
                               if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
-                                    Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                                    Doc.RemoveDocFromList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
                               }
                               if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
-                                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                                    Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
                               }
                         }
                         //@ts-ignore
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 61cca0421..6ee8065f5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -225,26 +225,21 @@ export class MainView extends React.Component {
         // Load the user's active dashboard, or create a new one if initial session after signup
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
-            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
+            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(), { fireImmediately: true });
         } else {
             PromiseValue(this.userDoc.activeDashboard).then(dash => {
-                if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
-                else CurrentUserUtils.createNewDashboard(this.userDoc);
+                if (dash instanceof Doc) CurrentUserUtils.openDashboard(dash);
+                else CurrentUserUtils.createNewDashboard();
             });
         }
     }
 
     @action
     createNewPresentation = async () => {
-        if (!await this.userDoc.myTrails) {
-            this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
-            }));
-        }
         const pres = Docs.Create.PresDocument({ title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "left");
-        this.userDoc.activePresentation = pres;
-        Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
+        CurrentUserUtils.ActivePresentation = pres;
+        Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", pres);
     }
 
     @action
@@ -353,9 +348,9 @@ export class MainView extends React.Component {
     addDocTabFunc = (doc: Doc, location: string): boolean => {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
         const locationParams = locationFields.length > 1 ? locationFields[1] : "";
-        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(doc);
         switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            case "dashboard": return CurrentUserUtils.openDashboard(doc);
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "lightbox": return LightboxView.AddDocTab(doc, location);
@@ -563,7 +558,7 @@ export class MainView extends React.Component {
             </div>;
     }
     @computed get snapLines() {
-        return !this.userDoc.showSnapLines ? (null) : <div className="mainView-snapLines">
+        return !SnappingManager.GetShowSnapLines() ? (null) : <div className="mainView-snapLines">
             <svg style={{ width: "100%", height: "100%" }}>
                 {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
                 {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 5f36a7a51..19355b8e1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -121,7 +121,7 @@ export class CollectionDockingView extends CollectionSubView() {
         SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
-            return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            return CurrentUserUtils.openDashboard(doc);
         }
         const newItemStackConfig = {
             type: 'stack',
@@ -172,7 +172,7 @@ export class CollectionDockingView extends CollectionSubView() {
     @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
-        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
+        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(document);
 
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
         if (tab) {
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 9b1bb5b97..668d82387 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -164,7 +164,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
         //     </button>
         // </Tooltip>;
 
-        // OLD BUTTONS
+        // //OLD BUTTONS
         // return this.getElement(!this.SelectedCollection ? [/*button*/] :
         //     [<CollectionViewBaseChrome key="chrome"
         //         docView={this.SelectedCollection}
@@ -306,7 +306,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
 
     @undoBatch
     viewChanged = (e: React.ChangeEvent) => {
-        const target = this.document !== Doc.UserDoc().sidebar ? this.document : this.document.proto as Doc;
+        const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc;
         //@ts-ignore
         target._viewType = e.target.selectedOptions[0].value;
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4b5c5e3fb..63616263e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -205,7 +205,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 });
             }
             if (this.Document._viewType === CollectionViewType.Docking) {
-                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "project-diagram" });
+                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(), icon: "project-diagram" });
             }
 
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 70db121d1..62d07b0e4 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -329,7 +329,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
         const locationParams = locationFields.length > 1 ? locationFields[1] : "";
         switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            case "dashboard": return CurrentUserUtils.openDashboard(doc);
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ffe146ae4..99eec7892 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -253,7 +253,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged(): d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -1645,7 +1645,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-        !Doc.noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? "Hide" : "Show") + " Snap Lines", event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: "compress-arrows-alt" }) : null;
         !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b62020a04..081a1a924 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -6,7 +6,7 @@ import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { RichTextField } from "../../../../fields/RichTextField";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { Cast, DocCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
 import { GetEffectiveAcl } from "../../../../fields/util";
 import { intersectRect, returnFalse, Utils } from "../../../../Utils";
@@ -156,7 +156,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                     }));
             } else if (e.key === "s" && e.ctrlKey) {
                 e.preventDefault();
-                const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!;
+                const slide = Doc.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
                 slide.x = x;
                 slide.y = y;
                 FormattedTextBox.SelectOnLoad = slide[Id];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4d84a8ad2..89096c948 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -484,7 +484,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             let stopPropagate = true;
             let preventDefault = true;
             const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
-            (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+            (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
             if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (this._timeout) {
                     clearTimeout(this._timeout);
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 3af6a3d51..1b37e729b 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -79,6 +79,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         }
     }
 
+    static GetShowLabels() { return BoolCast(Doc.UserDoc()._showLabel); }
+    static SetShowLabels(show:boolean) { Doc.UserDoc()._showLabel = show; }
+
     // Determining UI Specs
     @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
     @observable private icon = StrCast(this.dataDoc.icon, "user") as any;
@@ -111,7 +114,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         // Script for checking the outcome of the toggle
         const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
 
-        const label = !Doc.UserDoc()._showLabel ? (null) :
+        const label = !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label">
                 {this.label}
             </div>;
@@ -212,7 +215,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                {!this.label || !FontIconBox.GetShowLabels() ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 <div
                     className="menuButton-dropdown"
                     style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
@@ -283,7 +286,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             </div>;
         });
 
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ bottom: 0, position: "absolute", color: color, backgroundColor: backgroundColor }}>
                 {this.label}
             </div>;
@@ -337,7 +340,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
 
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
@@ -349,7 +352,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         </div>;
         setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
         return (
-            <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")} ${this.colorPickerClosed}`}
+            <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")} ${this.colorPickerClosed}`}
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)}
                 onPointerDown={e => e.stopPropagation()}>
@@ -381,7 +384,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
         // Button label
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
@@ -400,7 +403,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             );
         } else {
             return (
-                <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`}
+                <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`}
                     style={{ opacity: 1, backgroundColor, color }}>
                     <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                     {label}
@@ -423,7 +426,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
                 <div className="menuButton-wrap">
                     <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
-                    {!this.label || !Doc.UserDoc()._showLabel ? (null) :
+                    {!this.label || !FontIconBox.GetShowLabels() ? (null) :
                         <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 </div>
             </div>
@@ -450,12 +453,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     render() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
 
-        const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
+        const menuLabel = !this.label || !FontIconBox.GetShowLabels()  ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
                 {this.label}
             </div>;
@@ -497,7 +500,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
             case ButtonType.ToolButton:
                 button = (
-                    <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`} style={{ opacity: 1, backgroundColor, color }}>
+                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} style={{ opacity: 1, backgroundColor, color }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -509,7 +512,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
             case ButtonType.ClickButton:
                 button = (
-                    <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`} style={{ color, backgroundColor, opacity: 1 }}>
+                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} style={{ color, backgroundColor, opacity: 1 }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -675,10 +678,9 @@ ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: b
 ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.noAutoLink : Doc.UserDoc().noAutoLink) ? Colors.MEDIUM_BLUE : "transparent";
+        return (editorView ? RichTextMenu.Instance.noAutoLink : false) ? Colors.MEDIUM_BLUE : "transparent";
     }
     if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor();
-    else Doc.UserDoc().noAutoLink = Doc.UserDoc().noAutoLink ? true : false;
 });
 
 ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
index 235495250..7f414ddbb 100644
--- a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
+++ b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
@@ -5,6 +5,7 @@ import { IButtonProps } from '../ButtonInterface';
 import { ColorState, SketchPicker } from 'react-color';
 import { ScriptField } from '../../../../../fields/ScriptField';
 import { Doc } from '../../../../../fields/Doc';
+import { FontIconBox } from '../FontIconBox';
 
 export class ColorDropdown extends Component<IButtonProps> {
     render() {
@@ -31,7 +32,7 @@ export class ColorDropdown extends Component<IButtonProps> {
             disableAlpha={!stroke}
             onChange={func} color={boolResult ? boolResult : "#FFFFFF"}
             presetColors={colorOptions} />;
-        const label = !this.props.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.props.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color: this.props.color, backgroundColor: this.props.backgroundColor, position: "absolute" }}>
                 {this.props.label}
             </div>;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 90199618b..600730d87 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -355,7 +355,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             var tr = this._editorView.state.tr as any;
             const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
             tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
-            DocListCast(Doc.UserDoc().myPublishedDocs).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks));
+            DocListCast(CurrentUserUtils.MyPublishedDocs.data).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks));
             tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
             oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
@@ -376,7 +376,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             if (!(cfield instanceof ComputedField)) {
                 this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : "");
                 if (str.startsWith("@") && str.length > 1) {
-                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc);
+                    Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, this.rootDoc);
                 }
             }
         }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 0d2cffc2c..9f858539f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -10,7 +10,7 @@ import { InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { PrefetchProxy } from "../../../../fields/Proxy";
 import { listSpec } from "../../../../fields/Schema";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
@@ -131,17 +131,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
         else return false;
     }
-    @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
     constructor(props: any) {
         super(props);
         if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
-        if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
-            Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
-                title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
-            }));
-        }
         this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
-
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
@@ -728,7 +721,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     }
-    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : this.presElement;
+    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement);
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 1de0c0b10..6a4deca38 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -52,7 +52,7 @@ export class TopBar extends React.Component {
                                         ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
                                         ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
                                             const batch = UndoManager.StartBatch("snapshot");
-                                            await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
+                                            await CurrentUserUtils.snapshotDashboard();
                                             batch.end();
                                         }, icon: "edit" });
                                         dashView?.showContextMenu(e.clientX+20, e.clientY+30);
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index fe8100997..bf06faeb9 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -391,7 +391,7 @@ export class MobileInterface extends React.Component {
      * Handles the 'Create New Dashboard' button in the menu (taken from MainView.tsx)
      */
     @action
-    createNewDashboard = async (id?: string) => {
+    createNewDashboard = (id?: string) => {
         const scens = CurrentUserUtils.MyDashboards;
         const dashboardCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
-- 
cgit v1.2.3-70-g09d2


From 0dd3bbfc119d48bd2c32f8e55b7051e3ddc530c3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 21 Jun 2022 14:40:04 -0400
Subject: restored experimental templates.  fixed (enabled) pointer events on
 nested column/row views when container is active.  got rid of
 freezeDimensions which wasn't needed and introduced artifacts for views that
 set it (timeView, GridView)

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/CurrentUserUtils.ts                | 74 +++++++++++-----------
 src/client/util/DragManager.ts                     | 11 ++--
 src/client/views/PropertiesView.tsx                |  1 -
 .../views/collections/CollectionStackingView.tsx   |  1 -
 .../views/collections/CollectionTimeView.tsx       |  5 +-
 src/client/views/collections/CollectionView.tsx    |  1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 -
 .../collectionGrid/CollectionGridView.tsx          |  1 -
 .../CollectionMulticolumnView.tsx                  | 14 ++--
 .../CollectionMultirowView.tsx                     |  6 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  1 -
 .../collections/collectionSchema/SchemaTable.tsx   |  1 -
 src/client/views/nodes/DocumentContentsView.tsx    |  1 -
 src/client/views/nodes/DocumentView.tsx            |  7 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  4 +-
 16 files changed, 61 insertions(+), 69 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 73b756a8d..59391d150 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -194,6 +194,7 @@ export class DocumentOptions {
     childLimitHeight?: number; // whether to limit the height of collection children.  0 - means  height can be no bigger than width
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
+    childDocumentsActive?: boolean; // whether child documents are active when parent is document active
     childDontRegisterViews?: boolean;
     childHideLinkButton?: boolean; // hide link buttons on all children
     childContextMenuFilters?: List<ScriptField>;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 9b5fc5b98..e80fe6776 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,6 +1,6 @@
 import { computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
-import { DataSym, Doc, DocListCast, DocListCastAsync, StrListCast } from "../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, Opt, StrListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
@@ -109,40 +109,43 @@ export class CurrentUserUtils {
     }
 
     // initializes experimental advanced template views - slideView,  headerView
-    static setupExperimentalTemplateButtons(doc: Doc, field="template-experimental-buttons") {
-        const tempDocs = DocCast(doc[field]);
-        const requiredTypeNameFields:{opts:DocumentOptions, template:() => Doc}[] = [
+    static setupExperimentalTemplateButtons(doc: Doc, tempDocs?:Doc) {
+        const requiredTypeNameFields:{btnOpts:DocumentOptions, templateOpts:DocumentOptions, template:(opts:DocumentOptions) => Doc}[] = [
             {
-                opts:{type: "slide", icon: "address-card"}, template: () => Docs.Create.MultirowDocument(
+                btnOpts: { title: "slide", icon: "address-card" }, 
+                templateOpts: { _width: 400, _height: 300, title: "slideView", childDocumentsActive: true, _xMargin: 3, _yMargin: 3, system: true }, 
+                template: (opts:DocumentOptions) => Docs.Create.MultirowDocument(
                     [
-                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }),
-                        Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
-                    ],
-                    { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true }
-                )
+                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200,  system: true }),
+                        Docs.Create.TextDocument("", { title: "text", _fitWidth:true,  _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
+                    ], opts)
             },
             {
-                opts:{type: "mobile", icon: "mobile"}, template: () => this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, },
-                    [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
-                    this.mobileTextContainer({},
-                        [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
-                    ]
+                btnOpts: { title: "mobile", icon: "mobile" }, 
+                templateOpts: { title: "NEW MOBILE BUTTON", onClick: undefined, }, 
+                template: (opts:DocumentOptions) => this.mobileButton(opts,
+                        [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
+                        this.mobileTextContainer({},
+                            [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
+                        ]
                 )
             },
         ];
-        const requiredTypes = requiredTypeNameFields.map(({ opts, template }) => {
-            const docType =  DocListCast(tempDocs?.data)?.find(doc => doc.title === opts.type);
-            const reqdOpts = {
-                dragFactory: template(),
-                title: opts.type,
-                icon: opts.icon
+        const requiredTypes = requiredTypeNameFields.map(({ btnOpts, template, templateOpts }) => {
+            const tempBtn =  DocListCast(tempDocs?.data)?.find(doc => doc.title === btnOpts.title);
+            const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory); }' };
+            const assignBtnAndTempOpts = (templateBtn:Opt<Doc>, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => {
+                if (templateBtn) {
+                    this.AssignOpts(templateBtn,btnOpts);
+                    this.AssignOpts(DocCast(templateBtn.dragFactory), templateOptions) ?? (templateBtn.dragFactory = template(templateOpts));
+                }
+                return templateBtn;
             };
-            const reqdScripts = {onDragStart: 'copyDragFactory(this.dragFactory)'};
             const makeTemp = (doc:Doc) => {
                 doc.isTemplateDoc = makeTemplate(doc);
                 return doc;
             }
-            return this.AssignScripts(!docType ? makeTemp(CurrentUserUtils.createToolButton(reqdOpts)) : this.AssignOpts(docType, reqdOpts)!, reqdScripts)!;
+            return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? CurrentUserUtils.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts);
         });
 
         const reqdOpts = {
@@ -150,12 +153,9 @@ export class CurrentUserUtils {
             _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true,
             _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
-        const reqdScripts = {dropConverter : "convertToButtons(dragData)"};
-        const reqdFuncs = {hidden: "IsNoviceMode()"}
-        return this.AssignScripts(!tempDocs ? 
-                (doc[field] = Docs.Create.MasonryDocument(requiredTypes, reqdOpts)) :
-                this.AssignOpts(tempDocs, reqdOpts, requiredTypes)!, 
-            reqdScripts, reqdFuncs);
+        const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
+        const reqdFuncs = { hidden: "IsNoviceMode()" };
+        return this.AssignScripts(this.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
     }
 
     /// Initializes templates that can be applied to notes
@@ -525,12 +525,12 @@ export class CurrentUserUtils {
     static setupToolsBtnPanel(doc: Doc, field:string) {
         const myTools = DocCast(doc[field]);
         const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined);
-        //const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc);
+        const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
         const reqdToolOps:DocumentOptions = {
             title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
             _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
         };
-        this.AssignOpts(myTools, reqdToolOps, [creatorBtns, /*templateBtns*/]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, /*templateBtns*/], reqdToolOps));
+        this.AssignOpts(myTools, reqdToolOps, [creatorBtns, templateBtns]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, templateBtns], reqdToolOps));
     }
 
     /// initializes the left sidebar dashboard pane
@@ -749,7 +749,7 @@ export class CurrentUserUtils {
     }
 
     static schemaTools():Button[] {
-        return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{onClick:'toggleSchemaPreview(_readOnly_)'}, }];
+        return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{return toggleSchemaPreview(_readOnly_);}'}, }];
     }
 
     static webTools() {
@@ -774,9 +774,9 @@ export class CurrentUserUtils {
             }, // Always show
             { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton,icon: "chevron-left",  scripts:{onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'} },
             { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, icon: "chevron-right", scripts:{onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}},
-            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: 'selectedDocumentType()' }}, // Only when a document is selected
-            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: 'selectedDocumentType()'} },
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: 'selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform
+            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: '!selectedDocumentType()' }}, // Only when a document is selected
+            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: '!selectedDocumentType()'} },
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
             { title: "Text", icon: "text",  subMenu: CurrentUserUtils.textTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available
             { title: "Ink", icon: "ink", subMenu: CurrentUserUtils.inkTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available
@@ -983,7 +983,6 @@ export class CurrentUserUtils {
         doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
         await this.setupLinkDocs(doc, linkDatabaseId);
         await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
@@ -995,7 +994,8 @@ export class CurrentUserUtils {
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
         this.setupDocTemplates(doc); // sets up the template menu of templates
         doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument());
-
+        doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
+        
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
         if (doc.activeDashboard instanceof Doc) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2b62945af..09b463c2f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -223,10 +223,13 @@ export namespace DragManager {
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
                 docDragData.droppedDocuments =
-                    await Promise.all(dragData.draggedDocuments.map(async d => !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
-                        docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
-                            docDragData.dropAction === "proto" ? Doc.GetProto(d) :
-                                docDragData.dropAction === "copy" ? (await Doc.MakeClone(d)).clone : d));
+                    await Promise.all(dragData.draggedDocuments.map(async d => 
+                        !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? 
+                        addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
+                            docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
+                                docDragData.dropAction === "proto" ? Doc.GetProto(d) :
+                                    docDragData.dropAction === "copy" ? 
+                                        (await Doc.MakeClone(d)).clone : d));
                 !["same", "proto"].includes(docDragData.dropAction as any) && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
                     const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []);
                     const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 90c86fa18..faab2ed26 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -304,7 +304,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     docViewPath={returnEmptyDoclist}
-                    freezeDimensions={true}
                     dontCenter={"y"}
                     isDocumentActive={returnFalse}
                     isContentActive={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 277fcd59c..684c919bd 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -250,7 +250,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             isDocumentActive={this.isContentActive}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            freezeDimensions={this.props.childFreezeDimensions}
             NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeWidth(doc) ? width : undefined}  // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
             NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeHeight(doc) ? height : undefined}
             dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 7573b938a..3dd9d2d84 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -59,7 +59,7 @@ export class CollectionTimeView extends CollectionSubView() {
         //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), "");
         ///const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         runInAction(() => {
-            this._childClickedScript = ScriptField.MakeScript("openInLightbox(self, shiftKey)", { this: Doc.name, shiftKey: "boolean" });//, { detailView: detailView! });
+            this._childClickedScript = ScriptField.MakeScript("openInLightbox(self)", { this: Doc.name });
             this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
         });
     }
@@ -138,8 +138,7 @@ export class CollectionTimeView extends CollectionSubView() {
                 fitContentsToBox={returnTrue}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
-                childFreezeDimensions={true}
-                dontScaleFilter={this.dontScaleFilter}
+                //dontScaleFilter={this.dontScaleFilter}
                 layoutEngine={this.layoutEngine} />
         </div>;
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 63616263e..b432104a1 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -75,7 +75,6 @@ export interface CollectionViewProps extends FieldViewProps {
     childHideResizeHandles?: () => boolean;
     childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
     childLayoutString?: string;
-    childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
     childIgnoreNativeSize?: boolean;
     childClickScript?: ScriptField;
     childDoubleClickScript?: ScriptField;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 99eec7892..542b1fce1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1265,7 +1265,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             styleProvider={this.getClusterColor}
             dataProvider={this.childDataProvider}
             sizeProvider={this.childSizeProvider}
-            freezeDimensions={BoolCast(this.props.Document.childFreezeDimensions, this.props.childFreezeDimensions)}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             bringToFront={this.bringToFront}
             showTitle={this.props.childShowTitle}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index da102fe18..4e4c33446 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -162,7 +162,6 @@ export class CollectionGridView extends CollectionSubView() {
             DataDoc={layout.resolvedDataDoc as Doc}
             PanelWidth={width}
             PanelHeight={height}
-            freezeDimensions={true}
             ScreenToLocalTransform={dxf}
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b7ba94940..777ef464f 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -6,6 +6,7 @@ import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -242,7 +243,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         return this.props.addDocTab(doc, where);
     }
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
+    isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -251,13 +252,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
-            isContentActive={this.isChildContentActive}
-            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
-            hideResizeHandles={this.props.childHideResizeHandles?.()}
-            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToBox={this.props.fitContentsToBox}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
@@ -266,6 +261,11 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             onDoubleClick={this.onChildDoubleClickHandler}
             suppressSetHeight={true}
             ScreenToLocalTransform={dxf}
+            isContentActive={this.isChildContentActive}
+            isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+            hideResizeHandles={this.props.childHideResizeHandles?.()}
+            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+            fitContentsToBox={this.props.fitContentsToBox}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 338639a83..08385bcb5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -6,6 +6,7 @@ import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -242,7 +243,7 @@ export class CollectionMultirowView extends CollectionSubView() {
         return this.props.addDocTab(doc, where);
     }
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
+    isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -251,7 +252,6 @@ export class CollectionMultirowView extends CollectionSubView() {
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
@@ -261,7 +261,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             isContentActive={this.isChildContentActive}
-            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
+            isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
             fitContentsToBox={this.props.fitContentsToBox}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f45068b6a..9eba788a9 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -403,7 +403,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     Document={this.previewDocument}
                     DataDoc={undefined}
                     fitContentsToBox={returnTrue}
-                    freezeDimensions={true}
                     dontCenter={"y"}
                     focus={DocUtils.DefaultFocus}
                     renderDepth={this.props.renderDepth}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index bea5b3be6..43266a571 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -574,7 +574,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         DataDoc={this._showDataDoc}
                         styleProvider={DefaultStyleProvider}
                         docViewPath={returnEmptyDoclist}
-                        freezeDimensions={true}
                         focus={DocUtils.DefaultFocus}
                         renderDepth={this.props.renderDepth}
                         rootSelected={returnFalse}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 70732e74c..db076c069 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -143,7 +143,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
         const docOnlyProps = [  // these are the properties in  DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
-            "freezeDimensions",
             "hideResizeHandles",
             "hideTitle",
             "treeViewDoc",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 89096c948..263e1dc4c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -160,7 +160,6 @@ export interface DocumentViewSharedProps {
 // these props are specific to DocuentViews
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
-    freezeDimensions?: boolean;
     hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
     hideTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDecorationTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
@@ -1220,11 +1219,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); }
     @computed get nativeWidth() {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
-            returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
+            returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
     }
     @computed get nativeHeight() {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
-            returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
+            returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
     }
     @computed get shouldNotScale() {
         return (this.fitWidth && !this.nativeWidth) ||
@@ -1312,8 +1311,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     ContentScale = () => this.nativeScaling;
     selfView = () => this;
     screenToLocalTransform = () => {
-        const oshift = this.fitWidth && this.ComponentView instanceof FormattedTextBox;
-        const shift = oshift ? -(this.props.PanelHeight() - this.rootDoc[HeightSym]()) / 2 : 0;
         return this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).translate(0, shift).scale(1 / this.nativeScaling);
     }
     componentDidMount() {
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 1b37e729b..85efc67a5 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -903,9 +903,9 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
     }
     else if (selected) {
         if (NumCast(selected.schemaPreviewWidth) > 0) {
-            selected.schemaPreviewWidth = 200;
-        } else {
             selected.schemaPreviewWidth = 0;
+        } else {
+            selected.schemaPreviewWidth = 200;
         }
     }
 });
-- 
cgit v1.2.3-70-g09d2


From 5c582ee52f3af9515d3397fa7661d068fe1babe8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 21 Jun 2022 14:44:48 -0400
Subject: from last and keep keyvalue tabs out of topbar

---
 src/client/views/collections/CollectionDockingView.tsx | 6 ++++--
 src/client/views/nodes/DocumentView.tsx                | 4 +---
 2 files changed, 5 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 19355b8e1..07fcd6a7d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -416,8 +416,10 @@ export class CollectionDockingView extends CollectionSubView() {
     }
 
     tabDestroyed = (tab: any) => {
-        Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, "data", tab.DashDoc);
-        Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
+        if(tab.DashDoc?.type !== DocumentType.KVP)  {
+            Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, "data", tab.DashDoc);
+            Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
+        }
         const dview = CollectionDockingView.Instance.props.Document;
         const fieldKey = CollectionDockingView.Instance.props.fieldKey;
         Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 263e1dc4c..5d2adc321 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1310,9 +1310,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     PanelHeight = () => this.panelHeight;
     ContentScale = () => this.nativeScaling;
     selfView = () => this;
-    screenToLocalTransform = () => {
-        return this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).translate(0, shift).scale(1 / this.nativeScaling);
-    }
+    screenToLocalTransform = () =>this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).scale(1 / this.nativeScaling);
     componentDidMount() {
         this._disposers.reactionScript = reaction(
             () => ScriptCast(this.rootDoc.reactionScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result,
-- 
cgit v1.2.3-70-g09d2


From 8eb794e233f905daaa5b9a25c6720e567512653e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 23 Jun 2022 12:15:47 -0400
Subject: fixed : contextMenu .  fixed some fitWidth issues for text where
 heights weren't working right

---
 src/client/documents/Documents.ts                  | 27 +++------
 src/client/util/CurrentUserUtils.ts                |  4 +-
 src/client/views/ContextMenu.tsx                   | 64 ++++++----------------
 src/client/views/ContextMenuItem.tsx               | 10 +---
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  3 +-
 6 files changed, 33 insertions(+), 77 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ad6a4dd3c..fc73deb36 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -363,7 +363,7 @@ export namespace Docs {
                 layout: { view: FormattedTextBox, dataField: "text" },
                 options: {
                     _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true,
-                    links: "@links(self)"
+                    forceReflow: true, links: "@links(self)"
                 }
             }],
             [DocumentType.SEARCH, {
@@ -400,7 +400,7 @@ export namespace Docs {
             }],
             [DocumentType.AUDIO, {
                 layout: { view: AudioBox, dataField: defaultDataKey },
-                options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" }
+                options: { _height: 100, backgroundColor: "lightGray", forceReflow: true, nativeDimModifiable: true, links: "@links(self)" }
             }],
             [DocumentType.REC, {
                 layout: { view: VideoBox, dataField: defaultDataKey },
@@ -412,7 +412,7 @@ export namespace Docs {
             }],
             [DocumentType.MAP, {
                 layout: { view: MapBox, dataField: defaultDataKey },
-                options: { _height: 600, _width: 800, links: "@links(self)" }
+                options: { _height: 600, _width: 800, nativeDimModifiable: true, links: "@links(self)" }
             }],
             [DocumentType.IMPORT, {
                 layout: { view: DirectoryImportBox, dataField: defaultDataKey },
@@ -450,11 +450,11 @@ export namespace Docs {
             }],
             [DocumentType.EQUATION, {
                 layout: { view: EquationBox, dataField: defaultDataKey },
-                options: { links: "@links(self)", hideResizeHandles: true, hideDecorationTitle: true }
+                options: { links: "@links(self)", nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true }
             }],
             [DocumentType.FUNCPLOT, {
                 layout: { view: FunctionPlotBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
+                options: { nativeDimModifiable: true, links: "@links(self)" }
             }],
             [DocumentType.BUTTON, {
                 layout: { view: LabelBox, dataField: "onClick" },
@@ -493,7 +493,7 @@ export namespace Docs {
             }],
             [DocumentType.COMPARISON, {
                 layout: { view: ComparisonBox, dataField: defaultDataKey },
-                options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" }
+                options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" }
             }],
             [DocumentType.GROUPDB, {
                 data: new List<Doc>(),
@@ -506,7 +506,7 @@ export namespace Docs {
             }],
             [DocumentType.DATAVIZ, {
                 layout: { view: DataVizBox, dataField: defaultDataKey },
-                options: { _fitWidth: true, _fitHeight: true, links: "@links(self)" }
+                options: { _fitWidth: true, nativeDimModifiable: true, links: "@links(self)" }
             }]
         ]);
 
@@ -530,15 +530,6 @@ export namespace Docs {
             const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
             // fetch the actual prototype documents from the server
             const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds);
-            if (!Docs.newAccount) {
-                Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeHeightUnfrozen = true; // to avoid having to recreate the DB
-                Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeHeightUnfrozen = true;
-                Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeHeightUnfrozen = true;
-                Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeDimModifiable = true; // to avoid having to recreate the DB
-                Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeDimModifiable = true;
-                Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeDimModifiable = true;
-            }
-
             // update this object to include any default values: DocumentOptions for all prototypes
             prototypeIds.map(id => {
                 const existing = actualProtos[id] as Doc;
@@ -1273,8 +1264,8 @@ export namespace DocUtils {
             })) as ContextMenuProps[],
             icon: "sticky-note"
         });
-        const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data).lastElement()?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
-            description: ":" + StrCast(dragDoc.title),
+        const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data)[0]?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
+            description: ":" + StrCast(dragDoc.title).replace("Untitled ",""),
             event: undoBatch((args: { x: number, y: number }) => {
                 const newDoc = Doc.copyDragFactory(dragDoc);
                 if (newDoc) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b9724051a..28d20c56b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -282,13 +282,13 @@ export class CurrentUserUtils {
               }[] = [
             {key: "Note",       creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _autoHeight: true }},
             {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 150, _height: 100 }},
-            {key: "Equation",   creator: opts => Docs.Create.EquationDocument(opts),        opts: { _width: 300, _height: 35, _backgroundGridShow: true, }},
+            {key: "Equation",   creator: opts => Docs.Create.EquationDocument(opts),        opts: { _width: 300, _height: 35, _fitWidth:false, _backgroundGridShow: true, }},
             {key: "Webpage",    creator: opts => Docs.Create.WebDocument("",opts),          opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }},
             {key: "Comparison", creator: Docs.Create.ComparisonDocument,                    opts: { _width: 300, _height: 300 }},
             {key: "Audio",      creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }},
             {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _showSidebar: true,  }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
-            {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
+            {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
             {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }},
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument(opts),         opts: { _width: 300, _height: 300 }},
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index e2f98de1e..9034cacb2 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,10 +1,10 @@
 import React = require("react");
-import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem";
-import { observable, action, computed, runInAction, IReactionDisposer, reaction } from "mobx";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable } from "mobx";
 import { observer } from "mobx-react";
 import "./ContextMenu.scss";
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import Measure from "react-measure";
+import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem";
+import { Utils } from "../../Utils";
 
 @observer
 export class ContextMenu extends React.Component {
@@ -116,10 +116,6 @@ export class ContextMenu extends React.Component {
         this._defaultItem = item;
     }
 
-    getItems() {
-        return this._items;
-    }
-
     static readonly buffer = 20;
     get pageX() {
         const x = this._pageX;
@@ -199,37 +195,25 @@ export class ContextMenu extends React.Component {
 
             return eles;
         };
-        return flattenItems(this._items, name => [name]);
+        return flattenItems(this._items.slice(), name => [name]);
     }
 
     @computed get flatItems(): OriginalMenuProps[] {
         return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[];
     }
 
-    @computed get filteredViews() {
-        const createGroupHeader = (contents: any) => {
-            return (
-                <div className="contextMenu-group">
-                    <div className="contextMenu-description">{contents}</div>
-                </div>
-            );
-        };
-        const createItem = (item: ContextMenuProps, selected: boolean) => <ContextMenuItem {...item} key={item.description} closeMenu={this.closeMenu} selected={selected} />;
-        let itemIndex = 0;
-        return this.filteredItems.map(value => {
-            if (Array.isArray(value)) {
-                return createGroupHeader(value.join(" -> "));
-            } else {
-                return createItem(value, itemIndex++ === this.selectedIndex);
-            }
-        });
-    }
-
     @computed get menuItems() {
         if (!this._searchString) {
             return this._items.map((item, ind) => <ContextMenuItem {...item} noexpand={this.itemsNeedSearch ? true : (item as any).noexpand} key={ind + item.description} closeMenu={this.closeMenu} />);
         }
-        return this.filteredViews;
+        return this.filteredItems.map((value, index) => 
+            Array.isArray(value)  ?
+                <div className="contextMenu-group">
+                    <div className="contextMenu-description">{value.join(" -> ")}</div>
+                </div>
+                :
+                <ContextMenuItem {...value} key={index+value.description} closeMenu={this.closeMenu} selected={index === this.selectedIndex} />
+        );
     }
 
     @computed get itemsNeedSearch() {
@@ -237,14 +221,8 @@ export class ContextMenu extends React.Component {
     }
 
     render() {
-        if (!this._display) {
-            return null;
-        }
-        const style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } :
-            { left: this.pageX, bottom: this.pageY };
-
-        const contents = (
-            <>
+        return !this._display ? (null) : 
+            <div className="contextMenu-cont" style={{left: this.pageX, ...(this._yRelativeToTop ? { top: this.pageY } : { bottom: this.pageY })}}>
                 {!this.itemsNeedSearch ? (null) :
                     <span className={"search-icon"}>
                         <span className="icon-background">
@@ -253,17 +231,7 @@ export class ContextMenu extends React.Component {
                         <input className="contextMenu-item contextMenu-description search" type="text" placeholder="Filter Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus />
                     </span>}
                 {this.menuItems}
-            </>
-        );
-        return (
-            <Measure offset onResize={action((r: any) => { this._width = r.offset.width; this._height = r.offset.height; })}>
-                {({ measureRef }) => (
-                    <div className="contextMenu-cont" style={style} ref={measureRef}>
-                        {contents}
-                    </div>
-                )}
-            </Measure>
-        );
+            </div>;
     }
 
     @action
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 25d00f701..c136de1c3 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -28,11 +28,10 @@ export type ContextMenuProps = OriginalMenuProps | SubmenuProps;
 export class ContextMenuItem extends React.Component<ContextMenuProps & { selected?: boolean }> {
     @observable private _items: Array<ContextMenuProps> = [];
     @observable private overItem = false;
-    @observable private subRef = React.createRef<HTMLDivElement>();
 
-    constructor(props: ContextMenuProps | SubmenuProps) {
-        super(props);
-        if ((this.props as SubmenuProps).subitems) {
+    componentDidMount() {
+        this._items.length = 0;
+        if ((this.props as SubmenuProps)?.subitems) {
             (this.props as SubmenuProps).subitems?.forEach(i => this._items.push(i));
         }
     }
@@ -78,9 +77,6 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
     }
 
     render() {
-
-
-
         if ("event" in this.props) {
             return (
                 <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onPointerDown={this.handleEvent}>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5d2adc321..8d4fd376f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1251,7 +1251,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get Xshift() { return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0; }
     @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 ? (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2 : 0; }
     @computed get centeringX() { return this.props.dontCenter?.includes("x") ? 0 : this.Xshift; }
-    @computed get centeringY() { return this.fitWidth || this.props.dontCenter?.includes("y") ? 0 : this.Yshift; }
+    @computed get centeringY() { return this.props.dontCenter?.includes("y") ? 0 : this.Yshift; }
 
     toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight());
     focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 600730d87..d3dee3c89 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -862,6 +862,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (nh) this.layoutDoc._nativeHeight = scrollHeight;
     }
 
+    @computed get contentScaling() { return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.scaling?.() || 1 : 1;}
     componentDidMount() {
         !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         this._cachedLinks = DocListCast(this.Document.links);
@@ -875,7 +876,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this._disposers.componentHeights = reaction(  // set the document height when one of the component heights changes and autoHeight is on
             () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }),
             ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => {
-                autoHeight && this.props.setHeight?.((this.props.scaling?.() || 1) * (marginsHeight + Math.max(sidebarHeight, textHeight)));
+                autoHeight && this.props.setHeight?.(this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight)));
             }, { fireImmediately: true });
         this._disposers.links = reaction(() => DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
             newLinks => {
-- 
cgit v1.2.3-70-g09d2

