From dd16695b0c5fe8c54bc276a230381ae36e19e5ac Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 7 Jul 2022 13:02:33 -0400
Subject: trying to fix errors in compiles

---
 .../views/collections/CollectionTreeView.tsx       | 321 +++++++++++----------
 1 file changed, 176 insertions(+), 145 deletions(-)

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ba72fb7b9..f5b9162d3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,5 +1,5 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
 import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
@@ -11,27 +11,27 @@ import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnF
 import { DocUtils } from '../../documents/Documents';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { EditableView } from "../EditableView";
+import { EditableView } from '../EditableView';
 import { DocumentView } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView } from "./CollectionSubView";
-import "./CollectionTreeView.scss";
-import { TreeView } from "./TreeView";
-import React = require("react");
-import { FieldViewProps } from "../nodes/FieldView";
-const _global = (window /* browser */ || global /* node */) as any;
+import { CollectionSubView } from './CollectionSubView';
+import './CollectionTreeView.scss';
+import { TreeView } from './TreeView';
+import React = require('react');
+import { FieldViewProps } from '../nodes/FieldView';
+const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionTreeViewProps = {
-    treeViewExpandedView?: "fields" | "layout" | "links" | "data";
+    treeViewExpandedView?: 'fields' | 'layout' | 'links' | 'data';
     treeViewOpen?: boolean;
     treeViewHideTitle?: boolean;
     treeViewHideHeaderFields?: boolean;
@@ -45,9 +45,9 @@ export type collectionTreeViewProps = {
 };
 
 export enum TreeViewType {
-    outline = "outline",
-    fileSystem = "fileSystem",
-    default = "default"
+    outline = 'outline',
+    fileSystem = 'fileSystem',
+    default = 'default',
 }
 
 @observer
@@ -61,13 +61,28 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     private observer: any; // observer for monitoring tree view items.
     private static expandViewLabelSize = 20;
 
-    @computed get doc() { return this.props.Document; }
-    @computed get dataDoc() { return this.props.DataDoc || this.doc; }
-    @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); }
-    @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 === CurrentUserUtils.MyDashboards; }
+    @computed get doc() {
+        return this.props.Document;
+    }
+    @computed get dataDoc() {
+        return this.props.DataDoc || this.doc;
+    }
+    @computed get treeViewtruncateTitleWidth() {
+        return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth());
+    }
+    @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 === CurrentUserUtils.MyDashboards;
+    }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
 
@@ -75,11 +90,11 @@ 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.ActiveTool !== InkTool.None ||
-        (this.props.isContentActive?.() || this.props.Document.forceActive ||
-            this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
-            this.props.rootSelected(outsideReaction)) ? true : false)
+    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
+    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;
 
     componentWillUnmount() {
         this._isDisposing = true;
@@ -89,47 +104,51 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     componentDidMount() {
-        this._disposers.autoheight = reaction(() => this.rootDoc.autoHeight,
+        this._disposers.autoheight = reaction(
+            () => this.rootDoc.autoHeight,
             auto => auto && this.computeHeight(),
-            { fireImmediately: true });
+            { fireImmediately: true }
+        );
     }
 
     computeHeight = () => {
         if (!this._isDisposing) {
-            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace("px", ""));
-            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.marginBot());
+            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', ''));
+            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot());
             this.layoutDoc._autoHeightMargins = bodyHeight;
             this.props.setHeight?.(bodyHeight + titleHeight);
         }
-    }
+    };
     unobserveHeight = (ref: any) => {
         this.refList.delete(ref);
         this.rootDoc.autoHeight && this.computeHeight();
-    }
+    };
     observeHeight = (ref: any) => {
         if (ref) {
             this.refList.add(ref);
-            this.observer = new _global.ResizeObserver(action((entries: any) => {
-                if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
-                    this.computeHeight();
-                }
-            }));
+            this.observer = new _global.ResizeObserver(
+                action((entries: any) => {
+                    if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
+                        this.computeHeight();
+                    }
+                })
+            );
             this.rootDoc.autoHeight && this.computeHeight();
             this.observer.observe(ref);
         }
-    }
+    };
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this._treedropDisposer?.();
-        if (this._mainEle = ele) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
-    }
+        if ((this._mainEle = ele)) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
+    };
 
     protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
         const dragData = de.complete.docDragData;
         if (dragData) {
             const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d));
-            dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction;
+            dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction;
         }
-    }
+    };
 
     @action
     remove = (doc: Doc | Doc[]): boolean => {
@@ -149,7 +168,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             return true;
         }
         return false;
-    }
+    };
 
     @action
     addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
@@ -161,81 +180,85 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             }, true);
         if (this.doc.resolvedDataDoc instanceof Promise) return false;
         return relativeTo === undefined ? this.props.addDocument?.(docs) || false : doAddDoc(docs);
-    }
+    };
     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.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" });
-            layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" });
-            ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
-            const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
-            const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-            onClicks.push({ description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" });
-            !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+            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' });
+            layoutItems.push({ description: (this.doc.treeViewHideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle), icon: 'paint-brush' });
+            ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
+            const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...');
+            const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
+            onClicks.push({ description: 'Edit onChecked Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, 'onCheckedClick'), 'edit onCheckedClick'), icon: 'edit' });
+            !existingOnClick && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
         }
-    }
+    };
     onTreeDrop = (e: React.DragEvent, addDocs?: (docs: Doc[]) => void) => this.onExternalDrop(e, {}, addDocs);
 
     @undoBatch
     makeTextCollection = (childDocs: Doc[]) => {
         this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true);
-    }
+    };
 
     get editableTitle() {
-        return <EditableView
-            contents={this.dataDoc.title}
-            display={"block"}
-            maxHeight={72}
-            height={"auto"}
-            GetValue={() => StrCast(this.dataDoc.title)}
-            SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
-                if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
-                this.dataDoc.title = value;
-                return true;
-            })} />;
+        return (
+            <EditableView
+                contents={this.dataDoc.title}
+                display={'block'}
+                maxHeight={72}
+                height={'auto'}
+                GetValue={() => StrCast(this.dataDoc.title)}
+                SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
+                    if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
+                    this.dataDoc.title = value;
+                    return true;
+                })}
+            />
+        );
     }
 
-
     onKey = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
-        if (this.outlineMode && e.key === "Enter") {
+        if (this.outlineMode && e.key === 'Enter') {
             e.stopPropagation();
             this.makeTextCollection(this.treeChildren);
             return true;
         }
-    }
+    };
     get documentTitle() {
-        return <FormattedTextBox
-            {...this.props}
-            fieldKey={"text"}
-            renderDepth={this.props.renderDepth + 1}
-            isContentActive={this.isContentActive}
-            isDocumentActive={this.isContentActive}
-            rootSelected={returnTrue}
-            forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
-            PanelWidth={this.documentTitleWidth}
-            PanelHeight={this.documentTitleHeight}
-            scaling={returnOne}
-            onKey={this.onKey}
-            docFilters={returnEmptyFilter}
-            docRangeFilters={returnEmptyFilter}
-            searchFilterDocs={returnEmptyDoclist}
-            ContainingCollectionDoc={this.doc}
-            ContainingCollectionView={this.props.CollectionView}
-            addDocument={returnFalse}
-            moveDocument={returnFalse}
-            removeDocument={returnFalse}
-            whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-            bringToFront={returnFalse}
-        />;
+        return (
+            <FormattedTextBox
+                {...this.props}
+                fieldKey={'text'}
+                renderDepth={this.props.renderDepth + 1}
+                isContentActive={this.isContentActive}
+                isDocumentActive={this.isContentActive}
+                rootSelected={returnTrue}
+                forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
+                PanelWidth={this.documentTitleWidth}
+                PanelHeight={this.documentTitleHeight}
+                scaling={returnOne}
+                onKey={this.onKey}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionDoc={this.doc}
+                ContainingCollectionView={this.props.CollectionView}
+                addDocument={returnFalse}
+                moveDocument={returnFalse}
+                removeDocument={returnFalse}
+                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                bringToFront={returnFalse}
+            />
+        );
     }
     childContextMenuItems = () => {
         const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
         const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
-    }
+    };
     @computed get treeViewElements() {
         TraceMobx();
         const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
@@ -266,36 +289,34 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             this.props.treeViewSkipFields,
             true,
             this.whenChildContentsActiveChanged,
-            this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null),
+            this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, 'boolean', null),
             this.observeHeight,
             this.unobserveHeight,
             this.childContextMenuItems(),
             //TODO: [AL] add these
             this.props.AddToMap,
             this.props.RemFromMap,
-            this.props.hierarchyIndex,
+            this.props.hierarchyIndex
         );
     }
     @computed get titleBar() {
-        return this.dataDoc === null ? (null) :
-            <div className="collectionTreeView-titleBar" key={this.doc[Id]}
-                style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}}
-                ref={r => this._titleRef = r}>
+        return this.dataDoc === null ? null : (
+            <div className="collectionTreeView-titleBar" key={this.doc[Id]} style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}} ref={r => (this._titleRef = r)}>
                 {this.outlineMode ? this.documentTitle : this.editableTitle}
-            </div>;
+            </div>
+        );
     }
 
     @computed get noviceExplainer() {
-        return !Doc.noviceMode || !this.rootDoc.explainer ? (null) :
-            <div className="documentExplanation"> {this.rootDoc.explainer} </div>;
+        return !Doc.noviceMode || !this.rootDoc.explainer ? null : <div className="documentExplanation"> {StrCast(this.rootDoc.explainer)} </div>;
     }
 
     return35 = () => 35;
     @computed get buttonMenu() {
         const menuDoc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
         // To create a multibutton menu add a CollectionLinearView
-        return !menuDoc ? null :
-            (<div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+        return !menuDoc ? null : (
+            <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
                 <DocumentView
                     Document={menuDoc}
                     DataDoc={menuDoc}
@@ -322,11 +343,16 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                 />
-            </div>);
+            </div>
+        );
     }
 
-    @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
-    @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
+    @computed get nativeWidth() {
+        return Doc.NativeWidth(this.Document, undefined, true);
+    }
+    @computed get nativeHeight() {
+        return Doc.NativeHeight(this.Document, undefined, true);
+    }
 
     @computed get contentScaling() {
         const nw = this.nativeWidth;
@@ -347,68 +373,73 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
     remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
     moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
-        this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false
+        this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false;
 
     contentFunc = () => {
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
-        const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined;
-        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : this.titleBar;
+        const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined);
+        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
         return [
-            <div className="collectionTreeView-contents" key="tree" style={{
-                ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
-                overflow: "auto",
-                height: "100%"//this.layoutDoc._autoHeight ? "max-content" : "100%"
-            }} >
+            <div
+                className="collectionTreeView-contents"
+                key="tree"
+                style={{
+                    ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
+                    overflow: 'auto',
+                    height: '100%', //this.layoutDoc._autoHeight ? "max-content" : "100%"
+                }}>
                 {titleBar}
-                <div className="collectionTreeView-container"
+                <div
+                    className="collectionTreeView-container"
                     style={{
-                        transform: this.outlineMode ? `scale(${this.contentScaling})` : "",
+                        transform: this.outlineMode ? `scale(${this.contentScaling})` : '',
                         paddingLeft: `${this.marginX()}px`,
-                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : ""
+                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : '',
                     }}
                     onContextMenu={this.onContextMenu}>
-                    {!this.buttonMenu && !this.noviceExplainer ? (null) :
+                    {!this.buttonMenu && !this.noviceExplainer ? null : (
                         <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
                             {this.buttonMenu}
                             {this.noviceExplainer}
                         </div>
-                    }
-                    <div className="collectionTreeView-dropTarget"
+                    )}
+                    <div
+                        className="collectionTreeView-dropTarget"
                         style={{
                             background: background(),
                             height: `calc(100% - ${this._explainerHeight}px)`,
-                            pointerEvents: pointerEvents()
+                            pointerEvents: pointerEvents(),
                         }}
                         onWheel={e => e.stopPropagation()}
                         onDrop={this.onTreeDrop}
                         ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>
-                        <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
-                            {this.treeViewElements}
-                        </ul>
-                    </div >
+                        <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
+                    </div>
                 </div>
-            </div>
+            </div>,
         ];
-    }
+    };
     render() {
         TraceMobx();
 
-        return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
-            this.doc.treeViewHasOverlay ?
-                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                    isAnnotationOverlay={true}
-                    isAnnotationOverlayScrollable={true}
-                    childDocumentsActive={this.props.isDocumentActive}
-                    fieldKey={this.props.fieldKey + "-annotations"}
-                    dropAction={"move"}
-                    select={emptyFunction}
-                    addDocument={this.addAnnotationDocument}
-                    removeDocument={this.remAnnotationDocument}
-                    moveDocument={this.moveAnnotationDocument}
-                    bringToFront={emptyFunction}
-                    renderDepth={this.props.renderDepth + 1} >
-                    {this.contentFunc}
-                </CollectionFreeFormView> :
-                this.contentFunc();
+        return !(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
+            <CollectionFreeFormView
+                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                isAnnotationOverlay={true}
+                isAnnotationOverlayScrollable={true}
+                childDocumentsActive={this.props.isDocumentActive}
+                fieldKey={this.props.fieldKey + '-annotations'}
+                dropAction={'move'}
+                select={emptyFunction}
+                addDocument={this.addAnnotationDocument}
+                removeDocument={this.remAnnotationDocument}
+                moveDocument={this.moveAnnotationDocument}
+                bringToFront={emptyFunction}
+                renderDepth={this.props.renderDepth + 1}>
+                {this.contentFunc}
+            </CollectionFreeFormView>
+        ) : (
+            this.contentFunc()
+        );
     }
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3-70-g09d2


From 146f8622d5bac2edc6b09f57c173bd057dfbcfad Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 8 Jul 2022 00:17:26 -0400
Subject: restructured currentUserUtils to avoid having import cycles.

---
 package-lock.json                                  |   39 -
 src/client/DocServer.ts                            |   91 +-
 src/client/apis/youtube/YoutubeBox.tsx             |  281 ++--
 src/client/documents/DocumentTypes.ts              |  100 +-
 src/client/documents/Documents.ts                  | 1494 ++++++++++-------
 src/client/util/CurrentUserUtils.ts                |  349 +---
 src/client/util/DocumentManager.ts                 |  184 ++-
 src/client/util/History.ts                         |   45 +-
 src/client/util/LinkFollower.ts                    |  112 ++
 src/client/util/LinkManager.ts                     |  259 ++-
 src/client/util/SelectionManager.ts                |   29 +-
 src/client/util/SettingsManager.tsx                |  471 +++---
 src/client/util/SharingManager.tsx                 |   11 +-
 src/client/views/DashboardView.tsx                 |  400 +++--
 src/client/views/DocComponent.tsx                  |  143 +-
 src/client/views/DocumentButtonBar.tsx             |  449 +++--
 src/client/views/DocumentDecorations.tsx           |   68 +-
 src/client/views/GestureOverlay.tsx                |  740 +++++----
 src/client/views/GlobalKeyHandler.ts               |  279 ++--
 src/client/views/InkStrokeProperties.ts            |  278 ++--
 src/client/views/InkTranscription.tsx              |    3 +-
 src/client/views/LightboxView.tsx                  |  258 +--
 src/client/views/Main.tsx                          |    2 +-
 src/client/views/MainView.tsx                      |   65 +-
 src/client/views/MarqueeAnnotator.tsx              |  226 +--
 src/client/views/OverlayView.tsx                   |  197 ++-
 src/client/views/PreviewCursor.tsx                 |  200 ++-
 src/client/views/PropertiesButtons.tsx             |  386 +++--
 src/client/views/PropertiesDocContextSelector.tsx  |   67 +-
 src/client/views/PropertiesView.tsx                | 1729 +++++++++++---------
 src/client/views/SidebarAnnos.tsx                  |  128 +-
 src/client/views/StyleProvider.tsx                 |   11 +-
 src/client/views/TemplateMenu.tsx                  |  189 +--
 .../views/collections/CollectionDockingView.tsx    |   25 +-
 src/client/views/collections/CollectionMenu.tsx    | 1246 +++++++-------
 .../collections/CollectionStackedTimeline.tsx      |  716 ++++----
 .../views/collections/CollectionStackingView.tsx   |    6 +-
 src/client/views/collections/CollectionSubView.tsx |  286 ++--
 .../views/collections/CollectionTreeView.tsx       |    9 +-
 src/client/views/collections/CollectionView.tsx    |   75 +-
 src/client/views/collections/TabDocView.tsx        |   16 +-
 src/client/views/collections/TreeView.tsx          | 1168 +++++++------
 .../CollectionFreeFormLayoutEngines.tsx            |  248 ++-
 .../CollectionFreeFormLinkView.tsx                 |  201 ++-
 .../CollectionFreeFormRemoteCursors.tsx            |  105 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   28 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  517 +++---
 .../collectionLinear/CollectionLinearView.tsx      |    9 +-
 src/client/views/linking/LinkMenuItem.tsx          |    3 +-
 src/client/views/linking/LinkPopup.tsx             |   31 +-
 src/client/views/nodes/AudioBox.tsx                |  613 +++----
 src/client/views/nodes/ColorBox.tsx                |   96 +-
 src/client/views/nodes/DocumentView.tsx            | 1460 ++++++++++-------
 src/client/views/nodes/FilterBox.tsx               |  486 +++---
 src/client/views/nodes/ImageBox.tsx                |  362 ++--
 src/client/views/nodes/LinkAnchorBox.tsx           |  167 +-
 src/client/views/nodes/LinkDocPreview.tsx          |    3 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |    5 +-
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |  111 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  489 +++---
 src/client/views/nodes/VideoBox.tsx                |  958 ++++++-----
 src/client/views/nodes/WebBox.tsx                  |  818 +++++----
 src/client/views/nodes/button/FontIconBox.tsx      |   25 +-
 .../views/nodes/formattedText/DashDocView.tsx      |   15 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |    2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   11 +-
 src/client/views/nodes/trails/PresBox.tsx          |   48 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  440 ++---
 src/client/views/pdf/Annotation.tsx                |  108 +-
 src/client/views/pdf/PDFViewer.tsx                 |  455 +++---
 src/client/views/topbar/TopBar.tsx                 |  180 +-
 src/client/views/webcam/DashWebRTCVideo.tsx        |   88 +-
 src/fields/Doc.ts                                  |  151 +-
 src/mobile/MobileInterface.tsx                     |   19 +-
 src/mobile/MobileMain.tsx                          |   32 +-
 75 files changed, 11685 insertions(+), 9429 deletions(-)
 create mode 100644 src/client/util/LinkFollower.ts

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

diff --git a/package-lock.json b/package-lock.json
index ed3160696..3b9cda8bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5059,16 +5059,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6218,28 +6208,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.61",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
-            "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6251,7 +6219,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -20748,12 +20715,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index c9a30b8e3..1b0ba6bc3 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,27 +1,27 @@
-import * as io from 'socket.io-client';
-import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message";
-import { Opt, Doc, UpdatingFromServer, updateCachedAcls } from '../fields/Doc';
-import { Utils, emptyFunction } from '../Utils';
-import { SerializationHelper } from './util/SerializationHelper';
-import { RefField } from '../fields/RefField';
-import { Id, HandleUpdate, Parent } from '../fields/FieldSymbols';
-import { GestureOverlay } from './views/GestureOverlay';
-import MobileInkOverlay from '../mobile/MobileInkOverlay';
 import { runInAction } from 'mobx';
+import * as rp from 'request-promise';
+import * as io from 'socket.io-client';
+import { Doc, Opt, UpdatingFromServer } from '../fields/Doc';
+import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols';
 import { ObjectField } from '../fields/ObjectField';
+import { RefField } from '../fields/RefField';
 import { StrCast } from '../fields/Types';
-import * as rp from 'request-promise';
+import MobileInkOverlay from '../mobile/MobileInkOverlay';
+import { emptyFunction, Utils } from '../Utils';
+import { GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message';
+import { SerializationHelper } from './util/SerializationHelper';
+import { GestureOverlay } from './views/GestureOverlay';
 
 /**
  * This class encapsulates the transfer and cross-client synchronization of
  * data stored only in documents (RefFields). In the process, it also
  * creates and maintains a cache of documents so that they can be accessed
  * more efficiently. Currently, there is no cache eviction scheme in place.
- * 
+ *
  * NOTE: while this class is technically abstracted to work with any [RefField], because
  * [Doc] instances are the only [RefField] we need / have implemented at the moment, the documentation
  * will treat all data used here as [Doc]s
- * 
+ *
  * Any time we want to write a new field to the database (via the server)
  * or update ourselves based on the server's update message, that occurs here
  */
@@ -32,12 +32,12 @@ export namespace DocServer {
         const strings: string[] = [];
         Array.from(Object.keys(_cache)).forEach(key => {
             const doc = _cache[key];
-            if (doc instanceof Doc) strings.push(StrCast(doc.author) + " " + StrCast(doc.title) + " " + StrCast(Doc.GetT(doc, "title", "string", true)));
+            if (doc instanceof Doc) strings.push(StrCast(doc.author) + ' ' + StrCast(doc.title) + ' ' + StrCast(Doc.GetT(doc, 'title', 'string', true)));
         });
-        print && strings.sort().forEach((str, i) => console.log(i.toString() + " " + str));
-        rp.post(Utils.prepend("/setCacheDocumentIds"), {
+        print && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
+        rp.post(Utils.prepend('/setCacheDocumentIds'), {
             body: {
-                cacheDocumentIds: Array.from(Object.keys(_cache)).join(";"),
+                cacheDocumentIds: Array.from(Object.keys(_cache)).join(';'),
             },
             json: true,
         });
@@ -50,8 +50,8 @@ export namespace DocServer {
     export enum WriteMode {
         Default = 0, //Anything goes
         Playground = 1, //Playground (write own/no read other updates)
-        LiveReadonly = 2,//Live Readonly (no write/read others)
-        LivePlayground = 3,//Live Playground (write own/read others)
+        LiveReadonly = 2, //Live Readonly (no write/read others)
+        LivePlayground = 3, //Live Playground (write own/read others)
     }
     const fieldWriteModes: { [field: string]: WriteMode } = {};
     const docsWithUpdates: { [field: string]: Set<Doc> } = {};
@@ -62,7 +62,7 @@ export namespace DocServer {
         livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
     }
     export function IsPlaygroundField(field: string) {
-        return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ""));
+        return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ''));
     }
 
     export function setFieldWriteMode(field: string, writeMode: WriteMode) {
@@ -83,7 +83,7 @@ export namespace DocServer {
     export function registerDocWithCachedUpdate(doc: Doc, field: string, oldValue: any) {
         let list = docsWithUpdates[field];
         if (!list) {
-            list = docsWithUpdates[field] = new Set;
+            list = docsWithUpdates[field] = new Set();
         }
         if (!list.has(doc)) {
             Doc.AddCachedUpdate(doc, field, oldValue);
@@ -92,7 +92,6 @@ export namespace DocServer {
     }
 
     export namespace Mobile {
-
         export function dispatchGesturePoints(content: GestureContent) {
             Utils.Emit(_socket, MessageStore.GesturePoints, content);
         }
@@ -111,17 +110,17 @@ export namespace DocServer {
         }
     }
 
-    const instructions = "This page will automatically refresh after this alert is closed. Expect to reconnect after about 30 seconds.";
+    const instructions = 'This page will automatically refresh after this alert is closed. Expect to reconnect after about 30 seconds.';
     function alertUser(connectionTerminationReason: string) {
         switch (connectionTerminationReason) {
-            case "crash":
+            case 'crash':
                 alert(`Dash has temporarily crashed. Administrators have been notified and the server is restarting itself. ${instructions}`);
                 break;
-            case "temporary":
+            case 'temporary':
                 alert(`An administrator has chosen to restart the server. ${instructions}`);
                 break;
-            case "exit":
-                alert("An administrator has chosen to kill the server. Do not expect to reconnect until administrators start the server.");
+            case 'exit':
+                alert('An administrator has chosen to kill the server. Do not expect to reconnect until administrators start the server.');
                 break;
             default:
                 console.log(`Received an unknown ConnectionTerminated message: ${connectionTerminationReason}`);
@@ -132,7 +131,7 @@ export namespace DocServer {
     export function init(protocol: string, hostname: string, port: number, identifier: string) {
         _cache = {};
         GUID = identifier;
-        protocol = protocol.startsWith("https") ? "wss" : "ws";
+        protocol = protocol.startsWith('https') ? 'wss' : 'ws';
         _socket = io.connect(`${protocol}://${hostname}:${port}`);
         // io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket
 
@@ -153,17 +152,17 @@ export namespace DocServer {
         Utils.AddServerHandler(_socket, MessageStore.ConnectionTerminated, alertUser);
 
         // mobile ink overlay socket events to communicate between mobile view and desktop view
-        _socket.addEventListener("receiveGesturePoints", (content: GestureContent) => {
+        _socket.addEventListener('receiveGesturePoints', (content: GestureContent) => {
             MobileInkOverlay.Instance.drawStroke(content);
         });
-        _socket.addEventListener("receiveOverlayTrigger", (content: MobileInkOverlayContent) => {
+        _socket.addEventListener('receiveOverlayTrigger', (content: MobileInkOverlayContent) => {
             GestureOverlay.Instance.enableMobileInkOverlay(content);
             MobileInkOverlay.Instance.initMobileInkOverlay(content);
         });
-        _socket.addEventListener("receiveUpdateOverlayPosition", (content: UpdateMobileInkOverlayPositionContent) => {
+        _socket.addEventListener('receiveUpdateOverlayPosition', (content: UpdateMobileInkOverlayPositionContent) => {
             MobileInkOverlay.Instance.updatePosition(content);
         });
-        _socket.addEventListener("receiveMobileDocumentUpload", (content: MobileDocumentUploadContent) => {
+        _socket.addEventListener('receiveMobileDocumentUpload', (content: MobileDocumentUploadContent) => {
             MobileInkOverlay.Instance.uploadDocument(content);
         });
     }
@@ -173,12 +172,11 @@ export namespace DocServer {
     }
 
     export namespace Control {
-
         let _isReadOnly = false;
         export function makeReadOnly() {
             if (!_isReadOnly) {
                 _isReadOnly = true;
-                _CreateField = field => _cache[field[Id]] = field;
+                _CreateField = field => (_cache[field[Id]] = field);
                 _UpdateField = emptyFunction;
                 _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB
             }
@@ -195,8 +193,9 @@ export namespace DocServer {
             }
         }
 
-        export function isReadOnly() { return _isReadOnly; }
-
+        export function isReadOnly() {
+            return _isReadOnly;
+        }
     }
 
     /**
@@ -209,7 +208,6 @@ export namespace DocServer {
     }
 
     export namespace Util {
-
         /**
          * Emits a message to the server that wipes
          * all documents in the database.
@@ -217,7 +215,6 @@ export namespace DocServer {
         export function deleteDatabase() {
             Utils.Emit(_socket, MessageStore.DeleteAll, {});
         }
-
     }
 
     // RETRIEVE DOCS FROM SERVER
@@ -258,8 +255,7 @@ export namespace DocServer {
                     });
                     cached[UpdatingFromServer] = false;
                     return cached;
-                }
-                else if (field !== undefined) {
+                } else if (field !== undefined) {
                     _cache[id] = field;
                 } else {
                     delete _cache[id];
@@ -272,7 +268,7 @@ export namespace DocServer {
             // here, indicate that the document associated with this id is currently
             // being retrieved and cached
             !force && (_cache[id] = deserializeField);
-            return force ? cached as any : deserializeField;
+            return force ? (cached as any) : deserializeField;
         } else if (cached instanceof Promise) {
             // BEING RETRIEVED AND CACHED => some other caller previously (likely recently) called GetRefField(s),
             // and requested the document I'm looking for. Shouldn't fetch again, just
@@ -316,7 +312,6 @@ export namespace DocServer {
         Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.VideoDetails, videoIds: videoIds }, callBack);
     }
 
-
     /**
      * Given a list of Doc GUIDs, this utility function will asynchronously attempt to each id's associated
      * field, first looking in the RefField cache and then communicating with
@@ -381,7 +376,7 @@ export namespace DocServer {
                                 return deserialized;
                             });
                             // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
-                            // we set the value at the field's id to a promise that will resolve to the field. 
+                            // we set the value at the field's id to a promise that will resolve to the field.
                             // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
                             // The mapping in the .then call ensures that when other callers await these promises, they'll
                             // get the resolved field
@@ -391,7 +386,7 @@ export namespace DocServer {
                             proms.push(prom);
                         } else if (cached instanceof Promise) {
                             proms.push(cached as any);
-                            cached.then((f: any) => fieldMap[field.id] = f);
+                            cached.then((f: any) => (fieldMap[field.id] = f));
                         } else if (field) {
                             proms.push(cached as any);
                             fieldMap[field.id] = DocServer.GetCachedRefField(field.id) || field;
@@ -413,20 +408,19 @@ export namespace DocServer {
                 const field = fields[id];
                 map[id] = field;
             });
-
         }
 
         // 7) those promises we encountered in the else if of 1), which represent
         // other callers having already submitted a request to the server for (a) document(s)
         // in which we're interested, must still be awaited so that we can return the proper
-        // values for those as well. 
+        // values for those as well.
         //
         // fortunately, those other callers will also hit their own version of 6) and clean up
         // the shared cache when these promises resolve, so all we have to do is...
         const otherCallersFetching = await Promise.all(promises);
         // ...extract the RefFields returned from the resolution of those promises and add them to our
         // own map.
-        waitingIds.forEach((id, index) => map[id] = otherCallersFetching[index]);
+        waitingIds.forEach((id, index) => (map[id] = otherCallersFetching[index]));
 
         // now, we return our completed mapping from all of the ids that were passed into the method
         // to their actual RefField | undefined values. This return value either becomes the input
@@ -476,7 +470,7 @@ export namespace DocServer {
     }
 
     function _UpdateFieldImpl(id: string, diff: any) {
-        (!DocServer.Control.isReadOnly()) && Utils.Emit(_socket, MessageStore.UpdateField, { id, diff });
+        !DocServer.Control.isReadOnly() && Utils.Emit(_socket, MessageStore.UpdateField, { id, diff });
     }
 
     let _UpdateField: (id: string, diff: any) => void = errorFunc;
@@ -520,12 +514,11 @@ export namespace DocServer {
         Utils.Emit(_socket, MessageStore.DeleteFields, ids);
     }
 
-
     function _respondToDeleteImpl(ids: string | string[]) {
         function deleteId(id: string) {
             delete _cache[id];
         }
-        if (typeof ids === "string") {
+        if (typeof ids === 'string') {
             deleteId(ids);
         } else if (Array.isArray(ids)) {
             ids.map(deleteId);
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index e14dc60b4..05879a247 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -1,17 +1,16 @@
 import { action, observable, runInAction } from 'mobx';
-import { observer } from "mobx-react";
-import { Doc, DocListCastAsync } from "../../../fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { Utils } from "../../../Utils";
-import { DocServer } from "../../DocServer";
-import { Docs } from "../../documents/Documents";
-import { DocumentDecorations } from "../../views/DocumentDecorations";
-import { FieldView, FieldViewProps } from "../../views/nodes/FieldView";
-import "../../views/nodes/WebBox.scss";
-import "./YoutubeBox.scss";
-import React = require("react");
+import { observer } from 'mobx-react';
+import { Doc, DocListCastAsync } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Utils } from '../../../Utils';
+import { DocServer } from '../../DocServer';
+import { Docs } from '../../documents/Documents';
+import { DocumentDecorations } from '../../views/DocumentDecorations';
+import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
+import '../../views/nodes/WebBox.scss';
+import './YoutubeBox.scss';
+import React = require('react');
 
 interface VideoTemplate {
     thumbnailUrl: string;
@@ -29,19 +28,19 @@ interface VideoTemplate {
  */
 @observer
 export class YoutubeBox extends React.Component<FieldViewProps> {
-
     @observable YoutubeSearchElement: HTMLInputElement | undefined;
     @observable searchResultsFound: boolean = false;
     @observable searchResults: any[] = [];
     @observable videoClicked: boolean = false;
-    @observable selectedVideoUrl: string = "";
+    @observable selectedVideoUrl: string = '';
     @observable lisOfBackUp: JSX.Element[] = [];
     @observable videoIds: string | undefined;
     @observable videoDetails: any[] = [];
     @observable curVideoTemplates: VideoTemplate[] = [];
 
-
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(YoutubeBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(YoutubeBox, fieldKey);
+    }
 
     /**
      * When component mounts, last search's results are laoded in based on the back up stored
@@ -54,19 +53,15 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         const castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc);
         const awaitedDetails = await castedDetailBackUp;
 
-
         if (awaitedBackUp) {
-
-
             const jsonList = await DocListCastAsync(awaitedBackUp.json);
             const jsonDetailList = await DocListCastAsync(awaitedDetails!.json);
 
             if (jsonList!.length !== 0) {
-                runInAction(() => this.searchResultsFound = true);
+                runInAction(() => (this.searchResultsFound = true));
                 let index = 0;
                 //getting the necessary information from backUps and building templates that will be used to map in render
                 for (const video of jsonList!) {
-
                     const videoId = await Cast(video.id, Doc);
                     const id = StrCast(videoId!.videoId);
                     const snippet = await Cast(video.snippet, Doc);
@@ -75,10 +70,10 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
                     const thumbnailMedium = await Cast(thumbnail!.medium, Doc);
                     const thumbnailUrl = StrCast(thumbnailMedium!.url);
                     const videoDescription = StrCast(snippet!.description);
-                    const pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!;
+                    const pusblishDate = this.roundPublishTime(StrCast(snippet!.publishedAt))!;
                     const channelTitle = StrCast(snippet!.channelTitle);
-                    let duration: string = "";
-                    let viewCount: string = "";
+                    let duration: string = '';
+                    let viewCount: string = '';
                     if (jsonDetailList!.length !== 0) {
                         const contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc);
                         const statistics = await Cast(jsonDetailList![index].statistics, Doc);
@@ -86,7 +81,16 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
                         viewCount = this.abbreviateViewCount(parseInt(StrCast(statistics!.viewCount)))!;
                     }
                     index = index + 1;
-                    const newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration, viewCount: viewCount };
+                    const newTemplate: VideoTemplate = {
+                        videoId: id,
+                        videoTitle: videoTitle,
+                        thumbnailUrl: thumbnailUrl,
+                        publishDate: pusblishDate,
+                        channelTitle: channelTitle,
+                        videoDescription: videoDescription,
+                        duration: duration,
+                        viewCount: viewCount,
+                    };
                     runInAction(() => this.curVideoTemplates.push(newTemplate));
                 }
             }
@@ -96,20 +100,20 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
     _ignore = 0;
     onPreWheel = (e: React.WheelEvent) => {
         this._ignore = e.timeStamp;
-    }
+    };
     onPrePointer = (e: React.PointerEvent) => {
         this._ignore = e.timeStamp;
-    }
+    };
     onPostPointer = (e: React.PointerEvent) => {
         if (this._ignore !== e.timeStamp) {
             e.stopPropagation();
         }
-    }
+    };
     onPostWheel = (e: React.WheelEvent) => {
         if (this._ignore !== e.timeStamp) {
             e.stopPropagation();
         }
-    }
+    };
 
     /**
      * Function that submits the title entered by user on enter press.
@@ -117,12 +121,11 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
     onEnterKeyDown = (e: React.KeyboardEvent) => {
         if (e.keyCode === 13) {
             const submittedTitle = this.YoutubeSearchElement!.value;
-            this.YoutubeSearchElement!.value = "";
+            this.YoutubeSearchElement!.value = '';
             this.YoutubeSearchElement!.blur();
             DocServer.getYoutubeVideos(submittedTitle, this.processesVideoResults);
-
         }
-    }
+    };
 
     /**
      * The callback that is passed in to server, which functions as a way to
@@ -134,12 +137,12 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         this.searchResults = videos;
         if (this.searchResults.length > 0) {
             this.searchResultsFound = true;
-            this.videoIds = "";
-            videos.forEach((video) => {
-                if (this.videoIds === "") {
+            this.videoIds = '';
+            videos.forEach(video => {
+                if (this.videoIds === '') {
                     this.videoIds = video.id.videoId;
                 } else {
-                    this.videoIds = this.videoIds! + ", " + video.id.videoId;
+                    this.videoIds = this.videoIds! + ', ' + video.id.videoId;
                 }
             });
             //Asking for details that include duration and viewCount from server for videoIds
@@ -149,7 +152,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
                 this.videoClicked = false;
             }
         }
-    }
+    };
 
     /**
      * The callback that is given to server to process and receive returned details about the videos.
@@ -157,28 +160,26 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
     @action
     processVideoDetails = (videoDetails: any[]) => {
         this.videoDetails = videoDetails;
-        this.props.Document.cachedDetails = Doc.Get.FromJson({ data: videoDetails, title: "detailBackUp" });
-    }
+        this.props.Document.cachedDetails = Doc.Get.FromJson({ data: videoDetails, title: 'detailBackUp' });
+    };
 
     /**
      * The function that stores the search results in the props document.
      */
     backUpSearchResults = (videos: any[]) => {
-        this.props.Document.cachedSearchResults = Doc.Get.FromJson({ data: videos, title: "videosBackUp" });
-    }
+        this.props.Document.cachedSearchResults = Doc.Get.FromJson({ data: videos, title: 'videosBackUp' });
+    };
 
     /**
      * The function that filters out escaped characters returned by the api
      * in the title of the videos.
      */
     filterYoutubeTitleResult = (resultTitle: string) => {
-        let processedTitle: string = resultTitle.replace(/&amp;/g, "&");//.ReplaceAll("&amp;", "&");
+        let processedTitle: string = resultTitle.replace(/&amp;/g, '&'); //.ReplaceAll("&amp;", "&");
         processedTitle = processedTitle.replace(/"&#39;/g, "'");
-        processedTitle = processedTitle.replace(/&quot;/g, "\"");
+        processedTitle = processedTitle.replace(/&quot;/g, '"');
         return processedTitle;
-    }
-
-
+    };
 
     /**
      * The function that converts ISO date, which is passed in, to normal date and finds the
@@ -195,7 +196,6 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         const totalMonths = totalDays / 30.417;
         const totalYears = totalMonths / 12;
 
-
         const truncYears = Math.trunc(totalYears);
         const truncMonths = Math.trunc(totalMonths);
         const truncDays = Math.trunc(totalDays);
@@ -203,62 +203,61 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         const truncMin = Math.trunc(totalMin);
         const truncSec = Math.trunc(totalSeconds);
 
-        let pluralCase = "";
+        let pluralCase = '';
 
         if (truncYears !== 0) {
-            truncYears > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncYears + " year" + pluralCase + " ago";
+            truncYears > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncYears + ' year' + pluralCase + ' ago';
         } else if (truncMonths !== 0) {
-            truncMonths > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncMonths + " month" + pluralCase + " ago";
+            truncMonths > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncMonths + ' month' + pluralCase + ' ago';
         } else if (truncDays !== 0) {
-            truncDays > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncDays + " day" + pluralCase + " ago";
+            truncDays > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncDays + ' day' + pluralCase + ' ago';
         } else if (truncHours !== 0) {
-            truncHours > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncHours + " hour" + pluralCase + " ago";
+            truncHours > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncHours + ' hour' + pluralCase + ' ago';
         } else if (truncMin !== 0) {
-            truncMin > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncMin + " minute" + pluralCase + " ago";
+            truncMin > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncMin + ' minute' + pluralCase + ' ago';
         } else if (truncSec !== 0) {
-            truncSec > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncSec + " second" + pluralCase + " ago";
+            truncSec > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncSec + ' second' + pluralCase + ' ago';
         }
-    }
+    };
 
     /**
      * The function that converts the passed in ISO time to normal duration time.
      */
     convertIsoTimeToDuration = (isoDur: string) => {
-
-        const convertedTime = isoDur.replace(/D|H|M/g, ":").replace(/P|T|S/g, "").split(":");
+        const convertedTime = isoDur.replace(/D|H|M/g, ':').replace(/P|T|S/g, '').split(':');
 
         if (1 === convertedTime.length) {
-            2 !== convertedTime[0].length && (convertedTime[0] = "0" + convertedTime[0]), convertedTime[0] = "0:" + convertedTime[0];
+            2 !== convertedTime[0].length && (convertedTime[0] = '0' + convertedTime[0]), (convertedTime[0] = '0:' + convertedTime[0]);
         } else {
             for (var r = 1, l = convertedTime.length - 1; l >= r; r++) {
-                2 !== convertedTime[r].length && (convertedTime[r] = "0" + convertedTime[r]);
+                2 !== convertedTime[r].length && (convertedTime[r] = '0' + convertedTime[r]);
             }
         }
 
-        return convertedTime.join(":");
-    }
+        return convertedTime.join(':');
+    };
 
     /**
-     * The function that rounds the viewCount to the nearest 
+     * The function that rounds the viewCount to the nearest
      * thousand, million or billion, given a viewCount number.
      */
     abbreviateViewCount = (viewCount: number) => {
         if (viewCount < 1000) {
             return viewCount.toString();
         } else if (viewCount >= 1000 && viewCount < 1000000) {
-            return (Math.trunc(viewCount / 1000)) + "K";
+            return Math.trunc(viewCount / 1000) + 'K';
         } else if (viewCount >= 1000000 && viewCount < 1000000000) {
-            return (Math.trunc(viewCount / 1000000)) + "M";
+            return Math.trunc(viewCount / 1000000) + 'M';
         } else if (viewCount >= 1000000000) {
-            return (Math.trunc(viewCount / 1000000000)) + "B";
+            return Math.trunc(viewCount / 1000000000) + 'B';
         }
-    }
+    };
 
     /**
      * The function that is called to decide on what'll be rendered by the component.
@@ -268,63 +267,69 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
     renderSearchResultsOrVideo = () => {
         if (this.searchResultsFound) {
             if (this.searchResults.length !== 0) {
-                return <ul>
-                    {this.searchResults.map((video, index) => {
-                        const filteredTitle = this.filterYoutubeTitleResult(video.snippet.title);
-                        const channelTitle = video.snippet.channelTitle;
-                        const videoDescription = video.snippet.description;
-                        const pusblishDate = this.roundPublishTime(video.snippet.publishedAt);
-                        let duration;
-                        let viewCount;
-                        if (this.videoDetails.length !== 0) {
-                            duration = this.convertIsoTimeToDuration(this.videoDetails[index].contentDetails.duration);
-                            viewCount = this.abbreviateViewCount(this.videoDetails[index].statistics.viewCount);
-                        }
-
-
-                        return <li onClick={() => this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}>
-                            <div className="search_wrapper">
-                                <div style={{ backgroundColor: "yellow" }}>
-                                    <img src={video.snippet.thumbnails.medium.url} />
-                                    <span className="video_duration">{duration}</span>
-                                </div>
-                                <div className="textual_info">
-                                    <span className="videoTitle">{filteredTitle}</span>
-                                    <span className="channelName">{channelTitle}</span>
-                                    <span className="viewCount">{viewCount}</span>
-                                    <span className="publish_time">{pusblishDate}</span>
-                                    <p className="video_description">{videoDescription}</p>
-
-                                </div>
-                            </div>
-                        </li>;
-                    })}
-                </ul>;
+                return (
+                    <ul>
+                        {this.searchResults.map((video, index) => {
+                            const filteredTitle = this.filterYoutubeTitleResult(video.snippet.title);
+                            const channelTitle = video.snippet.channelTitle;
+                            const videoDescription = video.snippet.description;
+                            const pusblishDate = this.roundPublishTime(video.snippet.publishedAt);
+                            let duration;
+                            let viewCount;
+                            if (this.videoDetails.length !== 0) {
+                                duration = this.convertIsoTimeToDuration(this.videoDetails[index].contentDetails.duration);
+                                viewCount = this.abbreviateViewCount(this.videoDetails[index].statistics.viewCount);
+                            }
+
+                            return (
+                                <li onClick={() => this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}>
+                                    <div className="search_wrapper">
+                                        <div style={{ backgroundColor: 'yellow' }}>
+                                            <img src={video.snippet.thumbnails.medium.url} />
+                                            <span className="video_duration">{duration}</span>
+                                        </div>
+                                        <div className="textual_info">
+                                            <span className="videoTitle">{filteredTitle}</span>
+                                            <span className="channelName">{channelTitle}</span>
+                                            <span className="viewCount">{viewCount}</span>
+                                            <span className="publish_time">{pusblishDate}</span>
+                                            <p className="video_description">{videoDescription}</p>
+                                        </div>
+                                    </div>
+                                </li>
+                            );
+                        })}
+                    </ul>
+                );
             } else if (this.curVideoTemplates.length !== 0) {
-                return <ul>
-                    {this.curVideoTemplates.map((video: VideoTemplate) => {
-                        return <li onClick={() => this.embedVideoOnClick(video.videoId, video.videoTitle)} key={Utils.GenerateGuid()}>
-                            <div className="search_wrapper">
-                                <div style={{ backgroundColor: "yellow" }}>
-                                    <img src={video.thumbnailUrl} />
-                                    <span className="video_duration">{video.duration}</span>
-                                </div>
-                                <div className="textual_info">
-                                    <span className="videoTitle">{video.videoTitle}</span>
-                                    <span className="channelName">{video.channelTitle}</span>
-                                    <span className="viewCount">{video.viewCount}</span>
-                                    <span className="publish_time">{video.publishDate}</span>
-                                    <p className="video_description">{video.videoDescription}</p>
-                                </div>
-                            </div>
-                        </li>;
-                    })}
-                </ul>;
+                return (
+                    <ul>
+                        {this.curVideoTemplates.map((video: VideoTemplate) => {
+                            return (
+                                <li onClick={() => this.embedVideoOnClick(video.videoId, video.videoTitle)} key={Utils.GenerateGuid()}>
+                                    <div className="search_wrapper">
+                                        <div style={{ backgroundColor: 'yellow' }}>
+                                            <img src={video.thumbnailUrl} />
+                                            <span className="video_duration">{video.duration}</span>
+                                        </div>
+                                        <div className="textual_info">
+                                            <span className="videoTitle">{video.videoTitle}</span>
+                                            <span className="channelName">{video.channelTitle}</span>
+                                            <span className="viewCount">{video.viewCount}</span>
+                                            <span className="publish_time">{video.publishDate}</span>
+                                            <p className="video_description">{video.videoDescription}</p>
+                                        </div>
+                                    </div>
+                                </li>
+                            );
+                        })}
+                    </ul>
+                );
             }
         } else {
-            return (null);
+            return null;
         }
-    }
+    };
 
     /**
      * Given a videoId and title, creates a new youtube embedded url, and uses that
@@ -332,7 +337,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
      */
     @action
     embedVideoOnClick = (videoId: string, filteredTitle: string) => {
-        const embeddedUrl = "https://www.youtube.com/embed/" + videoId;
+        const embeddedUrl = 'https://www.youtube.com/embed/' + videoId;
         this.selectedVideoUrl = embeddedUrl;
         const addFunction = this.props.addDocument!;
         const newVideoX = NumCast(this.props.Document.x);
@@ -340,24 +345,24 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
 
         addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, _width: 400, _height: 315, x: newVideoX, y: newVideoY }));
         this.videoClicked = true;
-    }
+    };
 
     render() {
-        const content =
-            <div className="youtubeBox-cont" style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
-                <input type="text" placeholder="Search for a video" onKeyDown={this.onEnterKeyDown} style={{ height: 40, width: "100%", border: "1px solid black", padding: 5, textAlign: "center" }} ref={(e) => this.YoutubeSearchElement = e!} />
+        const content = (
+            <div className="youtubeBox-cont" style={{ width: '100%', height: '100%', position: 'absolute' }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
+                <input type="text" placeholder="Search for a video" onKeyDown={this.onEnterKeyDown} style={{ height: 40, width: '100%', border: '1px solid black', padding: 5, textAlign: 'center' }} ref={e => (this.YoutubeSearchElement = e!)} />
                 {this.renderSearchResultsOrVideo()}
-            </div>;
+            </div>
+        );
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? '-interactive' : '');
         return (
             <>
-                <div className={classname}  >
-                    {content}
-                </div>
-                {!frozen ? (null) : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
-            </>);
+                <div className={classname}>{content}</div>
+                {!frozen ? null : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
+            </>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 2343c2f34..4e98a90a3 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -1,46 +1,66 @@
 export enum DocumentType {
-    NONE = "none",
+    NONE = 'none',
 
     // core data types
-    RTF = "rtf",
-    IMG = "image",
-    WEB = "web",
-    COL = "collection",
-    KVP = "kvp",
-    VID = "video",
-    AUDIO = "audio",
-    REC = "recording",
-    PDF = "pdf",
-    INK = "inks",
-    SCREENSHOT = "screenshot",
-    FONTICON = "fonticonbox",
-    FILTER = "filter",
-    SEARCH = "search",          // search query
-    LABEL = "label",            // simple text label
-    BUTTON = "button",          // onClick button
-    WEBCAM = "webcam",          // webcam
-    MARKER = "marker",          // generic marker document not intended to be viewed independently of its context (e.g., for text selections in PDF/Web/RTF)
-    DATE = "date",              // calendar view of a date
-    SCRIPTING = "script",       // script editor
-    EQUATION = "equation",      // equation editor
-    FUNCPLOT = "funcplot",      // function plotter
-    MAP = "map",
-    DATAVIZ = "dataviz",
+    RTF = 'rtf',
+    IMG = 'image',
+    WEB = 'web',
+    COL = 'collection',
+    KVP = 'kvp',
+    VID = 'video',
+    AUDIO = 'audio',
+    REC = 'recording',
+    PDF = 'pdf',
+    INK = 'inks',
+    SCREENSHOT = 'screenshot',
+    FONTICON = 'fonticonbox',
+    FILTER = 'filter',
+    SEARCH = 'search', // search query
+    LABEL = 'label', // simple text label
+    BUTTON = 'button', // onClick button
+    WEBCAM = 'webcam', // webcam
+    MARKER = 'marker', // generic marker document not intended to be viewed independently of its context (e.g., for text selections in PDF/Web/RTF)
+    DATE = 'date', // calendar view of a date
+    SCRIPTING = 'script', // script editor
+    EQUATION = 'equation', // equation editor
+    FUNCPLOT = 'funcplot', // function plotter
+    MAP = 'map',
+    DATAVIZ = 'dataviz',
 
     // special purpose wrappers that either take no data or are compositions of lower level types
-    LINK = "link",
-    LINKANCHOR = "linkanchor",
-    IMPORT = "import",
-    SLIDER = "slider",
-    PRES = "presentation",
-    PRESELEMENT = "preselement",
-    COLOR = "color",
-    YOUTUBE = "youtube",
-    SEARCHITEM = "searchitem",
-    COMPARISON = "comparison",
-    GROUP = "group",
+    LINK = 'link',
+    LINKANCHOR = 'linkanchor',
+    IMPORT = 'import',
+    SLIDER = 'slider',
+    PRES = 'presentation',
+    PRESELEMENT = 'preselement',
+    COLOR = 'color',
+    YOUTUBE = 'youtube',
+    SEARCHITEM = 'searchitem',
+    COMPARISON = 'comparison',
+    GROUP = 'group',
 
-    LINKDB = "linkdb",          // database of links  ??? why do we have this
-    SCRIPTDB = "scriptdb",      // database of scripts
-    GROUPDB = "groupdb",        // database of groups
-}
\ No newline at end of file
+    LINKDB = 'linkdb', // database of links  ??? why do we have this
+    SCRIPTDB = 'scriptdb', // database of scripts
+    GROUPDB = 'groupdb', // database of groups
+}
+export enum CollectionViewType {
+    Invalid = 'invalid',
+    Freeform = 'freeform',
+    Schema = 'schema',
+    Docking = 'docking',
+    Tree = 'tree',
+    Stacking = 'stacking',
+    Masonry = 'masonry',
+    Multicolumn = 'multicolumn',
+    Multirow = 'multirow',
+    Time = 'time',
+    Carousel = 'carousel',
+    Carousel3D = '3D Carousel',
+    Linear = 'linear',
+    //Staff = "staff",
+    Map = 'map',
+    Grid = 'grid',
+    Pile = 'pileup',
+    StackedTimeline = 'stacked timeline',
+}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 65b3db0e2..ed4c99d70 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,124 +1,158 @@
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { action, runInAction } from "mobx";
-import { basename } from "path";
-import { DateField } from "../../fields/DateField";
-import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { HtmlField } from "../../fields/HtmlField";
-import { InkField, PointData } from "../../fields/InkField";
-import { List } from "../../fields/List";
-import { ProxyField } from "../../fields/Proxy";
-import { RichTextField } from "../../fields/RichTextField";
-import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
-import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
-import { inheritParentAcls, SharingPermissions } from "../../fields/util";
-import { Upload } from "../../server/SharedMediaTypes";
-import { aggregateBounds, OmitKeys, Utils } from "../../Utils";
-import { YoutubeBox } from "../apis/youtube/YoutubeBox";
-import { DocServer } from "../DocServer";
-import { Networking } from "../Network";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DocumentManager } from "../util/DocumentManager";
-import { dropActionType } from "../util/DragManager";
-import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
-import { LinkManager } from "../util/LinkManager";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
-import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
-import { ContextMenu } from "../views/ContextMenu";
-import { ContextMenuProps } from "../views/ContextMenuItem";
-import { DFLT_IMAGE_NATIVE_DIM } from "../views/global/globalCssVariables.scss";
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke";
-import { AudioBox } from "../views/nodes/AudioBox";
-import { FontIconBox } from "../views/nodes/button/FontIconBox";
-import { ColorBox } from "../views/nodes/ColorBox";
-import { ComparisonBox } from "../views/nodes/ComparisonBox";
-import { DataVizBox } from "../views/nodes/DataVizBox/DataVizBox";
-import { DocFocusOptions } from "../views/nodes/DocumentView";
-import { EquationBox } from "../views/nodes/EquationBox";
-import { FieldViewProps } from "../views/nodes/FieldView";
-import { FilterBox } from "../views/nodes/FilterBox";
-import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
-import { FunctionPlotBox } from "../views/nodes/FunctionPlotBox";
-import { ImageBox } from "../views/nodes/ImageBox";
-import { KeyValueBox } from "../views/nodes/KeyValueBox";
-import { LabelBox } from "../views/nodes/LabelBox";
-import { LinkBox } from "../views/nodes/LinkBox";
-import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup";
-import { MapBox } from "../views/nodes/MapBox/MapBox";
-import { PDFBox } from "../views/nodes/PDFBox";
-import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox";
-import { ScreenshotBox } from "../views/nodes/ScreenshotBox";
-import { ScriptingBox } from "../views/nodes/ScriptingBox";
-import { SliderBox } from "../views/nodes/SliderBox";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import { PresBox } from "../views/nodes/trails/PresBox";
-import { PresElementBox } from "../views/nodes/trails/PresElementBox";
-import { VideoBox } from "../views/nodes/VideoBox";
-import { WebBox } from "../views/nodes/WebBox";
-import { SearchBox } from "../views/search/SearchBox";
-import { DocumentType } from "./DocumentTypes";
-
-const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { action, runInAction } from 'mobx';
+import { basename } from 'path';
+import { DateField } from '../../fields/DateField';
+import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { HtmlField } from '../../fields/HtmlField';
+import { InkField, PointData } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
+import { RichTextField } from '../../fields/RichTextField';
+import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
+import { ComputedField, ScriptField } from '../../fields/ScriptField';
+import { Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types';
+import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from '../../fields/URLField';
+import { inheritParentAcls, SharingPermissions } from '../../fields/util';
+import { Upload } from '../../server/SharedMediaTypes';
+import { aggregateBounds, OmitKeys, Utils } from '../../Utils';
+import { YoutubeBox } from '../apis/youtube/YoutubeBox';
+import { DocServer } from '../DocServer';
+import { Networking } from '../Network';
+import { DocumentManager } from '../util/DocumentManager';
+import { DragManager, dropActionType } from '../util/DragManager';
+import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox';
+import { LinkManager } from '../util/LinkManager';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { undoBatch, UndoManager } from '../util/UndoManager';
+import { CollectionDockingView } from '../views/collections/CollectionDockingView';
+import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
+import { CollectionView } from '../views/collections/CollectionView';
+import { ContextMenu } from '../views/ContextMenu';
+import { ContextMenuProps } from '../views/ContextMenuItem';
+import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss';
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from '../views/InkingStroke';
+import { AudioBox } from '../views/nodes/AudioBox';
+import { FontIconBox } from '../views/nodes/button/FontIconBox';
+import { ColorBox } from '../views/nodes/ColorBox';
+import { ComparisonBox } from '../views/nodes/ComparisonBox';
+import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
+import { DocFocusOptions } from '../views/nodes/DocumentView';
+import { EquationBox } from '../views/nodes/EquationBox';
+import { FieldViewProps } from '../views/nodes/FieldView';
+import { FilterBox } from '../views/nodes/FilterBox';
+import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox';
+import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox';
+import { ImageBox } from '../views/nodes/ImageBox';
+import { KeyValueBox } from '../views/nodes/KeyValueBox';
+import { LabelBox } from '../views/nodes/LabelBox';
+import { LinkBox } from '../views/nodes/LinkBox';
+import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup';
+import { MapBox } from '../views/nodes/MapBox/MapBox';
+import { PDFBox } from '../views/nodes/PDFBox';
+import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox';
+import { ScreenshotBox } from '../views/nodes/ScreenshotBox';
+import { ScriptingBox } from '../views/nodes/ScriptingBox';
+import { SliderBox } from '../views/nodes/SliderBox';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import { PresBox } from '../views/nodes/trails/PresBox';
+import { PresElementBox } from '../views/nodes/trails/PresElementBox';
+import { VideoBox } from '../views/nodes/VideoBox';
+import { WebBox } from '../views/nodes/WebBox';
+import { SearchBox } from '../views/search/SearchBox';
+import { CollectionViewType, DocumentType } from './DocumentTypes';
+
+const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', ''));
 class EmptyBox {
     public static LayoutString() {
-        return "";
+        return '';
     }
 }
 export abstract class FInfo {
-    description: string = "";
+    description: string = '';
     fieldType?: string;
     values?: Field[];
     // format?: string; // format to display values (e.g, decimal places, $, etc)
     // parse?: ScriptField; // parse a value from a string
-    constructor(d: string) { this.description = d; }
+    constructor(d: string) {
+        this.description = d;
+    }
+}
+class BoolInfo extends FInfo {
+    fieldType? = 'boolean';
+    values?: boolean[] = [true, false];
+}
+class NumInfo extends FInfo {
+    fieldType? = 'number';
+    values?: number[] = [];
+    constructor(d: string, values?: number[]) {
+        super(d);
+        this.values = values;
+    }
+}
+class StrInfo extends FInfo {
+    fieldType? = 'string';
+    values?: string[] = [];
+    constructor(d: string, values?: string[]) {
+        super(d);
+        this.values = values;
+    }
+}
+class DocInfo extends FInfo {
+    fieldType? = 'Doc';
+    values?: Doc[] = [];
+    constructor(d: string, values?: Doc[]) {
+        super(d);
+        this.values = values;
+    }
+}
+class DimInfo extends FInfo {
+    fieldType? = 'DimUnit';
+    values? = [DimUnit.Pixel, DimUnit.Ratio];
+}
+class PEInfo extends FInfo {
+    fieldType? = 'pointerEvents';
+    values? = ['all', 'none'];
+}
+class DAInfo extends FInfo {
+    fieldType? = 'dropActionType';
+    values? = ['alias', 'copy', 'move', 'same', 'proto', 'none'];
 }
-class BoolInfo extends FInfo { fieldType?= "boolean";        values?: boolean[] = [true, false]; }
-class NumInfo  extends FInfo { fieldType?= "number";         values?: number[] = []; constructor(d:string, values?:number[]) { super(d); this.values = values; }}
-class StrInfo  extends FInfo { fieldType?= "string";         values?: string[] = []; constructor(d:string, values?:string[]) { super(d); this.values = values; }}
-class DocInfo  extends FInfo { fieldType?= "Doc";            values?: Doc[] = []; constructor(d:string, values?:Doc[]) { super(d); this.values = values; }}
-class DimInfo  extends FInfo { fieldType?= "DimUnit";        values?= [DimUnit.Pixel, DimUnit.Ratio]; }
-class PEInfo   extends FInfo { fieldType?= "pointerEvents";  values?= ["all", "none"]; }
-class DAInfo   extends FInfo { fieldType?= "dropActionType"; values?= ["alias", "copy", "move", "same", "proto", "none"]; }
 type BOOLt = BoolInfo | boolean;
-type NUMt  = NumInfo | number;
-type STRt  = StrInfo | string;
-type DOCt  = DocInfo | Doc;
-type DIMt  = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio;
-type PEVt  = PEInfo | "none" | "all";
+type NUMt = NumInfo | number;
+type STRt = StrInfo | string;
+type DOCt = DocInfo | Doc;
+type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio;
+type PEVt = PEInfo | 'none' | 'all';
 type DROPt = DAInfo | dropActionType;
 export class DocumentOptions {
-    x?: NUMt = new NumInfo("x coordinate of document in a freeform view");
-    y?: NUMt = new NumInfo("y coordinage of document in a freeform view");
-    z?: NUMt = new NumInfo("whether document is in overlay (1) or not (0)", [1,0]);
-    system?: BOOLt = new BoolInfo("is this a system created/owned doc");
-    type?: STRt = new StrInfo("type of document", Array.from(Object.keys(DocumentType)));
+    x?: NUMt = new NumInfo('x coordinate of document in a freeform view');
+    y?: NUMt = new NumInfo('y coordinage of document in a freeform view');
+    z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', [1, 0]);
+    system?: BOOLt = new BoolInfo('is this a system created/owned doc');
+    type?: STRt = new StrInfo('type of document', Array.from(Object.keys(DocumentType)));
     title?: string;
     _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
-    allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?");
+    allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?');
     childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
-    targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? ");
-    userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)");
-    color?: STRt = new StrInfo("foreground color data doc");
-    backgroundColor?: STRt = new StrInfo("background color for data doc");
-    _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents");
-    _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title");
-    _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes");
-    _headerPointerEvents?: PEVt = new PEInfo("types of events the header of a custom text document can consume");
-    _panX?: NUMt = new NumInfo("horizontal pan location of a freeform view");
-    _panY?: NUMt = new NumInfo("vertical pan location of a freeform view");
-    _width?: NUMt = new NumInfo("displayed width of a document");
-    _height?: NUMt = new NumInfo("displayed height of document");
-    _nativeWidth?: NUMt = new NumInfo("native width of document contents (e.g., the pixel width of an image)");
-    _nativeHeight?: NUMt = new NumInfo("native height of document contents (e.g., the pixel height of an image)");
+    targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? ');
+    userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
+    color?: STRt = new StrInfo('foreground color data doc');
+    backgroundColor?: STRt = new StrInfo('background color for data doc');
+    _autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
+    _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title');
+    _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes');
+    _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
+    _panX?: NUMt = new NumInfo('horizontal pan location of a freeform view');
+    _panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
+    _width?: NUMt = new NumInfo('displayed width of a document');
+    _height?: NUMt = new NumInfo('displayed height of document');
+    _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)');
+    _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)');
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height");
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
-    _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)");
-    _fitContentsToBox?: BOOLt = new BoolInfo("whether a freeformview should zoom/scale to create a shrinkwrapped view of its content");
+    _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
+    _fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
     _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
@@ -126,11 +160,11 @@ export class DocumentOptions {
     _showTitle?: string; // field name to display in header (:hover is an optional suffix)
     _showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
     _scrollTop?: number; // scroll location for pdfs
-    _noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged
+    _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged
     _chromeHidden?: boolean; // whether the editing chrome for a document is hidden
     _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
     _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active)
-    _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
+    _stayInCollection?: boolean; // whether the document should remain in its collection when someone tries to drag and drop it elsewhere
     _raiseWhenDragged?: boolean; // whether a document is brought to front when dragged.
     _hideContextMenu?: boolean; // whether the context menu can be shown
     _viewType?: string; // sub type of a collection
@@ -142,7 +176,7 @@ export class DocumentOptions {
     _xPadding?: number;
     _yPadding?: number;
     _itemIndex?: number; // which item index the carousel viewer is showing
-    _showSidebar?: boolean;  //whether an annotationsidebar should be displayed for text docuemnts
+    _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts
     _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document)
     _minFontSize?: number; // minimum font size for labelBoxes
     _maxFontSize?: number; // maximum font size for labelBoxes
@@ -158,11 +192,11 @@ export class DocumentOptions {
     _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays)
     _timecodeToHide?: number; // the time that a document should be hidden
     _timelineLabel?: boolean; // whether the document exists on a timeline
-    "_carousel-caption-xMargin"?: NUMt = new NumInfo("x margin of caption inside of a carouself collection");
-    "_carousel-caption-yMargin"?: NUMt = new NumInfo("y margin of caption inside of a carouself collection");
-    "icon-nativeWidth"?: NUMt = new NumInfo("native width of icon view");
-    "icon-nativeHeight"?: NUMt = new NumInfo("native height of icon view");
-    "dragFactory-count"?: NUMt = new NumInfo("number of items created from a drag button (used for setting title with incrementing index)");
+    '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection');
+    '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection');
+    'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view');
+    'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view');
+    'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)');
     lat?: number;
     lng?: number;
     infoWindowOpen?: boolean;
@@ -171,8 +205,8 @@ export class DocumentOptions {
     fieldValues?: List<any>; // possible field values used by fieldInfos
     fieldType?: string; // type of afield used by fieldInfos
     unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)
-     "acl-Public"?: string; // public permissions
-    "_acl-Public"?: string; // public permissions
+    'acl-Public'?: string; // public permissions
+    '_acl-Public'?: string; // public permissions
     version?: string; // version identifier for a document
     label?: string;
     hidden?: boolean;
@@ -181,7 +215,7 @@ export class DocumentOptions {
     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.
+    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>;
@@ -190,7 +224,7 @@ export class DocumentOptions {
     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
-    contentPointerEvents?: string;  // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
+    contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     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
@@ -230,12 +264,12 @@ export class DocumentOptions {
     baseProto?: boolean; // is this a base prototoype
     dontRegisterView?: boolean;
     lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document.  see PresBox.
-    "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form
-    "onChildDoubleClick-rawScript"?: string; // onChildDoubleClick script in raw text form
-    "onChildClick-rawScript"?: string; // on ChildClick script in raw text form
-    "onClick-rawScript"?: string; // onClick script in raw text form
-    "onCheckedClick-rawScript"?: string; // onChecked script in raw text form
-    "onCheckedClick-params"?: List<string>; // parameter list for onChecked treeview functions
+    'onDoubleClick-rawScript'?: string; // onDoubleClick script in raw text form
+    'onChildDoubleClick-rawScript'?: string; // onChildDoubleClick script in raw text form
+    'onChildClick-rawScript'?: string; // on ChildClick script in raw text form
+    'onClick-rawScript'?: string; // onClick script in raw text form
+    'onCheckedClick-rawScript'?: string; // onChecked script in raw text form
+    'onCheckedClick-params'?: List<string>; // parameter list for onChecked treeview functions
     columnHeaders?: List<SchemaHeaderField>; // headers for stacking views
     schemaHeaders?: List<SchemaHeaderField>; // headers for schema view
     clipWidth?: number; // percent transition from before to after in comparisonBox
@@ -270,7 +304,7 @@ export class DocumentOptions {
     linearViewSubMenu?: boolean;
     linearViewFloating?: boolean;
     flexGap?: number; // Linear view flex gap
-    flexDirection?: "unset" | "row" | "column" | "row-reverse" | "column-reverse";
+    flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse';
 
     layout_linkView?: Doc; // view template for a link document
     layout_keyValue?: string; // view tempalte for key value docs
@@ -278,8 +312,8 @@ export class DocumentOptions {
     linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors
     anchor1?: Doc;
     anchor2?: Doc;
-    "anchor1-useLinkSmallAnchor"?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
-    "anchor2-useLinkSmallAnchor"?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
+    'anchor1-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
+    'anchor2-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
     ignoreClick?: boolean;
     onClick?: ScriptField;
     onDoubleClick?: ScriptField;
@@ -292,7 +326,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;
+    filterBoolean?: string;
     useCors?: boolean;
     icon?: string;
     target?: Doc; // available for use in scripts as the primary target document
@@ -306,9 +340,9 @@ export class DocumentOptions {
     treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
-    treeViewChildDoubleClick?: ScriptField; // 
+    treeViewChildDoubleClick?: ScriptField; //
     // Action Button
-    buttonMenu?: boolean; // whether a action button should be displayed 
+    buttonMenu?: boolean; // whether a action button should be displayed
     buttonMenuDoc?: Doc;
     explainer?: string;
 
@@ -321,7 +355,7 @@ export class DocumentOptions {
     treeViewTruncateTitleWidth?: number;
     treeViewHasOverlay?: boolean; // whether the treeview has an overlay for freeform annotations
     treeViewType?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy
-    sidebarColor?: string;  // background color of text sidebar
+    sidebarColor?: string; // background color of text sidebar
     sidebarViewType?: string; // collection type of text sidebar
     docMaxAutoHeight?: number; // maximum height for newly created (eg, from pasting) text documents
     text?: string;
@@ -331,192 +365,305 @@ export class DocumentOptions {
     selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui
     clipboard?: Doc;
     searchQuery?: string; // for quersyBox
-    useLinkSmallAnchor?: boolean;  // whether links to this document should use a miniature linkAnchorBox
+    useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox
     border?: string; //for searchbox
     hoverBackgroundColor?: string; // background color of a label when hovered
     linkRelationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor)
-    linkRelationshipSizes?: List<number>; //stores number of links contained in each relationship 
+    linkRelationshipSizes?: List<number>; //stores number of links contained in each relationship
     linkColorList?: List<string>; // colors of links corresponding to specific link relationships
 }
 export namespace Docs {
-
     export let newAccount: boolean = false;
 
     export namespace Prototypes {
-
         type LayoutSource = { LayoutString: (key: string) => string };
         type PrototypeTemplate = {
             layout: {
-                view: LayoutSource,
-                dataField: string
-            },
-            data?: any,
-            options?: Partial<DocumentOptions>
+                view: LayoutSource;
+                dataField: string;
+            };
+            data?: any;
+            options?: Partial<DocumentOptions>;
         };
         type TemplateMap = Map<DocumentType, PrototypeTemplate>;
         type PrototypeMap = Map<DocumentType, Doc>;
-        const defaultDataKey = "data";
+        const defaultDataKey = 'data';
 
         const TemplateMap: TemplateMap = new Map([
-            [DocumentType.RTF, {
-                layout: { view: FormattedTextBox, dataField: "text" },
-                options: {
-                    _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, treeViewGrowsHorizontally: true,
-                    forceReflow: true, links: "@links(self)"
-                }
-            }],
-            [DocumentType.SEARCH, {
-                layout: { view: SearchBox, dataField: defaultDataKey },
-                options: { _width: 400, links: "@links(self)" }
-            }],
-            [DocumentType.FILTER, {
-                layout: { view: FilterBox, dataField: defaultDataKey },
-                options: { _width: 400, links: "@links(self)" }
-            }],
-            [DocumentType.COLOR, {
-                layout: { view: ColorBox, dataField: defaultDataKey },
-                options: { _nativeWidth: 220, _nativeHeight: 300, links: "@links(self)" }
-            }],
-            [DocumentType.IMG, {
-                layout: { view: ImageBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.WEB, {
-                layout: { view: WebBox, dataField: defaultDataKey },
-                options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
-            }],
-            [DocumentType.COL, {
-                layout: { view: CollectionView, dataField: defaultDataKey },
-                options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: "@links(self)" }
-            }],
-            [DocumentType.KVP, {
-                layout: { view: KeyValueBox, dataField: defaultDataKey },
-                options: { _fitWidth: true, _height: 150 }
-            }],
-            [DocumentType.VID, {
-                layout: { view: VideoBox, dataField: defaultDataKey },
-                options: { _currentTimecode: 0, links: "@links(self)" },
-            }],
-            [DocumentType.AUDIO, {
-                layout: { view: AudioBox, dataField: defaultDataKey },
-                options: { _height: 100, backgroundColor: "lightGray", forceReflow: true, nativeDimModifiable: true, links: "@links(self)" }
-            }],
-            [DocumentType.REC, {
-                layout: { view: VideoBox, dataField: defaultDataKey },
-                options: { _height: 100, backgroundColor: "pink", links: "@links(self)" }
-            }],
-            [DocumentType.PDF, {
-                layout: { view: PDFBox, dataField: defaultDataKey },
-                options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
-            }],
-            [DocumentType.MAP, {
-                layout: { view: MapBox, dataField: defaultDataKey },
-                options: { _height: 600, _width: 800, nativeDimModifiable: true, links: "@links(self)" }
-            }],
-            [DocumentType.IMPORT, {
-                layout: { view: DirectoryImportBox, dataField: defaultDataKey },
-                options: { _height: 150 }
-            }],
-            [DocumentType.LINK, {
-                layout: { view: LinkBox, dataField: defaultDataKey },
-                options: {
-                    childDontRegisterViews: true, _isLinkButton: true, _height: 150, description: "", showCaption: "description",
-                    backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area
-                    links: "@links(self)",
-                    _removeDropProperties: new List(["isLinkButton"]),
-                }
-            }],
-            [DocumentType.LINKDB, {
-                data: new List<Doc>(),
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { childDropAction: "alias", title: "Global Link Database" }
-            }],
-            [DocumentType.SCRIPTDB, {
-                data: new List<Doc>(),
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { childDropAction: "alias", title: "Global Script Database" }
-            }],
-            [DocumentType.SCRIPTING, {
-                layout: { view: ScriptingBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.YOUTUBE, {
-                layout: { view: YoutubeBox, dataField: defaultDataKey }
-            }],
-            [DocumentType.LABEL, {
-                layout: { view: LabelBox, dataField: defaultDataKey },
-                options: { links: "@links(self)", _singleLine: true }
-            }],
-            [DocumentType.EQUATION, {
-                layout: { view: EquationBox, dataField: defaultDataKey },
-                options: { links: "@links(self)", nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true }
-            }],
-            [DocumentType.FUNCPLOT, {
-                layout: { view: FunctionPlotBox, dataField: defaultDataKey },
-                options: { nativeDimModifiable: true, links: "@links(self)" }
-            }],
-            [DocumentType.BUTTON, {
-                layout: { view: LabelBox, dataField: "onClick" },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.SLIDER, {
-                layout: { view: SliderBox, dataField: defaultDataKey, },
-                options: { links: "@links(self)", treeViewGrowsHorizontally: true }
-            }],
-            [DocumentType.PRES, {
-                layout: { view: PresBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.FONTICON, {
-                layout: { view: FontIconBox, dataField: defaultDataKey },
-                options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" },
-            }],
-            [DocumentType.WEBCAM, {
-                layout: { view: RecordingBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.PRESELEMENT, {
-                layout: { view: PresElementBox, dataField: defaultDataKey }
-            }],
-            [DocumentType.MARKER, {
-                layout: { view: CollectionView, dataField: defaultDataKey },
-                options: { links: "@links(self)", hideLinkButton: true, pointerEvents: "none" }
-            }],
-            [DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
-                layout: { view: InkingStroke, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.SCREENSHOT, {
-                layout: { view: ScreenshotBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.COMPARISON, {
-                layout: { view: ComparisonBox, dataField: defaultDataKey },
-                options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" }
-            }],
-            [DocumentType.GROUPDB, {
-                data: new List<Doc>(),
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { childDropAction: "alias", title: "Global Group Database" }
-            }],
-            [DocumentType.GROUP, {
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.DATAVIZ, {
-                layout: { view: DataVizBox, dataField: defaultDataKey },
-                options: { _fitWidth: true, nativeDimModifiable: true, links: "@links(self)" }
-            }]
+            [
+                DocumentType.RTF,
+                {
+                    layout: { view: FormattedTextBox, dataField: 'text' },
+                    options: {
+                        _height: 35,
+                        _xMargin: 10,
+                        _yMargin: 10,
+                        nativeDimModifiable: true,
+                        treeViewGrowsHorizontally: true,
+                        forceReflow: true,
+                        links: '@links(self)',
+                    },
+                },
+            ],
+            [
+                DocumentType.SEARCH,
+                {
+                    layout: { view: SearchBox, dataField: defaultDataKey },
+                    options: { _width: 400, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.FILTER,
+                {
+                    layout: { view: FilterBox, dataField: defaultDataKey },
+                    options: { _width: 400, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.COLOR,
+                {
+                    layout: { view: ColorBox, dataField: defaultDataKey },
+                    options: { _nativeWidth: 220, _nativeHeight: 300, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.IMG,
+                {
+                    layout: { view: ImageBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.WEB,
+                {
+                    layout: { view: WebBox, dataField: defaultDataKey },
+                    options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.COL,
+                {
+                    layout: { view: CollectionView, dataField: defaultDataKey },
+                    options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.KVP,
+                {
+                    layout: { view: KeyValueBox, dataField: defaultDataKey },
+                    options: { _fitWidth: true, _height: 150 },
+                },
+            ],
+            [
+                DocumentType.VID,
+                {
+                    layout: { view: VideoBox, dataField: defaultDataKey },
+                    options: { _currentTimecode: 0, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.AUDIO,
+                {
+                    layout: { view: AudioBox, dataField: defaultDataKey },
+                    options: { _height: 100, backgroundColor: 'lightGray', forceReflow: true, nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.REC,
+                {
+                    layout: { view: VideoBox, dataField: defaultDataKey },
+                    options: { _height: 100, backgroundColor: 'pink', links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.PDF,
+                {
+                    layout: { view: PDFBox, dataField: defaultDataKey },
+                    options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.MAP,
+                {
+                    layout: { view: MapBox, dataField: defaultDataKey },
+                    options: { _height: 600, _width: 800, nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.IMPORT,
+                {
+                    layout: { view: DirectoryImportBox, dataField: defaultDataKey },
+                    options: { _height: 150 },
+                },
+            ],
+            [
+                DocumentType.LINK,
+                {
+                    layout: { view: LinkBox, dataField: defaultDataKey },
+                    options: {
+                        childDontRegisterViews: true,
+                        _isLinkButton: true,
+                        _height: 150,
+                        description: '',
+                        showCaption: 'description',
+                        backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
+                        links: '@links(self)',
+                        _removeDropProperties: new List(['isLinkButton']),
+                    },
+                },
+            ],
+            [
+                DocumentType.LINKDB,
+                {
+                    data: new List<Doc>(),
+                    layout: { view: EmptyBox, dataField: defaultDataKey },
+                    options: { childDropAction: 'alias', title: 'Global Link Database' },
+                },
+            ],
+            [
+                DocumentType.SCRIPTDB,
+                {
+                    data: new List<Doc>(),
+                    layout: { view: EmptyBox, dataField: defaultDataKey },
+                    options: { childDropAction: 'alias', title: 'Global Script Database' },
+                },
+            ],
+            [
+                DocumentType.SCRIPTING,
+                {
+                    layout: { view: ScriptingBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.YOUTUBE,
+                {
+                    layout: { view: YoutubeBox, dataField: defaultDataKey },
+                },
+            ],
+            [
+                DocumentType.LABEL,
+                {
+                    layout: { view: LabelBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)', _singleLine: true },
+                },
+            ],
+            [
+                DocumentType.EQUATION,
+                {
+                    layout: { view: EquationBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)', nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true },
+                },
+            ],
+            [
+                DocumentType.FUNCPLOT,
+                {
+                    layout: { view: FunctionPlotBox, dataField: defaultDataKey },
+                    options: { nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.BUTTON,
+                {
+                    layout: { view: LabelBox, dataField: 'onClick' },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.SLIDER,
+                {
+                    layout: { view: SliderBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)', treeViewGrowsHorizontally: true },
+                },
+            ],
+            [
+                DocumentType.PRES,
+                {
+                    layout: { view: PresBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.FONTICON,
+                {
+                    layout: { view: FontIconBox, dataField: defaultDataKey },
+                    options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%', links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.WEBCAM,
+                {
+                    layout: { view: RecordingBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.PRESELEMENT,
+                {
+                    layout: { view: PresElementBox, dataField: defaultDataKey },
+                },
+            ],
+            [
+                DocumentType.MARKER,
+                {
+                    layout: { view: CollectionView, dataField: defaultDataKey },
+                    options: { links: '@links(self)', hideLinkButton: true, pointerEvents: 'none' },
+                },
+            ],
+            [
+                DocumentType.INK,
+                {
+                    // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
+                    layout: { view: InkingStroke, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.SCREENSHOT,
+                {
+                    layout: { view: ScreenshotBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.COMPARISON,
+                {
+                    layout: { view: ComparisonBox, dataField: defaultDataKey },
+                    options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias', links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.GROUPDB,
+                {
+                    data: new List<Doc>(),
+                    layout: { view: EmptyBox, dataField: defaultDataKey },
+                    options: { childDropAction: 'alias', title: 'Global Group Database' },
+                },
+            ],
+            [
+                DocumentType.GROUP,
+                {
+                    layout: { view: EmptyBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.DATAVIZ,
+                {
+                    layout: { view: DataVizBox, dataField: defaultDataKey },
+                    options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
         ]);
 
-        const suffix = "Proto";
+        const suffix = 'Proto';
 
         /**
          * This function loads or initializes the prototype for each docment type.
-         * 
+         *
          * This is an asynchronous function because it has to attempt
          * to fetch the prototype documents from the server.
-         * 
+         *
          * Once we have this object that maps the prototype ids to a potentially
          * undefined document, we either initialize our private prototype
          * variables with the document returned from the server or, if prototypes
@@ -526,13 +673,15 @@ export namespace Docs {
             ProxyField.initPlugin();
             ComputedField.initPlugin();
             // non-guid string ids for each document prototype
-            const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
+            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);
             // update this object to include any default values: DocumentOptions for all prototypes
             prototypeIds.map(id => {
                 const existing = actualProtos[id] as Doc;
-                const type = id.replace(suffix, "") as DocumentType;
+                const type = id.replace(suffix, '') as DocumentType;
                 // get or create prototype of the specified type...
                 const target = existing || buildPrototype(type, id);
                 // ...and set it if not undefined (can be undefined only if TemplateMap does not contain
@@ -545,30 +694,38 @@ export namespace Docs {
          * Retrieves the prototype for the given document type, or
          * undefined if that type's proto doesn't have a configuration
          * in the template map.
-         * @param type 
+         * @param type
          */
         const PrototypeMap: PrototypeMap = new Map();
-        export function get(type: DocumentType): Doc { return PrototypeMap.get(type)!; }
+        export function get(type: DocumentType): Doc {
+            return PrototypeMap.get(type)!;
+        }
 
         /**
          * A collection of all links in the database.  Ideally, this would be a search, but for now all links are cached here.
          */
-        export function MainLinkDocument() { return Prototypes.get(DocumentType.LINKDB); }
+        export function MainLinkDocument() {
+            return Prototypes.get(DocumentType.LINKDB);
+        }
 
         /**
          * A collection of all scripts in the database
          */
-        export function MainScriptDocument() { return Prototypes.get(DocumentType.SCRIPTDB); }
+        export function MainScriptDocument() {
+            return Prototypes.get(DocumentType.SCRIPTDB);
+        }
 
         /**
          * A collection of all user acl groups in the database
          */
-        export function MainGroupDocument() { return Prototypes.get(DocumentType.GROUPDB); }
+        export function MainGroupDocument() {
+            return Prototypes.get(DocumentType.GROUPDB);
+        }
 
         /**
          * This is a convenience method that is used to initialize
          * prototype documents for the first time.
-         * 
+         *
          * @param protoId the id of the prototype, indicating the specific prototype
          * to initialize (see the *protoId list at the top of the namespace)
          * @param title the prototype document's title, follows *-PROTO
@@ -590,11 +747,21 @@ export namespace Docs {
             // synthesize the default options, the type and title from computed values and
             // whatever options pertain to this specific prototype
             const options: DocumentOptions = {
-                system: true, _layoutKey: "layout", title, type, baseProto: true, x: 0, y: 0, _width: 300, ...(template.options || {}),
-                layout: layout.view?.LayoutString(layout.dataField), data: template.data, layout_keyValue: KeyValueBox.LayoutString("")
+                system: true,
+                _layoutKey: 'layout',
+                title,
+                type,
+                baseProto: true,
+                x: 0,
+                y: 0,
+                _width: 300,
+                ...(template.options || {}),
+                layout: layout.view?.LayoutString(layout.dataField),
+                data: template.data,
+                layout_keyValue: KeyValueBox.LayoutString(''),
             };
             Object.entries(options).map(pair => {
-                if (typeof pair[1] === "string" && pair[1].startsWith("@")) {
+                if (typeof pair[1] === 'string' && pair[1].startsWith('@')) {
                     (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1));
                 }
             });
@@ -607,16 +774,15 @@ export namespace Docs {
      * delegated from top-level prototypes
      */
     export namespace Create {
-
         /**
          * This function receives the relevant document prototype and uses
          * it to create a new of that base-level prototype, or the
-         * underlying data document, which it then delegates again 
+         * underlying data document, which it then delegates again
          * to create the view document.
-         * 
+         *
          * It also takes the opportunity to register the user
          * that created the document and the time of creation.
-         * 
+         *
          * @param proto the specific document prototype off of which to model
          * this new instance (textProto, imageProto, etc.)
          * @param data the Field to store at this new instance's data key
@@ -626,45 +792,48 @@ export namespace Docs {
          * only when creating a DockDocument from the current user's already existing
          * main document.
          */
-        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) {
-            const viewKeys = ["x", "y", "system"]; // keys that should be addded to the view document even though they don't begin with an "_"
-            const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_");
+        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string) {
+            const viewKeys = ['x', 'y', 'system']; // keys that should be addded to the view document even though they don't begin with an "_"
+            const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
-            dataProps["acl-Override"] = "None";
-            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            dataProps['acl-Override'] = 'None';
+            dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps.system = viewProps.system;
             dataProps.isPrototype = true;
             dataProps.author = Doc.CurrentUserEmail;
-            dataProps.creationDate = new DateField;
-            dataProps[`${fieldKey}-lastModified`] = new DateField;
+            dataProps.creationDate = new DateField();
+            dataProps[`${fieldKey}-lastModified`] = new DateField();
 
             dataProps[fieldKey] = data;
 
             // so that the list of annotations is already initialised, prevents issues in addonly.
             // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
-            dataProps[fieldKey + "-annotations"] = new List<Doc>();
-            dataProps[fieldKey + "-sidebar"] = new List<Doc>();
+            dataProps[fieldKey + '-annotations'] = new List<Doc>();
+            dataProps[fieldKey + '-sidebar'] = new List<Doc>();
             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.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-            viewFirstProps["acl-Override"] = "None";
+            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);
             Doc.assign(viewDoc, viewProps, true, true);
             ![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(CurrentUserUtils.MyFileOrphans, undefined, dataDoc);
+            !Doc.IsSystem(dataDoc) &&
+                ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
+                !dataDoc.isFolder &&
+                !dataProps.annotationOn &&
+                Doc.AddDocToList(Doc.MyFileOrphans, undefined, dataDoc);
 
             updateCachedAcls(dataDoc);
             updateCachedAcls(viewDoc);
             return viewDoc;
         }
 
-        export function ImageDocument(url: string|ImageField, options: DocumentOptions = {}) {
-            const imgField =  url instanceof ImageField ? url : new ImageField(url);
+        export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}) {
+            const imgField = url instanceof ImageField ? url : new ImageField(url);
             return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options });
         }
 
@@ -672,9 +841,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List<Doc>(), options);
         }
 
-        export function ScriptingDocument(script: Opt<ScriptField>|null, options: DocumentOptions = {}, fieldKey?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script: undefined,
-                { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
+        export function ScriptingDocument(script: Opt<ScriptField> | null, options: DocumentOptions = {}, fieldKey?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
         }
 
         export function VideoDocument(url: string, options: DocumentOptions = {}) {
@@ -686,24 +854,23 @@ export namespace Docs {
         }
 
         export function WebCamDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), "", options);
+            return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), '', options);
         }
 
         export function ScreenshotDocument(options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", options);
+            return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), '', options);
         }
 
-        export function ComparisonDocument(options: DocumentOptions = { title: "Comparison Box" }) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), "", options);
+        export function ComparisonDocument(options: DocumentOptions = { title: 'Comparison Box' }) {
+            return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options);
         }
 
         export function AudioDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url),
-                { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
+            return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
         }
 
         export function RecordingDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.REC), "", options);
+            return InstanceFromProto(Prototypes.get(DocumentType.REC), '', options);
         }
 
         export function SearchDocument(options: DocumentOptions = {}) {
@@ -711,34 +878,46 @@ export namespace Docs {
         }
 
         export function ColorDocument(options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COLOR), "", options);
+            return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options);
         }
 
-        export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = "text") {
+        export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
         }
-        export function TextDocument(text: string, options: DocumentOptions = {}, fieldKey: string = "text") {
+        export function TextDocument(text: string, options: DocumentOptions = {}, fieldKey: string = 'text') {
             const rtf = {
                 doc: {
-                    type: "doc", content: [{
-                        type: "paragraph",
-                        content: [{
-                            type: "text",
-                            text
-                        }]
-                    }]
+                    type: 'doc',
+                    content: [
+                        {
+                            type: 'paragraph',
+                            content: [
+                                {
+                                    type: 'text',
+                                    text,
+                                },
+                            ],
+                        },
+                    ],
                 },
-                selection: { type: "text", anchor: 1, head: 1 },
-                storedMarks: []
+                selection: { type: 'text', anchor: 1, head: 1 },
+                storedMarks: [],
             };
             const field = text ? new RichTextField(JSON.stringify(rtf), text) : undefined;
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
         }
 
-        export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) {
-            const linkDoc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, {
-                anchor1: source.doc, anchor2: target.doc, ...options
-            }, id);
+        export function LinkDocument(source: { doc: Doc; ctx?: Doc }, target: { doc: Doc; ctx?: Doc }, options: DocumentOptions = {}, id?: string) {
+            const linkDoc = InstanceFromProto(
+                Prototypes.get(DocumentType.LINK),
+                undefined,
+                {
+                    anchor1: source.doc,
+                    anchor2: target.doc,
+                    ...options,
+                },
+                id
+            );
 
             LinkManager.Instance.addLink(linkDoc);
 
@@ -749,9 +928,9 @@ export namespace Docs {
             const I = new Doc();
             I[Initializing] = true;
             I.type = DocumentType.INK;
-            I.layout = InkingStroke.LayoutString("data");
+            I.layout = InkingStroke.LayoutString('data');
             I.color = color;
-            I.hideDecorationTitle = true;  // don't show title when selected
+            I.hideDecorationTitle = true; // don't show title when selected
             // I.hideOpenButton = true; // don't show open full screen button when selected
             I.fillColor = fillColor;
             I.strokeWidth = strokeWidth;
@@ -760,20 +939,20 @@ export namespace Docs {
             I.strokeEndMarker = arrowEnd;
             I.strokeDash = dash;
             I.tool = tool;
-            I["text-align"] = "center";
-            I.title = "ink";
+            I['text-align'] = 'center';
+            I.title = 'ink';
             I.x = options.x as number;
             I.y = options.y as number;
             I._width = options._width as number;
             I._height = options._height as number;
-            I._fontFamily = "cursive";
+            I._fontFamily = 'cursive';
             I.author = Doc.CurrentUserEmail;
             I.rotation = 0;
             I.data = new InkField(points);
-            I.creationDate = new DateField;
-            I["acl-Public"] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-            I["acl-Override"] = "None";
-            I.links = ComputedField.MakeFunction("links(self)");
+            I.creationDate = new DateField();
+            I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            I['acl-Override'] = 'None';
+            I.links = ComputedField.MakeFunction('links(self)');
             I[Initializing] = false;
             return I;
         }
@@ -783,7 +962,7 @@ export namespace Docs {
             const height = options._height || undefined;
             const nwid = options._nativeWidth || undefined;
             const nhght = options._nativeHeight || undefined;
-            if (!nhght && width && height && nwid) options._nativeHeight = Number(nwid) * Number(height) / Number(width);
+            if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width);
             return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options);
         }
 
@@ -792,8 +971,8 @@ export namespace Docs {
             const height = options._height || undefined;
             const nwid = options._nativeWidth || undefined;
             const nhght = options._nativeHeight || undefined;
-            if (!nhght && width && height && nwid) options._nativeHeight = Number(nwid) * Number(height) / Number(width);
-            return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : "http://www.bing.com/"), options);
+            if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width);
+            return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : 'http://www.bing.com/'), options);
         }
 
         export function HtmlDocument(html: string, options: DocumentOptions = {}) {
@@ -809,12 +988,12 @@ export namespace Docs {
         }
 
         export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + ".kvp", ...options });
+            return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + '.kvp', ...options });
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id);
-            documents.map(d => d.context = inst);
+            documents.map(d => (d.context = inst));
             return inst;
         }
 
@@ -831,7 +1010,7 @@ export namespace Docs {
         }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: "visible", _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: 'visible', _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id);
         }
 
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -886,7 +1065,7 @@ export namespace Docs {
         }
 
         export function ButtonDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), "onClick-rawScript": "-script-" });
+            return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), 'onClick-rawScript': '-script-' });
         }
 
         export function SliderDocument(options?: DocumentOptions) {
@@ -905,11 +1084,11 @@ export namespace Docs {
         }
 
         export function DataVizDocument(url: string, options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: "Data Viz", ...options });
+            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options });
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: 'remove|add', ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
 
         export function DirectoryImportDocument(options: DocumentOptions = {}) {
@@ -917,23 +1096,26 @@ export namespace Docs {
         }
 
         export type DocConfig = {
-            doc: Doc,
-            initialWidth?: number,
-            path?: Doc[]
+            doc: Doc;
+            initialWidth?: number;
+            path?: Doc[];
         };
 
-        export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = "row") {
+        export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = 'row') {
             const layoutConfig = {
                 content: [
                     {
                         type: type,
-                        content: [
-                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))
-                        ]
-                    }
-                ]
+                        content: [...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))],
+                    },
+                ],
             };
-            return DockDocument(configs.map(c => c.doc), JSON.stringify(layoutConfig), options, id);
+            return DockDocument(
+                configs.map(c => c.doc),
+                JSON.stringify(layoutConfig),
+                options,
+                id
+            );
         }
 
         export function DelegateDocument(proto: Doc, options: DocumentOptions = {}) {
@@ -944,9 +1126,9 @@ export namespace Docs {
 
 export namespace DocUtils {
     export function Excluded(d: Doc, docFilters: string[]) {
-        const filterFacets: { [key: string]: { [value: string]: string } } = {};  // maps each filter key to an object with value=>modifier fields
+        const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
         docFilters.forEach(filter => {
-            const fields = filter.split(":");
+            const fields = filter.split(':');
             const key = fields[0];
             const value = fields[1];
             const modifiers = fields[2];
@@ -959,7 +1141,7 @@ export namespace DocUtils {
         if (d.z) return false;
         for (const facetKey of Object.keys(filterFacets)) {
             const facet = filterFacets[facetKey];
-            const xs = Object.keys(facet).filter(value => facet[value] === "x");
+            const xs = Object.keys(facet).filter(value => facet[value] === 'x');
             const failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value));
             if (failsNotEqualFacets) {
                 return true;
@@ -968,21 +1150,21 @@ export namespace DocUtils {
         return false;
     }
     /**
-     * @param docs 
-     * @param docFilters 
-     * @param docRangeFilters 
-     * @param viewSpecScript 
-     * Given a list of docs and docFilters, @returns the list of Docs that match those filters 
+     * @param docs
+     * @param docFilters
+     * @param docRangeFilters
+     * @param viewSpecScript
+     * Given a list of docs and docFilters, @returns the list of Docs that match those filters
      */
     export function FilterDocs(docs: Doc[], docFilters: string[], docRangeFilters: string[], viewSpecScript?: ScriptField, parentCollection?: Doc) {
         const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
         if (!docFilters?.length && !docRangeFilters?.length) {
-            return childDocs.filter(d => !d.cookies);  // remove documents that need a cookie if there are no filters to provide one
+            return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one
         }
 
-        const filterFacets: { [key: string]: { [value: string]: string } } = {};  // maps each filter key to an object with value=>modifier fields
+        const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
         docFilters.forEach(filter => {
-            const fields = filter.split(":");
+            const fields = filter.split(':');
             const key = fields[0];
             const value = fields[1];
             const modifiers = fields[2];
@@ -992,63 +1174,67 @@ export namespace DocUtils {
             filterFacets[key][value] = modifiers;
         });
 
-        const filteredDocs = docFilters.length ? childDocs.filter(d => {
-            if (d.z) return true;
-            // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter
-            if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) {
-                return false;
-            }
-
-            for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== "cookies")) {
-                const facet = filterFacets[facetKey];
-
-                // facets that match some value in the field of the document (e.g. some text field)
-                const matches = Object.keys(facet).filter(value => value !== "cookies" && facet[value] === "match");
-
-                // facets that have a check next to them
-                const checks = Object.keys(facet).filter(value => facet[value] === "check");
-
-                // metadata facets that exist
-                const exists = Object.keys(facet).filter(value => facet[value] === "exists");
-
-                // metadata facets that exist
-                const unsets = Object.keys(facet).filter(value => facet[value] === "unset");
-
-                // facets that have an x next to them
-                const xs = Object.keys(facet).filter(value => facet[value] === "x");
-
-                if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
-                const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
-                const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
-                const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined);
-                const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined);
-                const satisfiesMatchFacets = !matches.length ? true : matches.some(value => {
-                    if (facetKey.startsWith("*")) { //  fields starting with a '*' are used to match families of related fields.  ie, *lastModified will match text-lastModified, data-lastModified, etc
-                        const allKeys = Array.from(Object.keys(d));
-                        allKeys.push(...Object.keys(Doc.GetProto(d)));
-                        const keys = allKeys.filter(key => key.includes(facetKey.substring(1)));
-                        return keys.some(key => Field.toString(d[key] as Field).includes(value));
-                    }
-                    return Field.toString(d[facetKey] as Field).includes(value);
-                });
-                // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
-                if ((parentCollection?.currentFilter as Doc)?.filterBoolean === "OR") {
-                    if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
-                }
-                // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
-                else {
-                    if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
-                }
-
-            }
-            return (parentCollection?.currentFilter as Doc)?.filterBoolean === "OR" ? false : true;
-        }) : childDocs;
+        const filteredDocs = docFilters.length
+            ? childDocs.filter(d => {
+                  if (d.z) return true;
+                  // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter
+                  if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) {
+                      return false;
+                  }
+
+                  for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies')) {
+                      const facet = filterFacets[facetKey];
+
+                      // facets that match some value in the field of the document (e.g. some text field)
+                      const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match');
+
+                      // facets that have a check next to them
+                      const checks = Object.keys(facet).filter(value => facet[value] === 'check');
+
+                      // metadata facets that exist
+                      const exists = Object.keys(facet).filter(value => facet[value] === 'exists');
+
+                      // metadata facets that exist
+                      const unsets = Object.keys(facet).filter(value => facet[value] === 'unset');
+
+                      // facets that have an x next to them
+                      const xs = Object.keys(facet).filter(value => facet[value] === 'x');
+
+                      if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
+                      const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
+                      const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
+                      const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined);
+                      const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined);
+                      const satisfiesMatchFacets = !matches.length
+                          ? true
+                          : matches.some(value => {
+                                if (facetKey.startsWith('*')) {
+                                    //  fields starting with a '*' are used to match families of related fields.  ie, *lastModified will match text-lastModified, data-lastModified, etc
+                                    const allKeys = Array.from(Object.keys(d));
+                                    allKeys.push(...Object.keys(Doc.GetProto(d)));
+                                    const keys = allKeys.filter(key => key.includes(facetKey.substring(1)));
+                                    return keys.some(key => Field.toString(d[key] as Field).includes(value));
+                                }
+                                return Field.toString(d[facetKey] as Field).includes(value);
+                            });
+                      // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
+                      if ((parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR') {
+                          if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
+                      }
+                      // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
+                      else {
+                          if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
+                      }
+                  }
+                  return (parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR' ? false : true;
+              })
+            : childDocs;
         const rangeFilteredDocs = filteredDocs.filter(d => {
             for (let i = 0; i < docRangeFilters.length; i += 3) {
                 const key = docRangeFilters[i];
                 const min = Number(docRangeFilters[i + 1]);
                 const max = Number(docRangeFilters[i + 2]);
-                const val = typeof d[key] === "string" ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], "number", null);
+                const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null);
                 if (val === undefined) {
                     //console.log("Should 'undefined' pass range filter or not?")
                 } else if (val < min || val > max) return false;
@@ -1059,7 +1245,7 @@ export namespace DocUtils {
     }
 
     export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) {
-        targetID = targetID.replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+        targetID = targetID.replace(/^-/, '').replace(/\([0-9]*\)$/, '');
         DocServer.GetRefField(targetID).then(doc => {
             if (promoteDoc !== doc) {
                 let copy = doc as Doc;
@@ -1073,16 +1259,17 @@ export namespace DocUtils {
                 remDoc && remDoc(promoteDoc);
                 if (!doc) {
                     DocListCastAsync(promoteDoc.links).then(links => {
-                        links && links.map(async link => {
-                            if (link) {
-                                const a1 = await Cast(link.anchor1, Doc);
-                                if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
-                                const a2 = await Cast(link.anchor2, Doc);
-                                if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
-                                LinkManager.Instance.deleteLink(link);
-                                LinkManager.Instance.addLink(link);
-                            }
-                        });
+                        links &&
+                            links.map(async link => {
+                                if (link) {
+                                    const a1 = await Cast(link.anchor1, Doc);
+                                    if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
+                                    const a2 = await Cast(link.anchor2, Doc);
+                                    if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
+                                    LinkManager.Instance.deleteLink(link);
+                                    LinkManager.Instance.addLink(link);
+                                }
+                            });
                     });
                 }
             }
@@ -1093,19 +1280,19 @@ export namespace DocUtils {
         options?.afterFocus?.(false);
     }
 
-    export let ActiveRecordings: { props: FieldViewProps, getAnchor: () => Doc }[] = [];
+    export let ActiveRecordings: { props: FieldViewProps; getAnchor: () => Doc }[] = [];
 
     export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) {
-        broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1);
+        broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
         return DocUtils.ActiveRecordings.map(audio => {
-            const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline");
-            link && (link.followLinkLocation = "add:right");
+            const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
+            link && (link.followLinkLocation = 'add:right');
             return link;
         });
     }
 
-    export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
-        if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? "Commentary:Comments On" : "link";
+    export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = '', description: string = '', id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
+        if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
         const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return;
         if (target.doc === Doc.UserDoc()) return undefined;
@@ -1114,7 +1301,7 @@ export namespace DocUtils {
             if (showPopup) {
                 LinkManager.currentLink = linkDoc;
 
-                TaskCompletionBox.textDisplayed = "Link Created";
+                TaskCompletionBox.textDisplayed = 'Link Created';
                 TaskCompletionBox.popupX = showPopup[0];
                 TaskCompletionBox.popupY = showPopup[1] - 33;
                 TaskCompletionBox.taskCompleted = true;
@@ -1133,25 +1320,75 @@ export namespace DocUtils {
                     TaskCompletionBox.popupY -= 40;
                 }
 
-                setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
+                setTimeout(
+                    action(() => (TaskCompletionBox.taskCompleted = false)),
+                    2500
+                );
             }
             return linkDoc;
         });
 
-        return makeLink(Docs.Create.LinkDocument(source, target, {
-            title: ComputedField.MakeFunction("generateLinkTitle(self)") as any,
-            "anchor1-useLinkSmallAnchor": source.doc.useLinkSmallAnchor ? true : undefined,
-            "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined,
-            "acl-Public": SharingPermissions.Augment,
-            "_acl-Public": SharingPermissions.Augment,
-            linkDisplay: true,
-            _hidden: true,
-            _linkAutoMove: true,
-            linkRelationship,
-            _showCaption: "description",
-            _showTitle: "linkRelationship",
-            description
-        }, id), showPopup);
+        return makeLink(
+            Docs.Create.LinkDocument(
+                source,
+                target,
+                {
+                    title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
+                    'anchor1-useLinkSmallAnchor': source.doc.useLinkSmallAnchor ? true : undefined,
+                    'anchor2-useLinkSmallAnchor': target.doc.useLinkSmallAnchor ? true : undefined,
+                    'acl-Public': SharingPermissions.Augment,
+                    '_acl-Public': SharingPermissions.Augment,
+                    linkDisplay: true,
+                    _hidden: true,
+                    _linkAutoMove: true,
+                    linkRelationship,
+                    _showCaption: 'description',
+                    _showTitle: 'linkRelationship',
+                    description,
+                },
+                id
+            ),
+            showPopup
+        );
+    }
+
+    export function 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', documentView: Doc.name }, { _readOnly_: true });
+                }
+            });
+        funcs &&
+            Object.keys(funcs).map(key => {
+                const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+                if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) {
+                    doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true });
+                }
+            });
+        return doc;
+    }
+    export function 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];
+                }
+            });
+            items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item));
+            items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item));
+        }
+        return doc;
+    }
+    export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) {
+        return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs);
     }
 
     export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined {
@@ -1160,24 +1397,24 @@ export namespace DocUtils {
         const field = target[fieldKey];
         const resolved = options || {};
         if (field instanceof ImageField) {
-            created = Docs.Create.ImageDocument((field).url.href, resolved);
+            created = Docs.Create.ImageDocument(field.url.href, resolved);
             layout = ImageBox.LayoutString;
         } else if (field instanceof Doc) {
             created = field;
         } else if (field instanceof VideoField) {
-            created = Docs.Create.VideoDocument((field).url.href, resolved);
+            created = Docs.Create.VideoDocument(field.url.href, resolved);
             layout = VideoBox.LayoutString;
         } else if (field instanceof PdfField) {
-            created = Docs.Create.PdfDocument((field).url.href, resolved);
+            created = Docs.Create.PdfDocument(field.url.href, resolved);
             layout = PDFBox.LayoutString;
         } else if (field instanceof AudioField) {
-            created = Docs.Create.AudioDocument((field).url.href, resolved);
+            created = Docs.Create.AudioDocument(field.url.href, resolved);
             layout = AudioBox.LayoutString;
         } else if (field instanceof RecordingField) {
-            created = Docs.Create.RecordingDocument((field).url.href, resolved);
+            created = Docs.Create.RecordingDocument(field.url.href, resolved);
             layout = RecordingBox.LayoutString;
         } else if (field instanceof InkField) {
-            created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
+            created = Docs.Create.InkDocument(ActiveInkColor(), Doc.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);
@@ -1185,9 +1422,8 @@ export namespace DocUtils {
         } else if (field instanceof MapField) {
             created = Docs.Create.MapDocument(DocListCast(field), resolved);
             layout = MapBox.LayoutString;
-        }
-        else {
-            created = Docs.Create.TextDocument("", { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
+        } else {
+            created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
             layout = FormattedTextBox.LayoutString;
         }
         if (created) {
@@ -1199,28 +1435,28 @@ export namespace DocUtils {
     }
 
     export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> {
-        let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
-        if (type.indexOf("image") !== -1) {
+        let ctor: ((path: string, options: DocumentOptions) => Doc | Promise<Doc | undefined>) | undefined = undefined;
+        if (type.indexOf('image') !== -1) {
             ctor = Docs.Create.ImageDocument;
             if (!options._width) options._width = 300;
         }
-        if (type.indexOf("video") !== -1) {
+        if (type.indexOf('video') !== -1) {
             ctor = Docs.Create.VideoDocument;
             if (!options._width) options._width = 600;
-            if (!options._height) options._height = (options._width as number) * 2 / 3;
+            if (!options._height) options._height = ((options._width as number) * 2) / 3;
         }
-        if (type.indexOf("audio") !== -1) {
+        if (type.indexOf('audio') !== -1) {
             ctor = Docs.Create.AudioDocument;
         }
-        if (type.indexOf("pdf") !== -1) {
+        if (type.indexOf('pdf') !== -1) {
             ctor = Docs.Create.PdfDocument;
             if (!options._width) options._width = 400;
-            if (!options._height) options._height = (options._width as number) * 1200 / 927;
+            if (!options._height) options._height = ((options._width as number) * 1200) / 927;
         }
-        if (type.indexOf("csv") !== -1) {
+        if (type.indexOf('csv') !== -1) {
             ctor = Docs.Create.DataVizDocument;
             if (!options._width) options._width = 400;
-            if (!options._height) options._height = (options._width as number) * 1200 / 927;
+            if (!options._height) options._height = ((options._width as number) * 1200) / 927;
         }
         //TODO:al+glr
         // if (type.indexOf("map") !== -1) {
@@ -1228,15 +1464,15 @@ export namespace DocUtils {
         //     if (!options._width) options._width = 800;
         //     if (!options._height) options._height = (options._width as number) * 3 / 4;
         // }
-        if (type.indexOf("html") !== -1) {
+        if (type.indexOf('html') !== -1) {
             if (path.includes(window.location.hostname)) {
                 const s = path.split('/');
                 const id = s[s.length - 1];
                 return DocServer.GetRefField(id).then(field => {
                     if (field instanceof Doc) {
                         const alias = Doc.MakeAlias(field);
-                        alias.x = options.x as number || 0;
-                        alias.y = options.y as number || 0;
+                        alias.x = (options.x as number) || 0;
+                        alias.y = (options.y as number) || 0;
                         alias._width = (options._width as number) || 300;
                         alias._height = (options._height as number) || (options._width as number) || 300;
                         return alias;
@@ -1245,55 +1481,63 @@ export namespace DocUtils {
                 });
             }
             ctor = Docs.Create.WebDocument;
-            options = { ...options, _width: 400, _height: 512, title: path, };
+            options = { ...options, _width: 400, _height: 512, title: path };
         }
-        
+
         return ctor ? ctor(path, options) : undefined;
     }
 
     export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false): void {
-        !simpleMenu && ContextMenu.Instance.addItem({
-            description: "Quick Notes",
-            subitems: DocListCast((Doc.UserDoc()["template-notes"] as Doc).data).map((note, i) => ({
-                description: ":" + StrCast(note.title),
-                event: undoBatch((args: { x: number, y: number }) => {
-                    const textDoc = Docs.Create.TextDocument("", {
-                        _width: 200, x, y, _autoHeight: note._autoHeight !== false,
-                        title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1)
-                    });
-                    textDoc.layoutKey = "layout_" + note.title;
-                    textDoc[textDoc.layoutKey] = note;
-                    docTextAdder(textDoc);
+        !simpleMenu &&
+            ContextMenu.Instance.addItem({
+                description: 'Quick Notes',
+                subitems: DocListCast((Doc.UserDoc()['template-notes'] as Doc).data).map((note, i) => ({
+                    description: ':' + StrCast(note.title),
+                    event: undoBatch((args: { x: number; y: number }) => {
+                        const textDoc = Docs.Create.TextDocument('', {
+                            _width: 200,
+                            x,
+                            y,
+                            _autoHeight: note._autoHeight !== false,
+                            title: StrCast(note.title) + '#' + (note.aliasCount = NumCast(note.aliasCount) + 1),
+                        });
+                        textDoc.layoutKey = 'layout_' + note.title;
+                        textDoc[textDoc.layoutKey] = note;
+                        docTextAdder(textDoc);
+                    }),
+                    icon: StrCast(note.icon) as IconProp,
+                })) as ContextMenuProps[],
+                icon: 'sticky-note',
+            });
+        const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.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 = DocUtils.copyDragFactory(dragDoc);
+                    if (newDoc) {
+                        newDoc.author = Doc.CurrentUserEmail;
+                        newDoc.x = x;
+                        newDoc.y = y;
+                        EquationBox.SelectOnLoad = newDoc[Id];
+                        if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id];
+                        docAdder?.(newDoc);
+                    }
                 }),
-                icon: StrCast(note.icon) as IconProp
-            })) as ContextMenuProps[],
-            icon: "sticky-note"
-        });
-        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 = DocUtils.copyDragFactory(dragDoc);
-                if (newDoc) {
-                    newDoc.author = Doc.CurrentUserEmail;
-                    newDoc.x = x;
-                    newDoc.y = y;
-                    EquationBox.SelectOnLoad = newDoc[Id];
-                    if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id];
-                    docAdder?.(newDoc);
-                }
-            }),
-            icon: Doc.toIcon(dragDoc),
-        })) as ContextMenuProps[];
+                icon: Doc.toIcon(dragDoc),
+            })) as ContextMenuProps[];
         ContextMenu.Instance.addItem({
-            description: "Create document",
+            description: 'Create document',
             subitems: documentList,
-            icon: "file"
+            icon: 'file',
         });
-    }// applies a custom template to a document.  the template is identified by it's short name (e.g, slideView not layout_slideView)
-    export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) {
-        const batch = UndoManager.StartBatch("makeCustomViewClicked");
+    } // applies a custom template to a document.  the template is identified by it's short name (e.g, slideView not layout_slideView)
+    export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
+        const batch = UndoManager.StartBatch('makeCustomViewClicked');
         runInAction(() => {
-            doc.layoutKey = "layout_" + templateSignature;
+            doc.layoutKey = 'layout_' + templateSignature;
             createCustomView(doc, creator, templateSignature, docLayoutTemplate);
         });
         batch.end();
@@ -1301,44 +1545,49 @@ export namespace DocUtils {
     }
     export function findTemplate(templateName: string, type: string, signature: string) {
         let docLayoutTemplate: Opt<Doc>;
-        const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data);
-        const templBtns = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
-        const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
+        const iconViews = DocListCast(Cast(Doc.UserDoc()['template-icons'], Doc, null)?.data);
+        const templBtns = DocListCast(Cast(Doc.UserDoc()['template-buttons'], Doc, null)?.data);
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data);
         const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data);
-        const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc);
+        const allTemplates = iconViews
+            .concat(templBtns)
+            .concat(noteTypes)
+            .concat(clickFuncs)
+            .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc)
+            .filter(doc => doc.isTemplateDoc);
         // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document.  but type is not reliable and there could be other types of template searches so this should be generalized
         // first try to find a template that matches the specific document type (<typeName>_<templateName>).  otherwise, fallback to a general match on <templateName>
-        !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc));
+        !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc));
         !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc));
         return docLayoutTemplate;
     }
-    export function createCustomView(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) {
-        const templateName = templateSignature.replace(/\(.*\)/, "");
-        docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc._isGroup && doc.transcription ? "transcription" : doc.type), templateSignature);
+    export function createCustomView(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
+        const templateName = templateSignature.replace(/\(.*\)/, '');
+        docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc._isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature);
 
-        const customName = "layout_" + templateSignature;
+        const customName = 'layout_' + templateSignature;
         const _width = NumCast(doc._width);
         const _height = NumCast(doc._height);
-        const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false };
+        const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _showSidebar: false };
 
         if (docLayoutTemplate) {
-            if (docLayoutTemplate !== doc[customName]) { 
+            if (docLayoutTemplate !== doc[customName]) {
                 Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined);
             }
         } else {
             let fieldTemplate: Opt<Doc>;
-            if (doc.data instanceof RichTextField || typeof (doc.data) === "string") {
-                fieldTemplate = Docs.Create.TextDocument("", options);
+            if (doc.data instanceof RichTextField || typeof doc.data === 'string') {
+                fieldTemplate = Docs.Create.TextDocument('', options);
             } else if (doc.data instanceof PdfField) {
-                fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options);
+                fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options);
             } else if (doc.data instanceof VideoField) {
-                fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options);
+                fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options);
             } else if (doc.data instanceof AudioField) {
-                fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options);
+                fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options);
             } else if (doc.data instanceof ImageField) {
-                fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+                fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options);
             }
-            const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
+            const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
             fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
             docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
         }
@@ -1350,13 +1599,14 @@ export namespace DocUtils {
         }
     }
     export function iconify(doc: Doc) {
-        const layoutKey = Cast(doc.layoutKey, "string", null);
-        DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined);
-        if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
+        const layoutKey = Cast(doc.layoutKey, 'string', null);
+        DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined);
+        if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') doc.deiconifyLayout = layoutKey.replace('layout_', '');
     }
 
     export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) {
-        let w = 0, h = 0;
+        let w = 0,
+            h = 0;
         runInAction(() => {
             docList.forEach(d => {
                 DocUtils.iconify(d);
@@ -1364,19 +1614,23 @@ export namespace DocUtils {
                 h = Math.max(NumCast(d._height), h);
             });
             docList.forEach((d, i) => {
-                d.x = Math.cos(Math.PI * 2 * i / docList.length) * size;
-                d.y = Math.sin(Math.PI * 2 * i / docList.length) * size;
-                d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size;
+                d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size;
+                d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
             });
-            const aggBounds = aggregateBounds(docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })), 0, 0);
+            const aggBounds = aggregateBounds(
+                docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })),
+                0,
+                0
+            );
             docList.forEach((d, i) => {
-                d.x = NumCast(d.x) - ((aggBounds.r + aggBounds.x) / 2);
-                d.y = NumCast(d.y) - ((aggBounds.b + aggBounds.y) / 2);
-                d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                d.x = NumCast(d.x) - (aggBounds.r + aggBounds.x) / 2;
+                d.y = NumCast(d.y) - (aggBounds.b + aggBounds.y) / 2;
+                d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
             });
         });
         if (create) {
-            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, });
+            const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2 });
             newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size;
             newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size;
             newCollection._width = newCollection._height = size * 2;
@@ -1388,18 +1642,25 @@ export namespace DocUtils {
     export function LeavePushpin(doc: Doc, annotationField: string) {
         if (doc.isPushpin) return undefined;
         const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null);
-        const hasContextAnchor = DocListCast(doc.links).
-            some(l =>
-                (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) ||
-                (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context));
+        const hasContextAnchor = DocListCast(doc.links).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context));
         if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
             const pushpin = Docs.Create.FontIconDocument({
-                title: "pushpin", label: "", annotationOn: Cast(doc.annotationOn, Doc, null), isPushpin: true,
-                icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), backgroundColor: "#ACCEF7",
-                _width: 15, _height: 15, _xPadding: 0, _isLinkButton: true, _timecodeToShow: Cast(doc._timecodeToShow, "number", null)
+                title: 'pushpin',
+                label: '',
+                annotationOn: Cast(doc.annotationOn, Doc, null),
+                isPushpin: true,
+                icon: 'map-pin',
+                x: Cast(doc.x, 'number', null),
+                y: Cast(doc.y, 'number', null),
+                backgroundColor: '#ACCEF7',
+                _width: 15,
+                _height: 15,
+                _xPadding: 0,
+                _isLinkButton: true,
+                _timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
             });
             Doc.AddDocToList(context, annotationField, pushpin);
-            const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", "");
+            const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, 'pushpin', '');
             doc._timecodeToShow = undefined;
             return pushpin;
         }
@@ -1407,7 +1668,7 @@ export namespace DocUtils {
     }
 
     // /**
-    //  * 
+    //  *
     //  * @param dms Degree Minute Second format exif gps data
     //  * @param ref ref that determines negativity of decimal coordinates
     //  * @returns a decimal format of gps latitude / longitude
@@ -1427,7 +1688,7 @@ export namespace DocUtils {
 
     function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) {
         var dd = degrees + minutes / 60 + seconds / (60 * 60);
-        if (direction === "S" || direction === "W") {
+        if (direction === 'S' || direction === 'W') {
             dd = dd * -1;
         } // Don't do anything for N or E
         return dd;
@@ -1444,15 +1705,18 @@ export namespace DocUtils {
         if (doc) {
             const proto = Doc.GetProto(doc);
             proto.text = result.rawText;
-            proto.fileUpload = pathname.replace(/.*\//, "").replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+            proto.fileUpload = pathname
+                .replace(/.*\//, '')
+                .replace('upload_', '')
+                .replace(/\.[a-z0-9]*$/, '');
             if (Upload.isImageInformation(result)) {
                 const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
-                proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation ?? ((StrCast((result.exifData?.data as any)?.Orientation).includes("Rotate 90")) ? 5 : undefined);
-                proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim;
-                proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
-                if (NumCast(proto["data-nativeOrientation"]) >= 5) {
-                    proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim;
-                    proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+                proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (StrCast((result.exifData?.data as any)?.Orientation).includes('Rotate 90') ? 5 : undefined);
+                proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+                proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+                if (NumCast(proto['data-nativeOrientation']) >= 5) {
+                    proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+                    proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
                 }
                 proto.contentSize = result.contentSize;
                 // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
@@ -1464,15 +1728,41 @@ export namespace DocUtils {
                     proto.lat = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection);
                     proto.lng = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
                 }
-
             }
             generatedDocuments.push(doc);
         }
     }
 
+    export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
+        const tbox = Docs.Create.TextDocument('', {
+            _xMargin: noMargins ? 0 : undefined,
+            _yMargin: noMargins ? 0 : undefined,
+            annotationOn,
+            docMaxAutoHeight: maxHeight,
+            backgroundColor: backgroundColor,
+            _width: width || 200,
+            _height: 35,
+            x: x,
+            y: y,
+            _fitWidth: true,
+            _autoHeight: true,
+            title,
+        });
+        const template = Doc.UserDoc().defaultTextLayout;
+        if (template instanceof Doc) {
+            tbox._width = NumCast(template._width);
+            tbox.layoutKey = 'layout_' + StrCast(template.title);
+            Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
+        }
+        return tbox;
+    }
+
     export async function uploadYoutubeVideo(videoId: string, options: DocumentOptions) {
         const generatedDocuments: Doc[] = [];
-        for (const { source: { name, type }, result } of await Networking.UploadYoutubeToServer(videoId)) {
+        for (const {
+            source: { name, type },
+            result,
+        } of await Networking.UploadYoutubeToServer(videoId)) {
             name && type && processFileupload(generatedDocuments, name, type, result, options);
         }
         return generatedDocuments;
@@ -1480,7 +1770,10 @@ export namespace DocUtils {
     export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
         const generatedDocuments: Doc[] = [];
         const upfiles = await Networking.UploadFilesToServer(files);
-        for (const { source: { name, type }, result } of upfiles) {
+        for (const {
+            source: { name, type },
+            result,
+        } of upfiles) {
             name && type && processFileupload(generatedDocuments, name, type, result, options);
         }
         return generatedDocuments;
@@ -1490,36 +1783,43 @@ export namespace DocUtils {
     export function copyDragFactory(dragFactory: Doc) {
         if (!dragFactory) return undefined;
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
-        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);
+        ndoc && Doc.AddDocToList(Doc.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 && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc);
+        if (ndoc && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, ndoc);
 
         return ndoc;
     }
     export function delegateDragFactory(dragFactory: Doc) {
         const ndoc = Doc.MakeDelegateWithProto(dragFactory);
-        if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
-            dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
-            Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString();
+        if (ndoc && dragFactory['dragFactory-count'] !== undefined) {
+            dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1;
+            Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString();
         }
         return ndoc;
     }
 }
 
-ScriptingGlobals.add("Docs", Docs);
-ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return DocUtils.copyDragFactory(dragFactory); });
-ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return DocUtils.delegateDragFactory(dragFactory); });
-ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; });
+ScriptingGlobals.add('Docs', Docs);
+ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) {
+    return DocUtils.copyDragFactory(dragFactory);
+});
+ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) {
+    return DocUtils.delegateDragFactory(dragFactory);
+});
+ScriptingGlobals.add(function makeDelegate(proto: any) {
+    const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title });
+    return d;
+});
 ScriptingGlobals.add(function generateLinkTitle(self: Doc) {
-    const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : "<?>";
-    const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : "<?>";
-    const relation = self.linkRelationship || "to";
+    const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : '<?>';
+    const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : '<?>';
+    const relation = self.linkRelationship || 'to';
     return `${anchor1title} (${relation}) ${anchor2title}`;
 });
 ScriptingGlobals.add(function openTabAlias(tab: Doc) {
-    CollectionDockingView.AddSplit(Doc.MakeAlias(tab), "right");
-});
\ No newline at end of file
+    CollectionDockingView.AddSplit(Doc.MakeAlias(tab), 'right');
+});
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 84efcb966..02d43088d 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -2,7 +2,6 @@ import { computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
 import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } 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";
@@ -14,11 +13,12 @@ import { SharingPermissions } from "../../fields/util";
 import { OmitKeys, Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
-import { DocumentType } from "../documents/DocumentTypes";
+import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { TreeViewType } from "../views/collections/CollectionTreeView";
-import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { CollectionView } from "../views/collections/CollectionView";
 import { TreeView } from "../views/collections/TreeView";
+import { DashboardView } from "../views/DashboardView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
@@ -57,59 +57,6 @@ interface Button {
 
 export let resolvedPorts: { server: number, socket: number };
 export class CurrentUserUtils {
-    private static curr_id: string;
-    //TODO tfs: these should be temporary...
-    private static mainDocId: string | undefined;
-
-    public static get id() { return this.curr_id; }
-    public static get MainDocId() { return this.mainDocId; }
-    public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
-    @computed public static get UserDocument() { return Doc.UserDoc(); }
-
-    @observable public static GuestTarget: Doc | undefined;
-    @observable public static GuestDashboard: Doc | undefined;
-    @observable public static GuestMobile: Doc | undefined;
-    @observable public static propertiesWidth: number = 0;
-    @observable public static headerBarHeight: number = 0;
-    @observable public static searchPanelWidth: number = 0;
-
-    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", documentView:Doc.name}, {"_readOnly_": true});
-            }
-        }); 
-        funcs && Object.keys(funcs).map(key => {
-            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) {
-                doc[key] = ComputedField.MakeFunction(funcs[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];
-                }
-            });
-            items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), "data", item));
-            items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), "data", item));
-        }
-        return doc;
-    }
-    static AssignDocField(doc:Doc, field:string, creator:(reqdOpts:DocumentOptions, items?:Doc[]) => Doc, reqdOpts:DocumentOptions, items?: Doc[], scripts?:{[key:string]:string}, funcs?:{[key:string]:string}) {
-        return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs);
-    }
 
     // initializes experimental advanced template views - slideView,  headerView
     static setupExperimentalTemplateButtons(doc: Doc, tempDocs?:Doc) {
@@ -139,12 +86,12 @@ export class CurrentUserUtils {
             const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory); }' };
             const assignBtnAndTempOpts = (templateBtn:Opt<Doc>, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => {
                 if (templateBtn) {
-                    this.AssignOpts(templateBtn,btnOpts);
-                    this.AssignDocField(templateBtn, "dragFactory", opts => template(opts), templateOptions);
+                    DocUtils.AssignOpts(templateBtn,btnOpts);
+                    DocUtils.AssignDocField(templateBtn, "dragFactory", opts => template(opts), templateOptions);
                 }
                 return templateBtn;
             };
-            return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts);
+            return DocUtils.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts);
         });
 
         const reqdOpts:DocumentOptions = {
@@ -154,7 +101,7 @@ export class CurrentUserUtils {
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
         const reqdFuncs = { hidden: "IsNoviceMode()" };
-        return this.AssignScripts(this.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
+        return DocUtils.AssignScripts(DocUtils.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
     }
 
     /// Initializes templates that can be applied to notes
@@ -167,16 +114,16 @@ export class CurrentUserUtils {
         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;
-            return this.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
+            return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
         });
 
         const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true };
-        return this.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
+        return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
     }
 
     /// Initializes collection of templates for notes and click functions
     static setupDocTemplates(doc: Doc, field="myTemplates") {
-        this.AssignDocField(doc, "presElement", opts => Docs.Create.PresElementBoxDocument(opts), { title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"});
+        DocUtils.AssignDocField(doc, "presElement", opts => Docs.Create.PresElementBoxDocument(opts), { title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"});
         const templates = [
             DocCast(doc.presElement),
             CurrentUserUtils.setupNoteTemplates(doc),
@@ -184,13 +131,13 @@ export class CurrentUserUtils {
         ];
         const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
         const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
-        return this.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts);
+        return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
     static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
         const reqdOpts = { title: "icon templates", _height: 75, system: true };
-        const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+        const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
         const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
             const iconFieldName = "icon" + (type ? "_" + type : "");
@@ -201,8 +148,8 @@ export class CurrentUserUtils {
                 case DocumentType.FONTICON: creator = fontBox; break;
             }
             const allopts = {system: true, ...opts};
-            return this.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
-                      this.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
+            return DocUtils.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
+                      DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
                 {onClick:"deiconifyView(documentView)"});
         };
         const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
@@ -224,7 +171,7 @@ export class CurrentUserUtils {
             makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
             //makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
         ].filter(d => d).map(d => d!);
-        this.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
+        DocUtils.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
     }
 
     /// initalizes the set of "empty<DocType>" versions of each document type with default fields.  e.g.,. emptyNote, emptyPresentation
@@ -297,7 +244,7 @@ export class CurrentUserUtils {
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
 
-        emptyThings.forEach(thing => this.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs));
+        emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs));
         
         return [
             { toolTip: "Tap or drag to create a note",            title: "Note", icon: "sticky-note",   dragFactory: doc.emptyNote as Doc, },
@@ -326,7 +273,7 @@ export class CurrentUserUtils {
                 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);
+            return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
         });
 
         const reqdOpts:DocumentOptions = {
@@ -335,7 +282,7 @@ export class CurrentUserUtils {
             childDocumentsActive: true
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
-        return this.AssignScripts(this.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
+        return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
     }
 
     /// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
@@ -348,7 +295,7 @@ export class CurrentUserUtils {
             { title: "Tools",           target: this.setupToolsBtnPanel(doc, "myTools"),           icon: "wrench",      funcs: {hidden: "IsNoviceMode()"} },
             { title: "Imports",         target: this.setupImportSidebar(doc, "myImports"),         icon: "upload",      },
             { title: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive",     },
-            { title: "Shared Docs",     target: this.MySharedDocs,                                 icon: "users",       funcs:{badgeValue:badgeValue}},
+            { title: "Shared Docs",     target: Doc.MySharedDocs,                                  icon: "users",       funcs:{badgeValue:badgeValue}},
             { title: "Trails",          target: this.setupTrails(doc, "myTrails"),                 icon: "pres-trail",  },
             { title: "User Doc View",   target: this.setupUserDocView(doc, "myUserDocView"),       icon: "address-card",funcs: {hidden: "IsNoviceMode()"} },
         ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}}));
@@ -356,7 +303,7 @@ export class CurrentUserUtils {
 
     /// the empty panel that is filled with whichever left menu button's panel has been selected
     static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
-        this.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true});
+        DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true});
     }
 
     /// Initializes the left sidebar menu buttons and the panels they open up
@@ -370,20 +317,20 @@ export class CurrentUserUtils {
                 _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);
+            return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
         });
 
         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
         };
-        return this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
+        return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
     }
 
     // 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.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
+        DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
     }
 
     // Sets up mobile buttons for inside mobile menu
@@ -436,42 +383,6 @@ export class CurrentUserUtils {
     }) as any as Doc
 
 
-    static setupThumbButtons(doc: Doc) {
-        const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, clipboard?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
-            { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue" },
-            { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow" },
-            { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }), backgroundColor: "orange" },
-            { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange" },
-            { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green" },
-        ];
-        return docProtoData.map(data => Docs.Create.FontIconDocument({
-            _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon,
-            _dropAction: data.pointerDown ? "copy" : undefined,
-            ignoreClick: data.ignoreClick,
-            onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
-            clipboard: data.clipboard,
-            onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
-            onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
-            backgroundColor: data.backgroundColor,
-            _removeDropProperties: new List<string>(["dropAction"]),
-            dragFactory: data.dragFactory,
-            system: true
-        }));
-    }
-
-    static setupThumbDoc(userDoc: Doc) {
-        if (!userDoc.thumbDoc) {
-            const thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), {
-                _width: 100, _height: 50, ignoreClick: true, _lockedPosition: true, title: "buttons",
-                _autoHeight: true, _yMargin: 5, linearViewIsExpanded: true, backgroundColor: "white", system: true
-            });
-            thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
-                _width: 300, _height: 25, _autoHeight: true, linearViewIsExpanded: true, flexDirection: "column", system: true
-            });
-            userDoc.thumbDoc = thumbDoc;
-        }
-        return Cast(userDoc.thumbDoc, Doc);
-    }
 
     static setupMobileInkingDoc(userDoc: Doc) {
         return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true });
@@ -492,7 +403,7 @@ export class CurrentUserUtils {
 
     /// Search option on the left side button panel
     static setupSearcher(doc: Doc, field:string) {
-        return this.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
+        return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
                 dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", 
                 _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true,  });
     }
@@ -506,7 +417,7 @@ export class CurrentUserUtils {
             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, 
         };
-        return this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
+        return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
     }
 
     /// initializes the left sidebar dashboard pane
@@ -517,7 +428,7 @@ export class CurrentUserUtils {
         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 newDashboardButton = DocUtils.AssignScripts(DocUtils.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,
@@ -530,7 +441,7 @@ export class CurrentUserUtils {
             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.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+        myDashboards = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), 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
@@ -553,7 +464,7 @@ export class CurrentUserUtils {
         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 newTrailButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
         
         const reqdOpts:DocumentOptions = {
             title: "My Trails", _showTitle: "title", _height: 100,
@@ -564,7 +475,7 @@ export class CurrentUserUtils {
             _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.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+        myTrails = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), 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)!));
@@ -575,7 +486,7 @@ export class CurrentUserUtils {
     /// initializes the left sidebar File system pane
     static setupFilesystem(doc: Doc, field:string) {
         var myFilesystem = DocCast(doc[field]);
-        const myFileOrphans = this.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
+        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
         
         const newFolder = `makeTopLevelFolder()`;
         const newFolderOpts: DocumentOptions = {
@@ -583,7 +494,7 @@ export class CurrentUserUtils {
             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 newFolderButton = DocUtils.AssignScripts(DocUtils.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,
@@ -593,7 +504,7 @@ export class CurrentUserUtils {
             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.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
+        myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
         const childContextMenuScripts = [newFolder];
         if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
             myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
@@ -610,13 +521,13 @@ export class CurrentUserUtils {
             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.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+        const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), 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 clearDocsButton = this.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
+        const clearDocsButton = DocUtils.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
 
         if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton;
         
@@ -626,19 +537,6 @@ export class CurrentUserUtils {
         return recentlyClosed;
     }
 
-    /// 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);
-    }
-
     /// initializes the left sidebar panel view of the UserDoc
     static setupUserDocView(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = {
@@ -646,8 +544,8 @@ export class CurrentUserUtils {
             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" });
-        return this.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]);
+        if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
+        return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]);
     }
 
     static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
@@ -666,7 +564,7 @@ export class CurrentUserUtils {
     static setupDockedButtons(doc: Doc, field="myDockedBtns") {
         const dockedBtns = DocCast(doc[field]);
         const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string}) =>
-            this.AssignScripts(this.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? 
+            DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? 
                             CurrentUserUtils.createToolButton(opts), scripts);
         
         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
@@ -680,7 +578,7 @@ export class CurrentUserUtils {
         };
         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.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
+        return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
     }
 
     static textTools():Button[] {
@@ -768,13 +666,13 @@ export class CurrentUserUtils {
             ...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);
+        return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
     }
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
         const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
-        const ctxtMenuBtnsDoc = this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
+        const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
             if (!params.subMenu) {
@@ -786,20 +684,20 @@ export class CurrentUserUtils {
                 const items = params.subMenu?.map(sub =>
                     this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title))
                 );
-                return this.AssignScripts(
-                    this.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), undefined, params.funcs);
+                return DocUtils.AssignScripts(
+                    DocUtils.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), undefined, params.funcs);
             }
         });
-        return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
+        return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
     }
 
     /// collection of documents rendered in the overlay layer above all tabs and other UI
     static setupOverlays(doc: Doc, field = "myOverlayDocs") {
-        return this.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true });
+        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true });
     }
 
     static setupPublished(doc:Doc, field = "myPublishedDocs") {
-        return this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true });
+        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true });
     }
     
     /// The database of all links on all documents
@@ -838,7 +736,7 @@ export class CurrentUserUtils {
             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'"
         };
         
-        this.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
+        DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
     }
 
     /// Import option on the left side button panel 
@@ -849,12 +747,12 @@ export class CurrentUserUtils {
             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.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
+        const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), 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 };
-        this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
+        DocUtils.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
         return myImports;
     }
 
@@ -909,7 +807,7 @@ export class CurrentUserUtils {
     /// written to the server if the code hasn't changed.  However, choices need to be made for each Doc/field
     /// whether to revert to "default" values, or to leave them as the user/system last set them.
     static updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
-        this.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
+        DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
         reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]),
             async () => {
                 const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
@@ -948,8 +846,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
         this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
-        this.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        this.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
         
         if (doc.activeDashboard instanceof Doc) {
             // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
@@ -962,7 +860,7 @@ export class CurrentUserUtils {
     }
     static setupFieldInfos(doc:Doc, field="fieldInfos") {
         const fieldInfoOpts = { title: "Field Infos", system: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
-        const infos = this.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
+        const infos = DocUtils.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
         const entries = Object.entries(new DocumentOptions());
         entries.forEach(pair => {
             if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) {
@@ -974,7 +872,7 @@ export class CurrentUserUtils {
                     case Doc.name: opts.fieldValues = new List<Doc>(options.values as any); break;
                     default: opts.fieldValues = new List<string>(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType
                 }
-                this.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
+                DocUtils.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
             }
         });
     }
@@ -995,7 +893,6 @@ export class CurrentUserUtils {
     }
 
     public static async loadUserDocument(id: string) {
-        this.curr_id = id;
         await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
             const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids);
             if (userDocumentId !== "guest") {
@@ -1012,46 +909,6 @@ export class CurrentUserUtils {
         });
     }
 
-    public static _urlState: HistoryUtil.DocUrl;
-
-    /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there).
-    /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url)
-    public static openDashboard = (doc: Doc|undefined, fromHistory = false) => {
-        if (!doc) return false;
-        CurrentUserUtils.MainDocId = doc[Id];
-        Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc);
-
-        // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
-        Doc.UserDoc() ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
-            
-        const state = CurrentUserUtils._urlState;
-        if (state.sharing === true && !Doc.UserDoc()) {
-            DocServer.Control.makeReadOnly();
-        } else {
-            fromHistory || HistoryUtil.pushState({
-                type: "doc",
-                docId: doc[Id],
-                readonly: state.readonly,
-                nro: state.nro,
-                sharing: false,
-            });
-            if (state.readonly === true || state.readonly === null) {
-                DocServer.Control.makeReadOnly();
-            } else if (state.safe) {
-                if (!state.nro) {
-                    DocServer.Control.makeReadOnly();
-                }
-                CollectionView.SetSafeMode(true);
-            } else if (state.nro || state.nro === null || state.readonly === false) {
-            } else if (doc.readOnly) {
-                DocServer.Control.makeReadOnly();
-            } else {
-                DocServer.Control.makeEditable();
-            }
-        }
-
-        return true;
-    }
 
     public static importDocument = () => {
         const input = document.createElement("input");
@@ -1068,7 +925,7 @@ export class CurrentUserUtils {
                 //     setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs =>
                 //         docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added.
                 // }
-                const list = Cast(CurrentUserUtils.MyImports.data, listSpec(Doc), null);
+                const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
                 doc instanceof Doc && list?.splice(0, 0, doc);
             } else if (input.files && input.files.length !== 0) {
                 const disposer = OverlayView.ShowSpinner();
@@ -1076,7 +933,7 @@ export class CurrentUserUtils {
                 if (results.length !== input.files?.length) {
                     alert("Error uploading files - possibly due to unsupported file types");
                 }
-                const list = Cast(CurrentUserUtils.MyImports.data, listSpec(Doc), null);
+                const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
                 list?.splice(0, 0, ...results);
                 disposer();
             } else {
@@ -1085,104 +942,20 @@ export class CurrentUserUtils {
         };
         input.click();
     }
-    
-    public static snapshotDashboard() { return CollectionDockingView.TakeSnapshot(CurrentUserUtils.ActiveDashboard);  }
-
-    public static closeActiveDashboard = () => { CurrentUserUtils.ActiveDashboard = undefined; }
-
-    public static removeDashboard = async (dashboard:Doc) => {
-        const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
-        if (dashboards?.length) {
-            if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard));
-            Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard);
-            if (!dashboards.length) CurrentUserUtils.ActivePage = "home";
-        }
-    }
-    public static createNewDashboard = (id?: string, name?: 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,
-            y: 400,
-            _width: 1500,
-            _height: 1000,
-            _fitWidth: true,
-            _backgroundGridShow: true,
-            title: `Untitled Tab 1`,
-        };
-        const title = name ? name : `Dashboard ${dashboardCount}`
-        const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, "row");
-        freeformDoc.context = dashboardDoc;
-
-        // switching the tabs from the datadoc to the regular doc
-        const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
-        dashboardDoc.data = new List<Doc>(dashboardTabs);
-        dashboardDoc["pane-count"] = 1;
-
-        CurrentUserUtils.ActivePresentation = presentation;
-
-        Doc.AddDocToList(dashboards, "data", dashboardDoc);
-        // open this new dashboard 
-        CurrentUserUtils.ActiveDashboard = dashboardDoc;
-        CurrentUserUtils.ActivePage = "dashboard";
-    }
-
-    public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
-        const tbox = Docs.Create.TextDocument("", {
-            _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, backgroundColor: backgroundColor,
-            _width: width || 200, _height: 35, x: x, y: y, _fitWidth: true, _autoHeight: true, title
-        });
-        const template = Doc.UserDoc().defaultTextLayout;
-        if (template instanceof Doc) {
-            tbox._width = NumCast(template._width);
-            tbox.layoutKey = "layout_" + StrCast(template.title);
-            Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
-        }
-        return tbox;
-    }
-
-    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 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); }
-    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 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); }
-    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 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 MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
+ScriptingGlobals.add(function MySharedDocs() { return Doc.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(); }, "creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(); }, "creates a new dashboard when called");
+ScriptingGlobals.add(function snapshotDashboard() { DashboardView.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
+ScriptingGlobals.add(function createNewDashboard() { return DashboardView.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)");
 ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
 ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard");
-ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards");
-ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {  CurrentUserUtils.openDashboard( Doc.MakeAlias(dashboard)); },  "adds Dashboard to set of Dashboards");
+ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { DashboardView.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards");
+ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {  DashboardView.openDashboard( Doc.MakeAlias(dashboard)); },  "adds Dashboard to set of Dashboards");
 ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) {
     let selected = (sel => checkContext ? DocCast(sel?.context) : sel)(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
     return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true;
@@ -1190,5 +963,5 @@ ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colTy
 ScriptingGlobals.add(function makeTopLevelFolder() {
     TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
     const opts = { title: "Untitled folder", _stayInCollection: true, isFolder: true };
-    return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
+    return Doc.AddDocToList(Doc.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
 });
\ No newline at end of file
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 8473ce703..d3ac2f03f 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -4,44 +4,43 @@ import { Id } from '../../fields/FieldSymbols';
 import { Cast } from '../../fields/Types';
 import { returnFalse } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
-import { CollectionDockingView } from '../views/collections/CollectionDockingView';
-import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
-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 { CollectionDockingView } from '../views/collections/CollectionDockingView';
+import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
+import { CollectionView } from '../views/collections/CollectionView';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
 
 export class DocumentManager {
-
     //global holds all of the nodes (regardless of which collection they're in)
     @observable public DocumentViews = new Set<DocumentView>();
     @observable public LinkAnchorBoxViews: DocumentView[] = [];
     @observable public RecordingEvent = 0;
-    @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = [];
+    @observable public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = [];
 
     private static _instance: DocumentManager;
-    public static get Instance(): DocumentManager { return this._instance || (this._instance = new this()); }
+    public static get Instance(): DocumentManager {
+        return this._instance || (this._instance = new this());
+    }
 
     //private constructor so no other class can create a nodemanager
-    private constructor() { }
+    private constructor() {}
 
     @action
     public AddView = (view: DocumentView) => {
         //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
         if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
-            const viewAnchorIndex = view.props.LayoutTemplateString.includes("anchor2") ? "anchor2" : "anchor1";
+            const viewAnchorIndex = view.props.LayoutTemplateString.includes('anchor2') ? 'anchor2' : 'anchor1';
             DocListCast(view.rootDoc.links).forEach(link => {
-                this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).
-                    forEach(otherView => this.LinkedDocumentViews.push(
-                        {
-                            a: viewAnchorIndex === "anchor2" ? otherView : view,
-                            b: viewAnchorIndex === "anchor2" ? view : otherView,
-                            l: link
-                        })
-                    );
+                this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView =>
+                    this.LinkedDocumentViews.push({
+                        a: viewAnchorIndex === 'anchor2' ? otherView : view,
+                        b: viewAnchorIndex === 'anchor2' ? view : otherView,
+                        l: link,
+                    })
+                );
             });
             this.LinkAnchorBoxViews.push(view);
             // this.LinkedDocumentViews.forEach(view => console.log("  LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " +
@@ -49,14 +48,16 @@ export class DocumentManager {
         } else {
             this.DocumentViews.add(view);
         }
-    }
+    };
     public RemoveView = action((view: DocumentView) => {
-        this.LinkedDocumentViews.slice().forEach(action(pair => {
-            if (pair.a === view || pair.b === view) {
-                const li = this.LinkedDocumentViews.indexOf(pair);
-                li !== -1 && this.LinkedDocumentViews.splice(li, 1);
-            }
-        }));
+        this.LinkedDocumentViews.slice().forEach(
+            action(pair => {
+                if (pair.a === view || pair.b === view) {
+                    const li = this.LinkedDocumentViews.indexOf(pair);
+                    li !== -1 && this.LinkedDocumentViews.splice(li, 1);
+                }
+            })
+        );
 
         if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
             const index = this.LinkAnchorBoxViews.indexOf(view);
@@ -125,11 +126,11 @@ export class DocumentManager {
         const views: DocumentView[] = [];
         Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
-    }
+    };
     public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
         const views = this.getDocumentViews(toFind).filter(view => view.rootDoc !== originatingDoc);
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
-    }
+    };
     public getDocumentViews(toFind: Doc): DocumentView[] {
         const toReturn: DocumentView[] = [];
         const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
@@ -146,17 +147,16 @@ export class DocumentManager {
         return toReturn;
     }
 
-
     static addView = (doc: Doc, finished?: () => void) => {
-        CollectionDockingView.AddSplit(doc, "right");
+        CollectionDockingView.AddSplit(doc, 'right');
         finished?.();
-    }
+    };
     public jumpToDocument = async (
-        targetDoc: Doc,        // document to display
-        willZoom: boolean,     // whether to zoom doc to take up most of screen
+        targetDoc: Doc, // document to display
+        willZoom: boolean, // whether to zoom doc to take up most of screen
         createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist
-        docContext: Doc[],  // context to load that should contain the target
-        linkDoc?: Doc,   // link that's being followed
+        docContext: Doc[], // context to load that should contain the target
+        linkDoc?: Doc, // link that's being followed
         closeContextIfNotFound: boolean = false, // after opening a context where the document should be, this determines whether the context should be closed if the Doc isn't actually there
         originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc
         finished?: () => void,
@@ -172,14 +172,15 @@ export class DocumentManager {
         var wasHidden = resolvedTarget.hidden;
         if (wasHidden) {
             runInAction(() => {
-                resolvedTarget.hidden = false;  // if the target is hidden, un-hide it here.
+                resolvedTarget.hidden = false; // if the target is hidden, un-hide it here.
                 docView?.props.bringToFront(resolvedTarget);
             });
         }
         const focusAndFinish = (didFocus: boolean) => {
             const finalTargetDoc = resolvedTarget;
             if (originatingDoc?.isPushpin) {
-                if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
+                if (!didFocus && !wasHidden) {
+                    // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
                     finalTargetDoc.hidden = !finalTargetDoc.hidden;
                 }
             } else {
@@ -192,77 +193,92 @@ export class DocumentManager {
         const contextDocs = docContext.length ? await DocListCastAsync(docContext[0].data) : undefined;
         const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext.lastElement() : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
-        const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||
-            (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
+        const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
         if (annoContainerView) {
-            if (annoContainerView.props.Document.layoutKey === "layout_icon") {
-                annoContainerView.iconify(() => annoContainerView.focus(targetDoc, {
-                    originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
-                        new Promise<ViewAdjustment>(res => {
-                            focusAndFinish(true);
-                            res(ViewAdjustment.doNothing);
-                        })
-                }));
+            if (annoContainerView.props.Document.layoutKey === 'layout_icon') {
+                annoContainerView.iconify(() =>
+                    annoContainerView.focus(targetDoc, {
+                        originalTarget,
+                        willZoom,
+                        scale: presZoom,
+                        afterFocus: (didFocus: boolean) =>
+                            new Promise<ViewAdjustment>(res => {
+                                focusAndFinish(true);
+                                res(ViewAdjustment.doNothing);
+                            }),
+                    })
+                );
                 return;
             } else if (!docView && targetDoc.type !== DocumentType.MARKER) {
-                annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
+                annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
             }
         }
         if (focusView) {
             !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
-            const doFocus = (forceDidFocus: boolean) => focusView.focus(originalTarget ?? targetDoc, {
-                originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
-                    new Promise<ViewAdjustment>(res => {
-                        focusAndFinish(forceDidFocus || didFocus);
-                        res(ViewAdjustment.doNothing);
-                    })
-            });
-            if (focusView.props.Document.layoutKey === "layout_icon") {
+            const doFocus = (forceDidFocus: boolean) =>
+                focusView.focus(originalTarget ?? targetDoc, {
+                    originalTarget,
+                    willZoom,
+                    scale: presZoom,
+                    afterFocus: (didFocus: boolean) =>
+                        new Promise<ViewAdjustment>(res => {
+                            focusAndFinish(forceDidFocus || didFocus);
+                            res(ViewAdjustment.doNothing);
+                        }),
+                });
+            if (focusView.props.Document.layoutKey === 'layout_icon') {
                 focusView.iconify(() => doFocus(true));
             } else {
                 doFocus(false);
             }
         } else {
-            if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
+            if (!targetDocContext) {
+                // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
                 createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
-            } else {  // otherwise try to get a view of the context of the target
-                if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
+            } else {
+                // otherwise try to get a view of the context of the target
+                if (targetDocContextView) {
+                    // we found a context view and aren't forced to create a new one ... focus on the context first..
                     wasHidden = wasHidden || targetDocContextView.rootDoc.hidden;
                     targetDocContextView.rootDoc.hidden = false; // make sure context isn't hidden
-                    targetDocContext._viewTransition = "transform 500ms";
+                    targetDocContext._viewTransition = 'transform 500ms';
                     targetDocContextView.props.focus(targetDocContextView.rootDoc, {
-                        willZoom, afterFocus: async () => {
+                        willZoom,
+                        afterFocus: async () => {
                             targetDocContext._viewTransition = undefined;
-                            if (targetDocContext.layoutKey === "layout_icon") {
-                                targetDocContextView.iconify(() => this.jumpToDocument(
-                                    resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
-                                    finished, originalTarget, noSelect, presZoom));
+                            if (targetDocContext.layoutKey === 'layout_icon') {
+                                targetDocContextView.iconify(() => this.jumpToDocument(resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom));
                             }
                             return ViewAdjustment.doNothing;
-                        }
+                        },
                     });
 
                     // now find the target document within the context
-                    if (targetDoc._timecodeToShow) {  // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
+                    if (targetDoc._timecodeToShow) {
+                        // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
                         targetDocContext._currentTimecode = targetDoc.anchorTimecodeToShow;
                         finished?.();
-                    } else { // no timecode means we need to find the context view and focus on our target
+                    } else {
+                        // no timecode means we need to find the context view and focus on our target
                         const findView = (delay: number) => {
-                            const retryDocView = getFirstDocView(resolvedTarget);  // test again for the target view snce we presumably created the context above by focusing on it
-                            if (retryDocView) {   // we found the target in the context. 
+                            const retryDocView = getFirstDocView(resolvedTarget); // test again for the target view snce we presumably created the context above by focusing on it
+                            if (retryDocView) {
+                                // we found the target in the context.
                                 Doc.linkFollowHighlight(retryDocView.rootDoc);
                                 retryDocView.focus(targetDoc, {
-                                    willZoom, afterFocus: (didFocus: boolean) =>
+                                    willZoom,
+                                    afterFocus: (didFocus: boolean) =>
                                         new Promise<ViewAdjustment>(res => {
                                             !noSelect && focusAndFinish(true);
                                             res(ViewAdjustment.doNothing);
-                                        })
+                                        }),
                                 }); // focus on the target in the context
                             } else if (delay > 1000) {
                                 // we didn't find the target, so it must have moved out of the context.  Go back to just creating it.
                                 if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.rootDoc);
-                                if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document
+                                if (targetDoc.layout) {
+                                    // there will no layout for a TEXTANCHOR type document
                                     createViewFunc(Doc.BrushDoc(targetDoc), finished); //  create a new view of the target
                                 }
                             } else {
@@ -272,22 +288,24 @@ export class DocumentManager {
                         setTimeout(() => findView(0), 0);
                     }
                 } else {
-                    if (docContext.length && docContext[0]?.layoutKey === "layout_icon") {
+                    if (docContext.length && docContext[0]?.layoutKey === 'layout_icon') {
                         const docContextView = this.getFirstDocumentView(docContext[0]);
                         if (docContextView) {
-                            return docContextView.iconify(() => this.jumpToDocument(
-                                targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc,
-                                finished, originalTarget, noSelect, presZoom));
+                            return docContextView.iconify(() =>
+                                this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom)
+                            );
                         }
                     }
                     // there's no context view so we need to create one first and try again when that finishes
                     const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget);
-                    createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
-                        finishFunc);
+                    createViewFunc(
+                        targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
+                        finishFunc
+                    );
                 }
             }
         }
-    }
+    };
 }
 export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
     const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc);
@@ -295,14 +313,12 @@ export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
     if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) {
         dv.props.focus(dv.props.Document, { willZoom: true });
         Doc.linkFollowHighlight(dv?.props.Document, false);
-    }
-    else {
-        const context = doc.context !== CurrentUserUtils.MyFilesystem && Cast(doc.context, Doc, null);
+    } else {
+        const context = doc.context !== Doc.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;
 
-        CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), "right") && context &&
-            setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));
+        CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), 'right') && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));
     }
 }
-ScriptingGlobals.add(DocFocusOrOpen);
\ No newline at end of file
+ScriptingGlobals.add(DocFocusOrOpen);
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 7dcff9c56..18aee6444 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,8 +1,8 @@
-import { Doc } from "../../fields/Doc";
-import { DocServer } from "../DocServer";
 import * as qs from 'query-string';
-import { Utils, OmitKeys } from "../../Utils";
-import { CurrentUserUtils } from "./CurrentUserUtils";
+import { Doc } from '../../fields/Doc';
+import { OmitKeys, Utils } from '../../Utils';
+import { DocServer } from '../DocServer';
+import { DashboardView } from '../views/DashboardView';
 
 export namespace HistoryUtil {
     export interface DocInitializerList {
@@ -10,7 +10,7 @@ export namespace HistoryUtil {
     }
 
     export interface DocUrl {
-        type: "doc";
+        type: 'doc';
         docId: string;
         initializers?: {
             [docId: string]: DocInitializerList;
@@ -25,11 +25,11 @@ export namespace HistoryUtil {
 
     // const handlers: ((state: ParsedUrl | null) => void)[] = [];
     function onHistory(e: PopStateEvent) {
-        if (window.location.pathname !== "/home") {
-            const url = e.state as ParsedUrl || parseUrl(window.location);
+        if (window.location.pathname !== '/home') {
+            const url = (e.state as ParsedUrl) || parseUrl(window.location);
             if (url) {
                 switch (url.type) {
-                    case "doc":
+                    case 'doc':
                         onDocUrl(url);
                         break;
                 }
@@ -43,13 +43,13 @@ export namespace HistoryUtil {
     let _lastStatePush = 0;
     export function pushState(state: ParsedUrl) {
         if (Date.now() - _lastStatePush > 1000) {
-            history.pushState(state, "", createUrl(state));
+            history.pushState(state, '', createUrl(state));
         }
         _lastStatePush = Date.now();
     }
 
     export function replaceState(state: ParsedUrl) {
-        history.replaceState(state, "", createUrl(state));
+        history.replaceState(state, '', createUrl(state));
     }
 
     function copyState(state: ParsedUrl): ParsedUrl {
@@ -61,7 +61,7 @@ export namespace HistoryUtil {
         if (state) {
             state.initializers = state.initializers || {};
         }
-        return state ?? {initializers:{}};
+        return state ?? { initializers: {} };
     }
 
     // export function addHandler(handler: (state: ParsedUrl | null) => void) {
@@ -78,10 +78,10 @@ export namespace HistoryUtil {
     const parsers: { [type: string]: (pathname: string[], opts: qs.ParsedQuery) => ParsedUrl | undefined } = {};
     const stringifiers: { [type: string]: (state: ParsedUrl) => string } = {};
 
-    type ParserValue = true | "none" | "json" | ((value: string) => any);
+    type ParserValue = true | 'none' | 'json' | ((value: string) => any);
 
     type Parser = {
-        [key: string]: ParserValue
+        [key: string]: ParserValue;
     };
 
     function addParser(type: string, requiredFields: Parser, optionalFields: Parser, customParser?: (pathname: string[], opts: qs.ParsedQuery, current: ParsedUrl) => ParsedUrl | null | undefined) {
@@ -90,9 +90,9 @@ export namespace HistoryUtil {
                 return value;
             }
             if (Array.isArray(value)) {
-            } else if (parser === true || parser === "json") {
+            } else if (parser === true || parser === 'json') {
                 value = JSON.parse(value);
-            } else if (parser === "none") {
+            } else if (parser === 'none') {
             } else {
                 value = parser(value);
             }
@@ -142,29 +142,28 @@ export namespace HistoryUtil {
             }
             const queryObj = OmitKeys(state, keys).extract;
             const query: any = {};
-            Object.keys(queryObj).forEach(key => query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key]));
+            Object.keys(queryObj).forEach(key => (query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key])));
             const queryString = qs.stringify(query);
-            return path + (queryString ? `?${queryString}` : "");
+            return path + (queryString ? `?${queryString}` : '');
         };
     }
 
-    addParser("doc", {}, { readonly: true, initializers: true, nro: true, sharing: true }, (pathname, opts, current) => {
+    addParser('doc', {}, { readonly: true, initializers: true, nro: true, sharing: true }, (pathname, opts, current) => {
         if (pathname.length !== 2) return undefined;
 
         current.initializers = current.initializers || {};
         const docId = pathname[1];
         current.docId = docId;
     });
-    addStringifier("doc", ["initializers", "readonly", "nro"], (state, current) => {
+    addStringifier('doc', ['initializers', 'readonly', 'nro'], (state, current) => {
         return `${current}/${state.docId}`;
     });
 
-
     export function parseUrl(location: Location | URL): ParsedUrl | undefined {
         const pathname = location.pathname.substring(1);
         const search = location.search;
         const opts = search.length ? qs.parse(search, { sort: false }) : {};
-        const pathnameSplit = pathname.split("/");
+        const pathnameSplit = pathname.split('/');
 
         const type = pathnameSplit[0];
 
@@ -179,7 +178,7 @@ export namespace HistoryUtil {
         if (params.type in stringifiers) {
             return stringifiers[params.type](params);
         }
-        return "";
+        return '';
     }
 
     export async function initDoc(id: string, initializer: DocInitializerList) {
@@ -197,7 +196,7 @@ export namespace HistoryUtil {
             await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
         }
         if (field instanceof Doc) {
-            CurrentUserUtils.openDashboard(field, true);
+            DashboardView.openDashboard(field, true);
         }
     }
 
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
new file mode 100644
index 000000000..f75ac24f5
--- /dev/null
+++ b/src/client/util/LinkFollower.ts
@@ -0,0 +1,112 @@
+import { action, observable, observe } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
+import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { LightboxView } from '../views/LightboxView';
+import { DocumentViewSharedProps, ViewAdjustment } from '../views/nodes/DocumentView';
+import { DocumentManager } from './DocumentManager';
+import { UndoManager } from './UndoManager';
+
+type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
+/*
+ * link doc:
+ * - anchor1: doc
+ * - anchor2: doc
+ *
+ * group doc:
+ * - type: string representing the group type/name/category
+ * - metadata: doc representing the metadata kvps
+ *
+ * metadata doc:
+ * - user defined kvps
+ */
+export class LinkFollower {
+    // follows a link - if the target is on screen, it highlights/pans to it.
+    // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
+    // depending on the followLinkLocation property of the source (or the link itself as a fallback);
+    public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean, zoom: boolean = false) => {
+        const batch = UndoManager.StartBatch('follow link click');
+        // open up target if it's not already in view ...
+        const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
+            const createTabForTarget = (didFocus: boolean) =>
+                new Promise<ViewAdjustment>(res => {
+                    const where = LightboxView.LightboxDoc ? 'lightbox' : StrCast(sourceDoc.followLinkLocation, followLoc);
+                    docViewProps.addDocTab(doc, where);
+                    setTimeout(() => {
+                        const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
+                        const targDocView = getFirstDocView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
+                        if (targDocView) {
+                            targDocView.props.focus(doc, {
+                                willZoom: BoolCast(sourceDoc.followLinkZoom, false),
+                                afterFocus: (didFocus: boolean) => {
+                                    finished?.();
+                                    res(ViewAdjustment.resetView);
+                                    return new Promise<ViewAdjustment>(res2 => res2(ViewAdjustment.doNothing));
+                                },
+                            });
+                        } else {
+                            res(where !== 'inPlace' ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace'  resetting the initial focus&zoom would negate the zoom into the target
+                        }
+                    });
+                });
+
+            if (!sourceDoc.followLinkZoom) {
+                createTabForTarget(false);
+            } else {
+                // first focus & zoom onto this (the clicked document).  Then execute the function to focus on the target
+                docViewProps.focus(sourceDoc, { willZoom: BoolCast(sourceDoc.followLinkZoom, true), scale: 1, afterFocus: createTabForTarget });
+            }
+        };
+        LinkFollower.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, zoom), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
+    };
+
+    public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
+        const linkDocs = link ? [link] : DocListCast(sourceDoc.links);
+        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
+        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
+        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
+        const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
+        const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs;
+        const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1);
+        var count = 0;
+        const allFinished = () => ++count === followLinks.length && finished?.();
+        followLinks.forEach(async linkDoc => {
+            if (linkDoc) {
+                const target = (
+                    sourceDoc === linkDoc.anchor1
+                        ? linkDoc.anchor2
+                        : sourceDoc === linkDoc.anchor2
+                        ? linkDoc.anchor1
+                        : Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)
+                        ? linkDoc.anchor2
+                        : linkDoc.anchor1
+                ) as Doc;
+                if (target) {
+                    if (target.TourMap) {
+                        const fieldKey = Doc.LayoutFieldKey(target);
+                        const tour = DocListCast(target[fieldKey]).reverse();
+                        LightboxView.SetLightboxDoc(currentContext, undefined, tour);
+                        setTimeout(LightboxView.Next);
+                        allFinished();
+                    } else {
+                        const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
+                        const containerDoc = containerAnnoDoc || target;
+                        var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : ([] as Doc[]);
+                        while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
+                            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+                        }
+                        const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
+                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, 'lightbox'), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
+                    }
+                } else {
+                    allFinished();
+                }
+            } else {
+                allFinished();
+            }
+        });
+    }
+}
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index d51cd350d..7a12a8580 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,35 +1,30 @@
-import { action, observable, observe } from "mobx";
-import { computedFn } from "mobx-utils";
-import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { ProxyField } from "../../fields/Proxy";
-import { BoolCast, Cast, StrCast } from "../../fields/Types";
-import { LightboxView } from "../views/LightboxView";
-import { DocumentViewSharedProps, ViewAdjustment } from "../views/nodes/DocumentView";
-import { DocumentManager } from "./DocumentManager";
-import { UndoManager } from "./UndoManager";
-
-type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
-/* 
- * link doc: 
- * - anchor1: doc 
+import { action, observable, observe } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
+import { Cast, StrCast } from '../../fields/Types';
+/*
+ * link doc:
+ * - anchor1: doc
  * - anchor2: doc
- * 
+ *
  * group doc:
  * - type: string representing the group type/name/category
  * - metadata: doc representing the metadata kvps
- * 
+ *
  * metadata doc:
- * - user defined kvps 
+ * - user defined kvps
  */
 export class LinkManager {
-
     @observable static _instance: LinkManager;
     @observable static userLinkDBs: Doc[] = [];
     public static currentLink: Opt<Doc>;
-    public static get Instance() { return LinkManager._instance; }
+    public static get Instance() {
+        return LinkManager._instance;
+    }
     public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb);
-    public static AutoKeywords = "keywords:Usages";
+    public static AutoKeywords = 'keywords:Usages';
     static links: Doc[] = [];
     constructor() {
         LinkManager._instance = this;
@@ -38,50 +33,61 @@ export class LinkManager {
         const addLinkToDoc = (link: Doc) => {
             const a1Prom = link?.anchor1;
             const a2Prom = link?.anchor2;
-            Promise.all([a1Prom, a2Prom]).then((all) => {
+            Promise.all([a1Prom, a2Prom]).then(all => {
                 const a1 = all[0];
                 const a2 = all[1];
                 const a1ProtoProm = (link?.anchor1 as Doc)?.proto;
                 const a2ProtoProm = (link?.anchor2 as Doc)?.proto;
-                Promise.all([a1ProtoProm, a2ProtoProm]).then(action((all) => {
-                    if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                        Doc.GetProto(a1)[DirectLinksSym].add(link);
-                        Doc.GetProto(a2)[DirectLinksSym].add(link);
-                        Doc.GetProto(link)[DirectLinksSym].add(link);
-                    }
-                }));
+                Promise.all([a1ProtoProm, a2ProtoProm]).then(
+                    action(all => {
+                        if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
+                            Doc.GetProto(a1)[DirectLinksSym].add(link);
+                            Doc.GetProto(a2)[DirectLinksSym].add(link);
+                            Doc.GetProto(link)[DirectLinksSym].add(link);
+                        }
+                    })
+                );
             });
         };
         const remLinkFromDoc = (link: Doc) => {
             const a1 = link?.anchor1;
             const a2 = link?.anchor2;
-            Promise.all([a1, a2]).then(action(() => {
-                if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                    Doc.GetProto(a1)[DirectLinksSym].delete(link);
-                    Doc.GetProto(a2)[DirectLinksSym].delete(link);
-                    Doc.GetProto(link)[DirectLinksSym].delete(link);
-                }
-            }));
+            Promise.all([a1, a2]).then(
+                action(() => {
+                    if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
+                        Doc.GetProto(a1)[DirectLinksSym].delete(link);
+                        Doc.GetProto(a2)[DirectLinksSym].delete(link);
+                        Doc.GetProto(link)[DirectLinksSym].delete(link);
+                    }
+                })
+            );
         };
         const watchUserLinkDB = (userLinkDBDoc: Doc) => {
             LinkManager.links.push(...DocListCast(userLinkDBDoc.data));
-            const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field;  // see List.ts.  data structure is not a simple list of Docs, but a list of ProxyField/Fields
-            observe(userLinkDBDoc.data as Doc, change => {  // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
-                switch (change.type as any) {
-                    case "splice":
-                        (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
-                        (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
-                        break;
-                    case "update": //let oldValue = change.oldValue;
-                }
-            }, true);
-            observe(userLinkDBDoc, "data",  // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+            const toRealField = (field: Field) => (field instanceof ProxyField ? field.value() : field); // see List.ts.  data structure is not a simple list of Docs, but a list of ProxyField/Fields
+            observe(
+                userLinkDBDoc.data as Doc,
                 change => {
+                    // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
                     switch (change.type as any) {
-                        case "update":
-                            Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => {
-                                const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length);
-                                const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length);
+                        case 'splice':
+                            (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
+                            (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+                            break;
+                        case 'update': //let oldValue = change.oldValue;
+                    }
+                },
+                true
+            );
+            observe(
+                userLinkDBDoc,
+                'data', // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+                change => {
+                    switch (change.type as any) {
+                        case 'update':
+                            Promise.all([...((change.oldValue as any as Doc[]) || []), ...((change.newValue as any as Doc[]) || [])]).then(doclist => {
+                                const oldDocs = doclist.slice(0, ((change.oldValue as any as Doc[]) || []).length);
+                                const newDocs = doclist.slice(((change.oldValue as any as Doc[]) || []).length, doclist.length);
 
                                 const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
                                 const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
@@ -89,38 +95,54 @@ export class LinkManager {
                                 removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
                             });
                     }
-                }, true);
+                },
+                true
+            );
         };
-        observe(LinkManager.userLinkDBs, change => {
-            switch (change.type as any) {
-                case "splice": (change as any).added.forEach(watchUserLinkDB); break;
-                case "update": //let oldValue = change.oldValue;
-            }
-        }, true);
+        observe(
+            LinkManager.userLinkDBs,
+            change => {
+                switch (change.type as any) {
+                    case 'splice':
+                        (change as any).added.forEach(watchUserLinkDB);
+                        break;
+                    case 'update': //let oldValue = change.oldValue;
+                }
+            },
+            true
+        );
         LinkManager.addLinkDB(Doc.LinkDBDoc());
-        DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist => dblist?.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);
-        }));
+        DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist =>
+            dblist?.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);
+            })
+        );
     }
 
-
     public createLinkrelationshipLists = () => {
         //create new lists for link relations and their associated colors if the lists don't already exist
         !Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List<string>());
         !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List<string>());
         !Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List<number>());
-    }
+    };
 
     public addLink(linkDoc: Doc, checkExists = false) {
         if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
-            Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc);
+            Doc.AddDocToList(Doc.LinkDBDoc(), 'data', linkDoc);
         }
     }
-    public deleteLink(linkDoc: Doc) { return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); }
-    public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); }
+    public deleteLink(linkDoc: Doc) {
+        return Doc.RemoveDocFromList(Doc.LinkDBDoc(), 'data', linkDoc);
+    }
+    public deleteAllLinksOnAnchor(anchor: Doc) {
+        LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc));
+    }
 
-    public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor
+    public getAllRelatedLinks(anchor: Doc) {
+        return this.relatedLinker(anchor);
+    } // finds all links that contain the given anchor
     public getAllDirectLinks(anchor: Doc): Doc[] {
         // FIXME:glr Why is Doc undefined?
         if (Doc.GetProto(anchor)[DirectLinksSym]) {
@@ -133,18 +155,16 @@ export class LinkManager {
     relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
         const lfield = Doc.LayoutFieldKey(anchor);
         if (!anchor || anchor instanceof Promise || Doc.GetProto(anchor) instanceof Promise) {
-            console.log("WAITING FOR DOC/PROTO IN LINKMANAGER");
+            console.log('WAITING FOR DOC/PROTO IN LINKMANAGER');
             return [];
         }
         const dirLinks = Doc.GetProto(anchor)[DirectLinksSym];
-        const annos = DocListCast(anchor[lfield + "-annotations"]);
-        const timelineAnnos = DocListCast(anchor[lfield + "-annotations-timeline"]);
+        const annos = DocListCast(anchor[lfield + '-annotations']);
+        const timelineAnnos = DocListCast(anchor[lfield + '-annotations-timeline']);
         if (!annos || !timelineAnnos) {
             debugger;
         }
-        const related = [...annos, ...timelineAnnos].reduce((list, anno) =>
-            [...list, ...LinkManager.Instance.relatedLinker(anno)],
-            Array.from(dirLinks).slice());
+        const related = [...annos, ...timelineAnnos].reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
         return related;
     }, true);
 
@@ -152,15 +172,15 @@ export class LinkManager {
     public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
         const anchorGroups = new Map<string, Array<Doc>>();
         this.relatedLinker(anchor).forEach(link => {
-            if (link.linkRelationship && link.linkRelationship !== "-ungrouped-") {
+            if (link.linkRelationship && link.linkRelationship !== '-ungrouped-') {
                 const relation = StrCast(link.linkRelationship);
-                const anchorRelation = relation.indexOf(":") !== -1 ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
+                const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
                 const group = anchorGroups.get(anchorRelation);
                 anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);
             } else {
                 // if link is in no groups then put it in default group
-                const group = anchorGroups.get("*");
-                anchorGroups.set("*", group ? [...group, link] : [link]);
+                const group = anchorGroups.get('*');
+                anchorGroups.set('*', group ? [...group, link] : [link]);
             }
         });
         return anchorGroups;
@@ -178,85 +198,4 @@ export class LinkManager {
         if (Doc.AreProtosEqual(anchor, a2.annotationOn as Doc)) return a1;
         if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
     }
-
-
-    // follows a link - if the target is on screen, it highlights/pans to it.
-    // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
-    // depending on the followLinkLocation property of the source (or the link itself as a fallback);
-    public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean, zoom: boolean = false) => {
-        const batch = UndoManager.StartBatch("follow link click");
-        // open up target if it's not already in view ...
-        const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
-            const createTabForTarget = (didFocus: boolean) => new Promise<ViewAdjustment>(res => {
-                const where = LightboxView.LightboxDoc ? "lightbox" : StrCast(sourceDoc.followLinkLocation, followLoc);
-                docViewProps.addDocTab(doc, where);
-                setTimeout(() => {
-                    const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
-                    const targDocView = getFirstDocView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
-                    if (targDocView) {
-                        targDocView.props.focus(doc, {
-                            willZoom: BoolCast(sourceDoc.followLinkZoom, false),
-                            afterFocus: (didFocus: boolean) => {
-                                finished?.();
-                                res(ViewAdjustment.resetView);
-                                return new Promise<ViewAdjustment>(res2 => res2(ViewAdjustment.doNothing));
-                            }
-                        });
-                    } else {
-                        res(where !== "inPlace" ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace'  resetting the initial focus&zoom would negate the zoom into the target 
-                    }
-                });
-            });
-
-            if (!sourceDoc.followLinkZoom) {
-                createTabForTarget(false);
-            } else {
-                // first focus & zoom onto this (the clicked document).  Then execute the function to focus on the target
-                docViewProps.focus(sourceDoc, { willZoom: BoolCast(sourceDoc.followLinkZoom, true), scale: 1, afterFocus: createTabForTarget });
-            }
-        };
-        LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, zoom), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
-    }
-
-    public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
-        const linkDocs = link ? [link] : DocListCast(sourceDoc.links);
-        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
-        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
-        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
-        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
-        const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
-        const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
-        const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1);
-        var count = 0;
-        const allFinished = () => ++count === followLinks.length && finished?.();
-        followLinks.forEach(async linkDoc => {
-            if (linkDoc) {
-                const target = (sourceDoc === linkDoc.anchor1 ? linkDoc.anchor2 : sourceDoc === linkDoc.anchor2 ? linkDoc.anchor1 :
-                    (Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
-                if (target) {
-                    if (target.TourMap) {
-                        const fieldKey = Doc.LayoutFieldKey(target);
-                        const tour = DocListCast(target[fieldKey]).reverse();
-                        LightboxView.SetLightboxDoc(currentContext, undefined, tour);
-                        setTimeout(LightboxView.Next);
-                        allFinished();
-                    } else {
-                        const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
-                        const containerDoc = containerAnnoDoc || target;
-                        var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : [] as Doc[];
-                        while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
-                            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
-                        }
-                        const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
-                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
-                    }
-                } else {
-                    allFinished();
-                }
-            } else {
-                allFinished();
-            }
-        });
-    }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index b71086561..d67ce6f6a 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,15 +1,11 @@
-import { action, observable, ObservableMap } from "mobx";
-import { computedFn } from "mobx-utils";
-import { Doc, Opt } from "../../fields/Doc";
-import { DocumentType } from "../documents/DocumentTypes";
-import { CollectionViewType } from "../views/collections/CollectionView";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { CurrentUserUtils } from "./CurrentUserUtils";
+import { action, observable, ObservableMap } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { Doc, Opt } from '../../fields/Doc';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { DocumentView } from '../views/nodes/DocumentView';
 
 export namespace SelectionManager {
-
     class Manager {
-
         @observable IsDragging: boolean = false;
         SelectedViews: ObservableMap<DocumentView, Doc> = new ObservableMap();
         @observable SelectedSchemaDocument: Doc | undefined;
@@ -63,16 +59,21 @@ export namespace SelectionManager {
         manager.SelectSchemaViewDoc(document);
     }
 
-    const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) {  // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
+    const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) {
+        // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
         return manager.SelectedViews.get(doc) ? true : false;
     });
     // computed functions, such as used in IsSelected generate errors if they're called outside of a
     // reaction context.  Specifying the context with 'outsideReaction' allows an efficiency feature
     // to avoid unnecessary mobx invalidations when running inside a reaction.
     export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean {
-        return !doc ? false : outsideReaction ?
-            manager.SelectedViews.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
-            IsSelectedCache(doc);
+        return !doc
+            ? false
+            : outsideReaction
+            ? manager.SelectedViews.get(doc)
+                ? true
+                : false // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
+            : IsSelectedCache(doc);
     }
 
     export function DeselectAll(except?: Doc): void {
@@ -96,4 +97,4 @@ export namespace SelectionManager {
     export function Docs(): Doc[] {
         return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 080237649..12d1793af 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,29 +1,28 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { ColorState, SketchPicker } from "react-color";
-import { Doc } from "../../fields/Doc";
-import { BoolCast, StrCast, Cast } from "../../fields/Types";
-import { addStyleSheet, addStyleSheetRule, Utils } from "../../Utils";
-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";
-const higflyout = require("@hig/flyout");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { ColorState, SketchPicker } from 'react-color';
+import { Doc } from '../../fields/Doc';
+import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
+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 { DragManager } from './DragManager';
+import { GroupManager } from './GroupManager';
+import './SettingsManager.scss';
+import { undoBatch } from './UndoManager';
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
 export enum ColorScheme {
-    Dark = "-Dark",
-    Light = "-Light",
-    System = "-MatchSystem"
+    Dark = '-Dark',
+    Light = '-Light',
+    System = '-MatchSystem',
 }
 
 @observer
@@ -31,67 +30,77 @@ export class SettingsManager extends React.Component<{}> {
     public static Instance: SettingsManager;
     static _settingsStyle = addStyleSheet();
     @observable private isOpen = false;
-    @observable private passwordResultText = "";
+    @observable private passwordResultText = '';
     @observable private playgroundMode = false;
 
-    @observable private curr_password = "";
-    @observable private new_password = "";
-    @observable private new_confirm = "";
-    @observable activeTab = "Accounts";
+    @observable private curr_password = '';
+    @observable private new_password = '';
+    @observable private new_confirm = '';
+    @observable activeTab = 'Accounts';
 
-    @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
-    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme; }
+    @observable public static propertiesWidth: number = 0;
+    @observable public static headerBarHeight: number = 0;
+
+    @computed get backgroundColor() {
+        return Doc.UserDoc().activeCollectionBackground;
+    }
+    @computed get colorScheme() {
+        return Doc.ActiveDashboard?.colorScheme;
+    }
 
     constructor(props: {}) {
         super(props);
         SettingsManager.Instance = this;
     }
 
-    public close = action(() => this.isOpen = false);
-    public open = action(() => this.isOpen = true);
+    public close = action(() => (this.isOpen = false));
+    public open = action(() => (this.isOpen = true));
 
     private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true));
     private changePassword = async () => {
         if (!(this.curr_password && this.new_password && this.new_confirm)) {
-            runInAction(() => this.passwordResultText = "Error: Hey, we're missing some fields!");
+            runInAction(() => (this.passwordResultText = "Error: Hey, we're missing some fields!"));
         } else {
             const passwordBundle = { curr_pass: this.curr_password, new_pass: this.new_password, new_confirm: this.new_confirm };
             const { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle);
-            runInAction(() => this.passwordResultText = error ? "Error: " + error[0].msg + "..." : "Password successfully updated!");
+            runInAction(() => (this.passwordResultText = error ? 'Error: ' + error[0].msg + '...' : 'Password successfully updated!'));
         }
-    }
-
-    @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);
-    @undoBatch switchActiveBackgroundColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex));
-    @undoBatch switchUserColor = action((color: ColorState) => { Doc.SharingDoc().userColor = undefined; Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex); });
+    };
+
+    @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));
+    @undoBatch switchActiveBackgroundColor = action((color: ColorState) => (Doc.UserDoc().activeCollectionBackground = String(color.hex)));
+    @undoBatch switchUserColor = action((color: ColorState) => {
+        Doc.SharingDoc().userColor = undefined;
+        Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex);
+    });
     @undoBatch playgroundModeToggle = action(() => {
         this.playgroundMode = !this.playgroundMode;
         if (this.playgroundMode) {
             DocServer.Control.makeReadOnly();
-            addStyleSheetRule(SettingsManager._settingsStyle, "topbar-inner-container", { background: "red !important" });
-        }
-        else DocServer.Control.makeEditable();
+            addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' });
+        } else DocServer.Control.makeEditable();
     });
 
     @undoBatch
     @action
     changeColorScheme = action((e: React.ChangeEvent) => {
-        const activeDashboard= CurrentUserUtils.ActiveDashboard;
+        const activeDashboard = Doc.ActiveDashboard;
         if (!activeDashboard) return;
         const scheme: ColorScheme = (e.currentTarget as any).value;
         switch (scheme) {
             case ColorScheme.Light:
                 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" });
+                addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: '#d3d3d3 !important' });
                 break;
             case ColorScheme.Dark:
                 activeDashboard.colorScheme = ColorScheme.Dark;
-                addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" });
+                addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: 'black !important' });
                 break;
-            case ColorScheme.System: default:
+            case ColorScheme.System:
+            default:
                 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
                     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)
                 });
@@ -99,223 +108,285 @@ export class SettingsManager extends React.Component<{}> {
         }
     });
 
-
     @computed get colorsContent() {
-        const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(this.backgroundColor)}
-            presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-                '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-                '#FFFFFF', '#f1efeb', 'transparent']} />;
-
-        const colorFlyout = <div className="colorFlyout">
-            <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchActiveBackgroundColor)}>
-                <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()} >
-                    <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
-                </div>
-            </Flyout>
-        </div>;
-        const userColorFlyout = <div className="colorFlyout">
-            <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchUserColor)}>
-                <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()} >
-                    <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
-                </div>
-            </Flyout>
-        </div>;
+        const colorBox = (func: (color: ColorState) => void) => (
+            <SketchPicker
+                onChange={func}
+                color={StrCast(this.backgroundColor)}
+                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+            />
+        );
+
+        const colorFlyout = (
+            <div className="colorFlyout">
+                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchActiveBackgroundColor)}>
+                    <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
+                    </div>
+                </Flyout>
+            </div>
+        );
+        const userColorFlyout = (
+            <div className="colorFlyout">
+                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchUserColor)}>
+                    <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
+                    </div>
+                </Flyout>
+            </div>
+        );
 
         const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System];
-        const schemeMap = ["Light", "Dark", "Match system"];
+        const schemeMap = ['Light', 'Dark', 'Match system'];
 
-        return <div className="colors-content">
-            <div className="preferences-color">
-                <div className="preferences-color-text">Background Color</div>
-                {colorFlyout}
-            </div>
-            <div className="preferences-color">
-                <div className="preferences-color-text">Border/Header Color</div>
-                {userColorFlyout}
-            </div>
-            <div className="preferences-colorScheme">
-                <div className="preferences-color-text">Color Scheme</div>
-                <div className="preferences-color-controls">
-                    <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme)}>
-                        {colorSchemes.map((scheme, i) => <option key={scheme} value={scheme}> {schemeMap[i]} </option>)}
-                    </select>
+        return (
+            <div className="colors-content">
+                <div className="preferences-color">
+                    <div className="preferences-color-text">Background Color</div>
+                    {colorFlyout}
+                </div>
+                <div className="preferences-color">
+                    <div className="preferences-color-text">Border/Header Color</div>
+                    {userColorFlyout}
+                </div>
+                <div className="preferences-colorScheme">
+                    <div className="preferences-color-text">Color Scheme</div>
+                    <div className="preferences-color-controls">
+                        <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(Doc.ActiveDashboard?.colorScheme)}>
+                            {colorSchemes.map((scheme, i) => (
+                                <option key={scheme} value={scheme}>
+                                    {' '}
+                                    {schemeMap[i]}{' '}
+                                </option>
+                            ))}
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get formatsContent() {
-        return <div className="prefs-content">
-            <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : "creationDate"} checked={Doc.UserDoc().showTitle !== undefined} />
-                <div className="preferences-check">Show doc header</div>
-            </div>
-            <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-fullMenu"] = !Doc.UserDoc()["documentLinksButton-fullMenu"]}
-                    checked={BoolCast(Doc.UserDoc()["documentLinksButton-fullMenu"])} />
-                <div className="preferences-check">Show full toolbar</div>
-            </div>
-            <div>
-                <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 => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())}
-                    checked={FontIconBox.GetShowLabels()} />
-                <div className="preferences-check">Show button labels</div>
+        return (
+            <div className="prefs-content">
+                <div>
+                    <input type="checkbox" onChange={e => (Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().showTitle !== undefined} />
+                    <div className="preferences-check">Show doc header</div>
+                </div>
+                <div>
+                    <input type="checkbox" onChange={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} checked={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} />
+                    <div className="preferences-check">Show full toolbar</div>
+                </div>
+                <div>
+                    <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 => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} checked={FontIconBox.GetShowLabels()} />
+                    <div className="preferences-check">Show button labels</div>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get appearanceContent() {
-
-        return <div className="tab-content appearances-content">
-            <div className="tab-column">
-                <div className="tab-column-title">Colors</div>
-                <div className="tab-column-content">{this.colorsContent}</div>
-            </div>
-            <div className="tab-column">
-                <div className="tab-column-title">Formats</div>
-                <div className="tab-column-content">{this.formatsContent}</div>
+        return (
+            <div className="tab-content appearances-content">
+                <div className="tab-column">
+                    <div className="tab-column-title">Colors</div>
+                    <div className="tab-column-content">{this.colorsContent}</div>
+                </div>
+                <div className="tab-column">
+                    <div className="tab-column-title">Formats</div>
+                    <div className="tab-column-content">{this.formatsContent}</div>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get textContent() {
-
-        const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text", "Roboto"];
-        const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"];
+        const fontFamilies = ['Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text', 'Roboto'];
+        const fontSizes = ['7px', '8px', '9px', '10px', '12px', '14px', '16px', '18px', '20px', '24px', '32px', '48px', '72px'];
 
         return (
             <div className="tab-content appearances-content">
                 <div className="preferences-font">
                     <div className="preferences-font-text">Default Font</div>
                     <div className="preferences-font-controls">
-                        <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}>
-                            {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
+                        <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, '7px')}>
+                            {fontSizes.map(size => (
+                                <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}>
+                                    {' '}
+                                    {size}{' '}
+                                </option>
+                            ))}
                         </select>
-                        <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} >
-                            {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)}
+                        <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, 'Times New Roman')}>
+                            {fontFamilies.map(font => (
+                                <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}>
+                                    {' '}
+                                    {font}{' '}
+                                </option>
+                            ))}
                         </select>
                     </div>
                 </div>
-            </div>);
+            </div>
+        );
     }
 
     @action
     changeVal = (e: React.ChangeEvent, pass: string) => {
         const value = (e.target as any).value;
         switch (pass) {
-            case "curr": this.curr_password = value; break;
-            case "new": this.new_password = value; break;
-            case "conf": this.new_confirm = value; break;
+            case 'curr':
+                this.curr_password = value;
+                break;
+            case 'new':
+                this.new_password = value;
+                break;
+            case 'conf':
+                this.new_confirm = value;
+                break;
         }
-    }
+    };
 
     @computed get passwordContent() {
-        return <div className="password-content">
-            <div className="password-content-inputs">
-                <input className="password-inputs" type="password" placeholder="current password" onChange={e => this.changeVal(e, "curr")} />
-                <input className="password-inputs" type="password" placeholder="new password" onChange={e => this.changeVal(e, "new")} />
-                <input className="password-inputs" type="password" placeholder="confirm new password" onChange={e => this.changeVal(e, "conf")} />
-            </div>
-            <div className="password-content-buttons">
-                {!this.passwordResultText ? (null) : <div className={`${this.passwordResultText.startsWith("Error") ? "error" : "success"}-text`}>{this.passwordResultText}</div>}
-                <a className="password-forgot" href="/forgotPassword">forgot password?</a>
-                <button className="password-submit" onClick={this.changePassword}>submit</button>
+        return (
+            <div className="password-content">
+                <div className="password-content-inputs">
+                    <input className="password-inputs" type="password" placeholder="current password" onChange={e => this.changeVal(e, 'curr')} />
+                    <input className="password-inputs" type="password" placeholder="new password" onChange={e => this.changeVal(e, 'new')} />
+                    <input className="password-inputs" type="password" placeholder="confirm new password" onChange={e => this.changeVal(e, 'conf')} />
+                </div>
+                <div className="password-content-buttons">
+                    {!this.passwordResultText ? null : <div className={`${this.passwordResultText.startsWith('Error') ? 'error' : 'success'}-text`}>{this.passwordResultText}</div>}
+                    <a className="password-forgot" href="/forgotPassword">
+                        forgot password?
+                    </a>
+                    <button className="password-submit" onClick={this.changePassword}>
+                        submit
+                    </button>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get accountOthersContent() {
-        return <div className="account-others-content">
-            <button onClick={this.googleAuthorize} value="data">Authorize Google Acc</button>
-        </div>;
+        return (
+            <div className="account-others-content">
+                <button onClick={this.googleAuthorize} value="data">
+                    Authorize Google Acc
+                </button>
+            </div>
+        );
     }
 
     @computed get accountsContent() {
-        return <div className="tab-content accounts-content">
-            <div className="tab-column">
-                <div className="tab-column-title">Password</div>
-                <div className="tab-column-content">{this.passwordContent}</div>
-            </div>
-            <div className="tab-column">
-                <div className="tab-column-title">Others</div>
-                <div className="tab-column-content">{this.accountOthersContent}</div>
+        return (
+            <div className="tab-content accounts-content">
+                <div className="tab-column">
+                    <div className="tab-column-title">Password</div>
+                    <div className="tab-column-content">{this.passwordContent}</div>
+                </div>
+                <div className="tab-column">
+                    <div className="tab-column-title">Others</div>
+                    <div className="tab-column-content">{this.accountOthersContent}</div>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get modesContent() {
-        return <div className="tab-content modes-content">
-            <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.noviceMode ? "Novice" : "Developer"}>
-                        <option key={"Novice"} value={"Novice"}> Novice </option>
-                        <option key={"Developer"} value={"Developer"}> Developer</option>
-                    </select>
-                    <div className="modes-playground">
-                        <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
-                        <div className="playground-text">Playground Mode</div>
+        return (
+            <div className="tab-content modes-content">
+                <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.noviceMode ? 'Novice' : 'Developer'}>
+                            <option key={'Novice'} value={'Novice'}>
+                                {' '}
+                                Novice{' '}
+                            </option>
+                            <option key={'Developer'} value={'Developer'}>
+                                {' '}
+                                Developer
+                            </option>
+                        </select>
+                        <div className="modes-playground">
+                            <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
+                            <div className="playground-text">Playground Mode</div>
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div className="tab-column">
-                <div className="tab-column-title">Permissions</div>
-                <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.defaultAclPrivate)}
-                            onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
-                        <div className="acl-text">Default access private</div>
+                <div className="tab-column">
+                    <div className="tab-column-title">Permissions</div>
+                    <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.defaultAclPrivate)} onChange={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))} />
+                            <div className="acl-text">Default access private</div>
+                        </div>
                     </div>
                 </div>
             </div>
-
-        </div>;
+        );
     }
 
-
     private get settingsInterface() {
         // const pairs = [{ title: "Password", ele: this.passwordContent }, { title: "Modes", ele: this.modesContent },
         // { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }];
 
-        const tabs = [{ title: "Accounts", ele: this.accountsContent }, { title: "Modes", ele: this.modesContent },
-        { title: "Appearance", ele: this.appearanceContent }, { title: "Text", ele: this.textContent }];
+        const tabs = [
+            { title: 'Accounts', ele: this.accountsContent },
+            { title: 'Modes', ele: this.modesContent },
+            { title: 'Appearance', ele: this.appearanceContent },
+            { title: 'Text', ele: this.textContent },
+        ];
 
-        return <div className="settings-interface">
-            <div className="settings-panel">
-                <div className="settings-tabs">
-                    {tabs.map(tab => <div key={tab.title} className={"tab-control " + (this.activeTab === tab.title ? "active" : "inactive")} onClick={action(() => this.activeTab = tab.title)}>{tab.title}</div>)}
-                </div>
+        return (
+            <div className="settings-interface">
+                <div className="settings-panel">
+                    <div className="settings-tabs">
+                        {tabs.map(tab => (
+                            <div key={tab.title} className={'tab-control ' + (this.activeTab === tab.title ? 'active' : 'inactive')} onClick={action(() => (this.activeTab = tab.title))}>
+                                {tab.title}
+                            </div>
+                        ))}
+                    </div>
 
-                <div className="settings-user">
-                    <div className="settings-username">{Doc.CurrentUserEmail}</div>
-                    <button className="logout-button" onClick={() => window.location.assign(Utils.prepend("/logout"))} >
-                        {CurrentUserUtils.GuestDashboard ? "Exit" : "Log Out"}
-                    </button>
+                    <div className="settings-user">
+                        <div className="settings-username">{Doc.CurrentUserEmail}</div>
+                        <button className="logout-button" onClick={() => window.location.assign(Utils.prepend('/logout'))}>
+                            {Doc.GuestDashboard ? 'Exit' : 'Log Out'}
+                        </button>
+                    </div>
                 </div>
-            </div>
 
-            <div className="close-button" onClick={this.close}>
-                <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
-            </div>
+                <div className="close-button" onClick={this.close}>
+                    <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
+                </div>
 
-            <div className="settings-content">
-                {tabs.map(tab => <div key={tab.title} className={"tab-section " + (this.activeTab === tab.title ? "active" : "inactive")}>{tab.ele}</div>)}
+                <div className="settings-content">
+                    {tabs.map(tab => (
+                        <div key={tab.title} className={'tab-section ' + (this.activeTab === tab.title ? 'active' : 'inactive')}>
+                            {tab.ele}
+                        </div>
+                    ))}
+                </div>
             </div>
-        </div>;
-
+        );
     }
 
     render() {
-        return <MainViewModal
-            contents={this.settingsInterface}
-            isDisplayed={this.isOpen}
-            interactive={true}
-            closeOnExternalClick={this.close}
-            dialogueBoxStyle={{ width: "500px", height: "300px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
+        return (
+            <MainViewModal
+                contents={this.settingsInterface}
+                isDisplayed={this.isOpen}
+                interactive={true}
+                closeOnExternalClick={this.close}
+                dialogueBoxStyle={{ width: '500px', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
+            />
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 1acb3ab38..793027ea1 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -17,7 +17,6 @@ import { MainViewModal } from '../views/MainViewModal';
 import { DocumentView } from '../views/nodes/DocumentView';
 import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
 import { SearchBox } from '../views/search/SearchBox';
-import { CurrentUserUtils } from './CurrentUserUtils';
 import { DocumentManager } from './DocumentManager';
 import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
@@ -180,7 +179,7 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const acl = `acl-${normalizeEmail(user.email)}`;
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
-        const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
+        const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
         return !docs
@@ -211,7 +210,7 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const key = normalizeEmail(StrCast(group.title));
         const acl = `acl-${key}`;
-        const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
+        const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
@@ -279,7 +278,7 @@ export class SharingManager extends React.Component<{}> {
                 else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
             });
         } else {
-            const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+            const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
                 if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
@@ -291,7 +290,7 @@ export class SharingManager extends React.Component<{}> {
      * Sets the background of the Dashboard if it has been shared as a visual indicator
      */
     setDashboardBackground = async (doc: Doc, permission: SharingPermissions) => {
-        if (Doc.IndexOf(doc, DocListCast(CurrentUserUtils.MyDashboards.data)) !== -1) {
+        if (Doc.IndexOf(doc, DocListCast(Doc.MyDashboards.data)) !== -1) {
             if (permission !== SharingPermissions.None) {
                 doc.isShared = true;
                 doc.backgroundColor = 'green';
@@ -329,7 +328,7 @@ export class SharingManager extends React.Component<{}> {
      */
     removeGroup = (group: Doc) => {
         if (group.docsShared) {
-            const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+            const dashboards = DocListCast(Doc.MyDashboards.data);
             DocListCast(group.docsShared).forEach(doc => {
                 const acl = `acl-${StrCast(group.title)}`;
                 const isDashboard = dashboards.indexOf(doc) !== -1;
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 868d63a90..c59c37488 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,164 +1,290 @@
-import { action, computed, observable } from "mobx";
-import { extname } from 'path';
-import { observer } from "mobx-react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { Cast, ImageCast, StrCast } from "../../fields/Types";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-import "./DashboardView.scss"
-import { MainViewModal } from "./MainViewModal";
-import { ContextMenu } from "./ContextMenu";
-import { DocumentManager } from "../util/DocumentManager";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { ContextMenuProps } from "./ContextMenuItem";
-import { simulateMouseClick } from "../../Utils";
-import { SharingManager } from "../util/SharingManager";
-import { CollectionViewType } from "./collections/CollectionView";
+import { DataSym, Doc, DocListCast, DocListCastAsync } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
+import { Cast, ImageCast, StrCast } from '../../fields/Types';
+import { DocServer } from '../DocServer';
+import { Docs, DocumentOptions } from '../documents/Documents';
+import { CollectionViewType } from '../documents/DocumentTypes';
+import { HistoryUtil } from '../util/History';
+import { SharingManager } from '../util/SharingManager';
+import { undoBatch } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionView } from './collections/CollectionView';
+import { ContextMenu } from './ContextMenu';
+import './DashboardView.scss';
+import { MainViewModal } from './MainViewModal';
 
 enum DashboardGroup {
-      MyDashboards, SharedDashboards
+    MyDashboards,
+    SharedDashboards,
 }
 
 // DashboardView is the view with the dashboard previews, rendered when the app first loads
 
 @observer
 export class DashboardView extends React.Component {
+    //TODO: delete dashboard, share dashboard, etc.
 
-      //TODO: delete dashboard, share dashboard, etc.
+    public static _urlState: HistoryUtil.DocUrl;
 
-      @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
+    @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
 
-      @observable private newDashboardName: string | undefined = undefined;
-      @action abortCreateNewDashboard = () => { this.newDashboardName = undefined }
-      @action setNewDashboardName(name: string) { this.newDashboardName = name }
+    @observable private newDashboardName: string | undefined = undefined;
+    @action abortCreateNewDashboard = () => {
+        this.newDashboardName = undefined;
+    };
+    @action setNewDashboardName(name: string) {
+        this.newDashboardName = name;
+    }
 
-      @action
-      selectDashboardGroup = (group: DashboardGroup) => {
-            this.selectedDashboardGroup = group
-      }
+    @action
+    selectDashboardGroup = (group: DashboardGroup) => {
+        this.selectedDashboardGroup = group;
+    };
 
-      clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
-            if (e.detail === 2) {
-                  Doc.AddDocToList(CurrentUserUtils.MySharedDocs, "viewed", dashboard)
-                  CurrentUserUtils.ActiveDashboard = dashboard;
-                  CurrentUserUtils.ActivePage = "dashboard";
+    clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
+        if (e.detail === 2) {
+            Doc.AddDocToList(Doc.MySharedDocs, 'viewed', dashboard);
+            Doc.ActiveDashboard = dashboard;
+            Doc.ActivePage = 'dashboard';
+        }
+    };
+
+    getDashboards = () => {
+        const allDashboards = DocListCast(Doc.MyDashboards.data);
+        if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
+            return allDashboards.filter(dashboard => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail);
+        } else {
+            const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+            return sharedDashboards;
+        }
+    };
+
+    isUnviewedSharedDashboard = (dashboard: Doc): boolean => {
+        // const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+        return !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard);
+    };
+
+    getSharedDashboards = () => {
+        const sharedDashs = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+        return sharedDashs.filter(dashboard => !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard));
+    };
+
+    @undoBatch
+    createNewDashboard = async (name: string) => {
+        DashboardView.createNewDashboard(undefined, name);
+        this.abortCreateNewDashboard();
+    };
+
+    @computed
+    get namingInterface() {
+        return (
+            <div>
+                <input className="password-inputs" placeholder="Untitled Dashboard" onChange={e => this.setNewDashboardName((e.target as any).value)} />
+                <button className="password-submit" onClick={this.abortCreateNewDashboard}>
+                    Cancel
+                </button>
+                <button
+                    className="password-submit"
+                    onClick={() => {
+                        this.createNewDashboard(this.newDashboardName!);
+                    }}>
+                    Create
+                </button>
+            </div>
+        );
+    }
+
+    _downX: number = 0;
+    _downY: number = 0;
+    @action
+    onContextMenu = (dashboard: Doc, e?: React.MouseEvent, pageX?: number, pageY?: number) => {
+        // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
+        if (e) {
+            e.preventDefault();
+            e.stopPropagation();
+            e.persist();
+
+            if (!navigator.userAgent.includes('Mozilla') && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
+                return;
             }
-      }
+            const cm = ContextMenu.Instance;
+            cm.addItem({
+                description: 'Share Dashboard',
+                event: async () => {
+                    SharingManager.Instance.open(undefined, dashboard);
+                },
+                icon: 'edit',
+            });
+            cm.addItem({
+                description: 'Delete Dashboard',
+                event: async () => {
+                    DashboardView.removeDashboard(dashboard);
+                },
+                icon: 'trash',
+            });
+            cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
+        }
+    };
+
+    render() {
+        return (
+            <>
+                <div className="dashboard-view">
+                    <div className="left-menu">
+                        <div
+                            className="text-button"
+                            onClick={() => {
+                                this.setNewDashboardName('');
+                            }}>
+                            New
+                        </div>
+                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}>
+                            My Dashboards
+                        </div>
+                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>
+                            Shared Dashboards
+                        </div>
+                    </div>
+                    <div className="all-dashboards">
+                        {this.getDashboards().map(dashboard => {
+                            const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
+                            return (
+                                <div
+                                    className="dashboard-container"
+                                    key={dashboard[Id]}
+                                    onContextMenu={e => {
+                                        this.onContextMenu(dashboard, e);
+                                    }}
+                                    onClick={e => this.clickDashboard(e, dashboard)}>
+                                    <img
+                                        src={
+                                            href ?? 'https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU='
+                                        }></img>
+                                    <div className="info">
+                                        <div className="title"> {StrCast(dashboard.title)} </div>
+                                        {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></div>}
+                                        <div
+                                            className="more"
+                                            onPointerDown={e => {
+                                                this._downX = e.clientX;
+                                                this._downY = e.clientY;
+                                            }}
+                                            onClick={e => {
+                                                this.onContextMenu(dashboard, e);
+                                            }}>
+                                            <FontAwesomeIcon color="black" size="lg" icon="bars" />
+                                        </div>
+                                    </div>
+                                </div>
+                            );
+                        })}
+                    </div>
+                </div>
+                <MainViewModal
+                    contents={this.namingInterface}
+                    isDisplayed={this.newDashboardName !== undefined}
+                    interactive={true}
+                    closeOnExternalClick={this.abortCreateNewDashboard}
+                    dialogueBoxStyle={{ width: '500px', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
+                />
+                ;
+            </>
+        );
+    }
+
+    public static closeActiveDashboard() {
+        Doc.ActiveDashboard = undefined;
+    }
+    public static snapshotDashboard() {
+        return CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
+    }
 
-      getDashboards = () => {
-            const allDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
-            if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
-                  return allDashboards.filter((dashboard) => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail)
+    /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there).
+    /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url)
+    public static openDashboard = (doc: Doc | undefined, fromHistory = false) => {
+        if (!doc) return false;
+        Doc.MainDocId = doc[Id];
+        Doc.AddDocToList(Doc.MyDashboards, 'data', doc);
+
+        // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
+        Doc.UserDoc() ? (Doc.ActiveDashboard = doc) : (Doc.GuestDashboard = doc);
+
+        const state = DashboardView._urlState;
+        if (state.sharing === true && !Doc.UserDoc()) {
+            DocServer.Control.makeReadOnly();
+        } else {
+            fromHistory ||
+                HistoryUtil.pushState({
+                    type: 'doc',
+                    docId: doc[Id],
+                    readonly: state.readonly,
+                    nro: state.nro,
+                    sharing: false,
+                });
+            if (state.readonly === true || state.readonly === null) {
+                DocServer.Control.makeReadOnly();
+            } else if (state.safe) {
+                if (!state.nro) {
+                    DocServer.Control.makeReadOnly();
+                }
+                CollectionView.SetSafeMode(true);
+            } else if (state.nro || state.nro === null || state.readonly === false) {
+            } else if (doc.readOnly) {
+                DocServer.Control.makeReadOnly();
             } else {
-                  const sharedDashboards = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
-                  return sharedDashboards
+                DocServer.Control.makeEditable();
             }
-      }
-
-      isUnviewedSharedDashboard = (dashboard: Doc): boolean => {
-            // const sharedDashboards = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
-            return !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard)
-      }
-
-      getSharedDashboards = () => {
-            const sharedDashs = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
-            return sharedDashs.filter((dashboard) => !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard))
-      }
-
-      @undoBatch
-      createNewDashboard = async (name: string) => { 
-            CurrentUserUtils.createNewDashboard(undefined, name);
-            this.abortCreateNewDashboard();
-      }
-
-      @computed
-      get namingInterface() {
-            return <div>
-                  <input className="password-inputs" placeholder="Untitled Dashboard" onChange={e => this.setNewDashboardName((e.target as any).value)} />
-                  <button className="password-submit" onClick={this.abortCreateNewDashboard}>Cancel</button>
-                  <button className="password-submit" onClick={() => { this.createNewDashboard(this.newDashboardName!) }}>Create</button>
-            </div>;
-      }
-      
-      _downX: number = 0;
-      _downY: number = 0;
-      @action
-      onContextMenu = (dashboard: Doc, e?: React.MouseEvent, pageX?: number, pageY?: number) => {
-          // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
-          if (e) {
-              e.preventDefault();
-              e.stopPropagation();
-              e.persist();
-  
-              if (!navigator.userAgent.includes("Mozilla") && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
-                  return;
-              }
-              const cm = ContextMenu.Instance;
-             cm.addItem({
-                    description: "Share Dashboard", event: async () => {
-                        SharingManager.Instance.open(undefined, dashboard)
-                    }, icon: "edit"
-              });
-              cm.addItem({
-                    description: "Delete Dashboard", event: async () => {
-                          CurrentUserUtils.removeDashboard(dashboard)
-                    }, icon: "trash"
-              });
-              cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
-          }
         }
-  
-
-      render() {
-            return <>
-                  <div className="dashboard-view">
-                        <div className="left-menu">
-                              <div className="text-button" onClick={() => { this.setNewDashboardName("") }}>New</div>
-                              <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}>My Dashboards</div>
-                              <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>Shared Dashboards</div>
-                        </div>
-                        <div className="all-dashboards">
-                              {this.getDashboards().map((dashboard) => {
-                                    const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
-                                    return <div className="dashboard-container" key={dashboard[Id]}
-                                              onContextMenu={(e) => {this.onContextMenu(dashboard, e)}}  
-                                              onClick={e => this.clickDashboard(e, dashboard)}>
-                                          <img src={href ?? "https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="}></img>
-                                          <div className="info">
-                                                <div className="title"> {StrCast(dashboard.title)} </div>
-                                                {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ?
-                                                 <div>unviewed</div> : <div></div>
-                                                }
-                                                <div className="more" onPointerDown={e => {
-                                                        this._downX = e.clientX;
-                                                        this._downY = e.clientY;
-                                                    }} 
-                                                    onClick={(e) => {this.onContextMenu(dashboard, e)}}
-                                                >
-                                                      <FontAwesomeIcon color="black" size="lg" icon="bars" />
-                                                </div>
-                                          </div>
 
-                                    </div>
+        return true;
+    };
 
-                              })}
-                            </div>
+    public static removeDashboard = async (dashboard: Doc) => {
+        const dashboards = await DocListCastAsync(Doc.MyDashboards.data);
+        if (dashboards?.length) {
+            if (dashboard === Doc.ActiveDashboard) DashboardView.openDashboard(dashboards.find(doc => doc !== dashboard));
+            Doc.RemoveDocFromList(Doc.MyDashboards, 'data', dashboard);
+            if (!dashboards.length) Doc.ActivePage = 'home';
+        }
+    };
 
-                  </div>
-                  <MainViewModal
-                        contents={this.namingInterface}
-                        isDisplayed={this.newDashboardName !== undefined}
-                        interactive={true}
-                        closeOnExternalClick={this.abortCreateNewDashboard}
-                        dialogueBoxStyle={{ width: "500px", height: "300px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
-            </>
+    public static createNewDashboard = (id?: string, name?: string) => {
+        const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
+        const dashboards = Doc.MyDashboards;
+        const dashboardCount = DocListCast(dashboards.data).length + 1;
+        const freeformOptions: DocumentOptions = {
+            x: 0,
+            y: 400,
+            _width: 1500,
+            _height: 1000,
+            _fitWidth: true,
+            _backgroundGridShow: true,
+            title: `Untitled Tab 1`,
+        };
+        const title = name ? name : `Dashboard ${dashboardCount}`;
+        const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row');
+        freeformDoc.context = dashboardDoc;
 
-      }
+        // switching the tabs from the datadoc to the regular doc
+        const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
+        dashboardDoc.data = new List<Doc>(dashboardTabs);
+        dashboardDoc['pane-count'] = 1;
+
+        Doc.ActivePresentation = presentation;
+
+        Doc.AddDocToList(dashboards, 'data', dashboardDoc);
+        // open this new dashboard
+        Doc.ActiveDashboard = dashboardDoc;
+        Doc.ActivePage = 'dashboard';
+    };
 }
 
 export function AddToList(MySharedDocs: Doc, arg1: string, dash: any) {
-      throw new Error("Function not implemented.");
+    throw new Error('Function not implemented.');
 }
-
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 169bd3873..280ca8a8c 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -8,13 +8,11 @@ import { Cast, ScriptCast } from '../../fields/Types';
 import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { InteractionUtils } from '../util/InteractionUtils';
 import { UndoManager } from '../util/UndoManager';
 import { DocumentView } from './nodes/DocumentView';
 import { Touchable } from './Touchable';
 
-
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 export interface DocComponentProps {
     Document: Doc;
@@ -24,13 +22,21 @@ export interface DocComponentProps {
 export function DocComponent<P extends DocComponentProps>() {
     class Component extends Touchable<P> {
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
-        @computed get Document() { return this.props.Document; }
+        @computed get Document() {
+            return this.props.Document;
+        }
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
-        @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
+        @computed get rootDoc() {
+            return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document;
+        }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        @computed get layoutDoc() { return this.props.LayoutTemplateString ? this.props.Document : Doc.Layout(this.props.Document, this.props.LayoutTemplate?.()); }
+        @computed get layoutDoc() {
+            return this.props.LayoutTemplateString ? this.props.Document : Doc.Layout(this.props.Document, this.props.LayoutTemplate?.());
+        }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
-        @computed get dataDoc() { return this.props.Document[DataSym] as Doc; }
+        @computed get dataDoc() {
+            return this.props.Document[DataSym] as Doc;
+        }
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
@@ -55,32 +61,39 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
         //@computed get Document(): T { return schemaCtor(this.props.Document); }
 
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
-        @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
+        @computed get rootDoc() {
+            return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document;
+        }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
+        @computed get layoutDoc() {
+            return Doc.Layout(this.props.Document);
+        }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
-        @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
+        @computed get dataDoc() {
+            return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym];
+        }
         // key where data is stored
-        @computed get fieldKey() { return this.props.fieldKey; }
-
-        isContentActive = (outsideReaction?: boolean) => (
-            this.props.isContentActive?.() === false ? false :
-                (CurrentUserUtils.ActiveTool !== InkTool.None ||
-                    (this.props.isContentActive?.() || this.props.Document.forceActive ||
-                        this.props.isSelected(outsideReaction) ||
-                        this.props.rootSelected(outsideReaction)) ? true : undefined))
+        @computed get fieldKey() {
+            return this.props.fieldKey;
+        }
+
+        isContentActive = (outsideReaction?: boolean) =>
+            this.props.isContentActive?.() === false
+                ? false
+                : Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction)
+                ? true
+                : undefined;
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
     return Component;
 }
 
-
 ///  DocAnnotatbleComponent -return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image)
 export interface ViewBoxAnnotatableProps {
     Document: Doc;
     DataDoc?: Doc;
     fieldKey: string;
-    filterAddDocument?: (doc: Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+    filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
     isContentActive: () => boolean | undefined;
     select: (isCtrlPressed: boolean) => void;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
@@ -91,19 +104,29 @@ export interface ViewBoxAnnotatableProps {
 }
 export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() {
     class Component extends Touchable<P> {
-        @observable _annotationKeySuffix = () => "annotations";
+        @observable _annotationKeySuffix = () => 'annotations';
         @observable _isAnyChildContentActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
-        @computed get Document() { return this.props.Document; }
+        @computed get Document() {
+            return this.props.Document;
+        }
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
-        @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
+        @computed get rootDoc() {
+            return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document;
+        }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
+        @computed get layoutDoc() {
+            return Doc.Layout(this.props.Document);
+        }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
-        @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
+        @computed get dataDoc() {
+            return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym];
+        }
 
         // key where data is stored
-        @computed get fieldKey() { return this.props.fieldKey; }
+        @computed get fieldKey() {
+            return this.props.fieldKey;
+        }
 
         isAnyChildContentActive = () => this._isAnyChildContentActive;
 
@@ -111,20 +134,23 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
         styleFromLayoutString = (scale: number) => {
             const style: { [key: string]: any } = {};
-            const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"];
-            const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string:  { script }  into a value
-                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? "";
+            const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'background', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position'];
+            const replacer = (match: any, expr: string, offset: any, string: any) => {
+                // bcz: this executes a script to convert a property expression string:  { script }  into a value
+                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? '';
             };
             divKeys.map((prop: string) => {
                 const p = (this.props as any)[prop];
-                typeof p === "string" && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
+                typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
             });
             return style;
-        }
+        };
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
-        @computed public get annotationKey() { return this.fieldKey + (this._annotationKeySuffix() ? "-" + this._annotationKeySuffix() : ""); }
+        @computed public get annotationKey() {
+            return this.fieldKey + (this._annotationKeySuffix() ? '-' + this._annotationKeySuffix() : '');
+        }
 
         @action.bound
         removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean): boolean {
@@ -132,23 +158,26 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             const indocs = doc instanceof Doc ? [doc] : doc;
             const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin);
             if (docs.length) {
-                setTimeout(() => docs.map(doc => { // this allows 'addDocument' to see the annotationOn field in order to create a pushin
-                    Doc.SetInPlace(doc, "isPushpin", undefined, true);
-                    doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, "annotationOn", undefined, true);
-                }));
+                setTimeout(() =>
+                    docs.map(doc => {
+                        // this allows 'addDocument' to see the annotationOn field in order to create a pushin
+                        Doc.SetInPlace(doc, 'isPushpin', undefined, true);
+                        doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true);
+                    })
+                );
                 const targetDataDoc = this.dataDoc;
                 const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]);
                 const toRemove = value.filter(v => docs.includes(v));
 
                 if (toRemove.length !== 0) {
-                    const recent = CurrentUserUtils.MyRecentlyClosed;
+                    const recent = Doc.MyRecentlyClosed;
                     toRemove.forEach(doc => {
                         leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
                         Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         doc.context = undefined;
                         if (recent) {
-                            Doc.RemoveDocFromList(recent, "data", doc);
-                            Doc.AddDocToList(recent, "data", doc, undefined, true, true);
+                            Doc.RemoveDocFromList(recent, 'data', doc);
+                            Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
                         }
                     });
                     this.isAnyChildContentActive() && this.props.select(false);
@@ -172,12 +201,11 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey));
             }
             return false;
-        }
+        };
         @action.bound
         addDocument = (doc: Doc | Doc[], annotationKey?: string): boolean => {
             const docs = doc instanceof Doc ? [doc] : doc;
-            if (this.props.filterAddDocument?.(docs) === false ||
-                docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
+            if (this.props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
                 return false;
             }
             const targetDataDoc = this.props.Document[DataSym];
@@ -188,8 +216,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             if (added.length) {
                 if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
                     return false;
-                }
-                else {
+                } else {
                     if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) {
                         added.forEach(d => {
                             for (const [key, value] of Object.entries(this.props.Document[AclSym])) {
@@ -200,32 +227,34 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                     if (effectiveAcl === AclAugment) {
                         added.map(doc => {
-                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
+                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.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);
                         });
-                    }
-                    else {
-                        added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => {  // only make a pushpin if we have acl's to edit the document
-                            //DocUtils.LeavePushpin(doc);
-                            doc._stayInCollection = undefined;
-                            doc.context = this.props.Document;
-                            if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
+                    } else {
+                        added
+                            .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)))
+                            .map(doc => {
+                                // only make a pushpin if we have acl's to edit the document
+                                //DocUtils.LeavePushpin(doc);
+                                doc._stayInCollection = undefined;
+                                doc.context = this.props.Document;
+                                if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
 
-                            CurrentUserUtils.ActiveDashboard && inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
-                        });
+                                Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
+                            });
                         const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                         if (annoDocs instanceof List) annoDocs.push(...added);
                         else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
-                        targetDataDoc[(annotationKey ?? this.annotationKey) + "-lastModified"] = new DateField(new Date(Date.now()));
+                        targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now()));
                     }
                 }
             }
             return true;
-        }
+        };
 
-        whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
+        whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
     }
     return Component;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 9b8f7238d..bac51a11d 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,57 +1,54 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocCastAsync } from "../../fields/Doc";
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from "../../Utils";
+import { Cast, NumCast } from '../../fields/Types';
+import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { Docs } from '../documents/Documents';
-import { DocumentType } from '../documents/DocumentTypes';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { DragManager } from '../util/DragManager';
 import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
+import { undoBatch } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { TabDocView } from './collections/TabDocView';
 import './DocumentButtonBar.scss';
+import { Colors } from './global/globalEnums';
 import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
-import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
-import { TemplateMenu } from "./TemplateMenu";
-import React = require("react");
-import { PresBox } from './nodes/trails/PresBox';
-import { undoBatch } from '../util/UndoManager';
-import { CollectionViewType } from './collections/CollectionView';
-import { Colors } from './global/globalEnums';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
-const higflyout = require("@hig/flyout");
+import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
+import { TemplateMenu } from './TemplateMenu';
+import React = require('react');
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
-const cloud: IconProp = "cloud-upload-alt";
-const fetch: IconProp = "sync-alt";
+const cloud: IconProp = 'cloud-upload-alt';
+const fetch: IconProp = 'sync-alt';
 
 enum UtilityButtonState {
     Default,
     OpenRight,
-    OpenExternally
+    OpenExternally,
 }
 
 @observer
-export class DocumentButtonBar extends React.Component<{ views: () => (DocumentView | undefined)[], stack?: any }, {}> {
+export class DocumentButtonBar extends React.Component<{ views: () => (DocumentView | undefined)[]; stack?: any }, {}> {
     private _dragRef = React.createRef<HTMLDivElement>();
     private _pullAnimating = false;
     private _pushAnimating = false;
     private _pullColorAnimating = false;
 
-    @observable private pushIcon: IconProp = "arrow-alt-circle-up";
-    @observable private pullIcon: IconProp = "arrow-alt-circle-down";
-    @observable private pullColor: string = "white";
+    @observable private pushIcon: IconProp = 'arrow-alt-circle-up';
+    @observable private pullIcon: IconProp = 'arrow-alt-circle-down';
+    @observable private pullColor: string = 'white';
     @observable public isAnimatingFetch = false;
     @observable public isAnimatingPulse = false;
 
@@ -63,17 +60,21 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
 
     constructor(props: { views: () => (DocumentView | undefined)[] }) {
         super(props);
-        runInAction(() => DocumentButtonBar.Instance = this);
+        runInAction(() => (DocumentButtonBar.Instance = this));
     }
 
     public startPullOutcome = action((success: boolean) => {
         if (!this._pullAnimating) {
             this._pullAnimating = true;
-            this.pullIcon = success ? "check-circle" : "stop-circle";
-            setTimeout(() => runInAction(() => {
-                this.pullIcon = "arrow-alt-circle-down";
-                this._pullAnimating = false;
-            }), 1000);
+            this.pullIcon = success ? 'check-circle' : 'stop-circle';
+            setTimeout(
+                () =>
+                    runInAction(() => {
+                        this.pullIcon = 'arrow-alt-circle-down';
+                        this._pullAnimating = false;
+                    }),
+                1000
+            );
         }
     });
 
@@ -81,11 +82,15 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         this.isAnimatingPulse = false;
         if (!this._pushAnimating) {
             this._pushAnimating = true;
-            this.pushIcon = success ? "check-circle" : "stop-circle";
-            setTimeout(() => runInAction(() => {
-                this.pushIcon = "arrow-alt-circle-up";
-                this._pushAnimating = false;
-            }), 1000);
+            this.pushIcon = success ? 'check-circle' : 'stop-circle';
+            setTimeout(
+                () =>
+                    runInAction(() => {
+                        this.pushIcon = 'arrow-alt-circle-up';
+                        this._pushAnimating = false;
+                    }),
+                1000
+            );
         }
     });
 
@@ -93,165 +98,241 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         this.isAnimatingFetch = false;
         if (!this._pullColorAnimating) {
             this._pullColorAnimating = true;
-            this.pullColor = unchanged ? "lawngreen" : "red";
+            this.pullColor = unchanged ? 'lawngreen' : 'red';
             setTimeout(this.clearPullColor, 1000);
         }
     });
 
     private clearPullColor = action(() => {
-        this.pullColor = "white";
+        this.pullColor = 'white';
         this._pullColorAnimating = false;
     });
 
-    get view0() { return this.props.views()?.[0]; }
+    get view0() {
+        return this.props.views()?.[0];
+    }
 
     @computed
     get considerGoogleDocsPush() {
         const targetDoc = this.view0?.props.Document;
         const published = targetDoc && Doc.GetProto(targetDoc)[GoogleRef] !== undefined;
-        const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none";
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${published ? "Push" : "Publish"} to Google Docs`}</div></>}>
-            <div
-                className="documentButtonBar-button"
-                style={{ animation }}
-                onClick={async () => {
-                    await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
-                    !published && runInAction(() => this.isAnimatingPulse = true);
-                    DocumentButtonBar.hasPushedHack = false;
-                    targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1;
-                }}>
-                <FontAwesomeIcon className="documentdecorations-icon" icon={published ? (this.pushIcon as any) : cloud} size={published ? "sm" : "xs"} />
-            </div></Tooltip>;
+        const animation = this.isAnimatingPulse ? 'shadow-pulse 1s linear infinite' : 'none';
+        return !targetDoc ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{`${published ? 'Push' : 'Publish'} to Google Docs`}</div>
+                    </>
+                }>
+                <div
+                    className="documentButtonBar-button"
+                    style={{ animation }}
+                    onClick={async () => {
+                        await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
+                        !published && runInAction(() => (this.isAnimatingPulse = true));
+                        DocumentButtonBar.hasPushedHack = false;
+                        targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1;
+                    }}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon={published ? (this.pushIcon as any) : cloud} size={published ? 'sm' : 'xs'} />
+                </div>
+            </Tooltip>
+        );
     }
 
     @computed
     get considerGoogleDocsPull() {
         const targetDoc = this.view0?.props.Document;
         const dataDoc = targetDoc && Doc.GetProto(targetDoc);
-        const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
+        const animation = this.isAnimatingFetch ? 'spin 0.5s linear infinite' : 'none';
 
         const title = (() => {
             switch (this.openHover) {
                 default:
-                case UtilityButtonState.Default: return `${!dataDoc?.googleDocUnchanged ? "Pull from" : "Fetch"} Google Docs`;
-                case UtilityButtonState.OpenRight: return "Open in Right Split";
-                case UtilityButtonState.OpenExternally: return "Open in new Browser Tab";
+                case UtilityButtonState.Default:
+                    return `${!dataDoc?.googleDocUnchanged ? 'Pull from' : 'Fetch'} Google Docs`;
+                case UtilityButtonState.OpenRight:
+                    return 'Open in Right Split';
+                case UtilityButtonState.OpenExternally:
+                    return 'Open in new Browser Tab';
             }
         })();
 
-        return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) : <Tooltip
-            title={<><div className="dash-tooltip">{title}</div></>}>
-            <div className="documentButtonBar-button"
-                style={{ backgroundColor: this.pullColor }}
-                onPointerEnter={action(e => {
-                    if (e.altKey) {
-                        this.openHover = UtilityButtonState.OpenExternally;
-                    } else if (e.shiftKey) {
-                        this.openHover = UtilityButtonState.OpenRight;
-                    }
-                })}
-                onPointerLeave={action(() => this.openHover = UtilityButtonState.Default)}
-                onClick={async e => {
-                    const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`;
-                    if (e.shiftKey) {
-                        e.preventDefault();
-                        let googleDoc = await Cast(dataDoc.googleDoc, Doc);
-                        if (!googleDoc) {
-                            const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false };
-                            googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
-                            dataDoc.googleDoc = googleDoc;
+        return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{title}</div>
+                    </>
+                }>
+                <div
+                    className="documentButtonBar-button"
+                    style={{ backgroundColor: this.pullColor }}
+                    onPointerEnter={action(e => {
+                        if (e.altKey) {
+                            this.openHover = UtilityButtonState.OpenExternally;
+                        } else if (e.shiftKey) {
+                            this.openHover = UtilityButtonState.OpenRight;
                         }
-                        CollectionDockingView.AddSplit(googleDoc, "right");
-                    } else if (e.altKey) {
-                        e.preventDefault();
-                        window.open(googleDocUrl);
-                    } else {
-                        this.clearPullColor();
-                        DocumentButtonBar.hasPulledHack = false;
-                        targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1;
-                        dataDoc.googleDocUnchanged && runInAction(() => this.isAnimatingFetch = true);
-                    }
-                }}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm"
-                    style={{ WebkitAnimation: animation, MozAnimation: animation }}
-                    icon={(() => {
-                        switch (this.openHover) {
-                            default:
-                            case UtilityButtonState.Default: return dataDoc.googleDocUnchanged === false ? (this.pullIcon as any) : fetch;
-                            case UtilityButtonState.OpenRight: return "arrow-alt-circle-right";
-                            case UtilityButtonState.OpenExternally: return "share";
+                    })}
+                    onPointerLeave={action(() => (this.openHover = UtilityButtonState.Default))}
+                    onClick={async e => {
+                        const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`;
+                        if (e.shiftKey) {
+                            e.preventDefault();
+                            let googleDoc = await Cast(dataDoc.googleDoc, Doc);
+                            if (!googleDoc) {
+                                const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false };
+                                googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
+                                dataDoc.googleDoc = googleDoc;
+                            }
+                            CollectionDockingView.AddSplit(googleDoc, 'right');
+                        } else if (e.altKey) {
+                            e.preventDefault();
+                            window.open(googleDocUrl);
+                        } else {
+                            this.clearPullColor();
+                            DocumentButtonBar.hasPulledHack = false;
+                            targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1;
+                            dataDoc.googleDocUnchanged && runInAction(() => (this.isAnimatingFetch = true));
                         }
-                    })()}
-                />
-            </div></Tooltip>;
+                    }}>
+                    <FontAwesomeIcon
+                        className="documentdecorations-icon"
+                        size="sm"
+                        style={{ WebkitAnimation: animation, MozAnimation: animation }}
+                        icon={(() => {
+                            switch (this.openHover) {
+                                default:
+                                case UtilityButtonState.Default:
+                                    return dataDoc.googleDocUnchanged === false ? (this.pullIcon as any) : fetch;
+                                case UtilityButtonState.OpenRight:
+                                    return 'arrow-alt-circle-right';
+                                case UtilityButtonState.OpenExternally:
+                                    return 'share';
+                            }
+                        })()}
+                    />
+                </div>
+            </Tooltip>
+        );
     }
     @computed
     get followLinkButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={
-            <div className="dash-tooltip">{"Set onClick to follow primary link"}</div>}>
-            <div className="documentButtonBar-icon"
-                style={{ backgroundColor: targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
-                onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="hand-point-right" />
-            </div>
-        </Tooltip>;
+        return !targetDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">{'Set onClick to follow primary link'}</div>}>
+                <div
+                    className="documentButtonBar-icon"
+                    style={{ backgroundColor: targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
+                    onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="hand-point-right" />
+                </div>
+            </Tooltip>
+        );
     }
     @computed
     get pinButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={
-            <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={(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>;
+        return !targetDoc ? null : (
+            <Tooltip title={<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={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>
+        );
     }
 
     @computed
     get shareButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Open Sharing Manager"}</div></>}>
-            <div className="documentButtonBar-icon" style={{ color: "white" }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
-                <FontAwesomeIcon className="documentdecorations-icon" icon="users" />
-            </div></Tooltip >;
+        return !targetDoc ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{'Open Sharing Manager'}</div>
+                    </>
+                }>
+                <div className="documentButtonBar-icon" style={{ color: 'white' }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon="users" />
+                </div>
+            </Tooltip>
+        );
     }
 
     @computed
     get menuButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`Open Context Menu`}</div></>}>
-            <div className="documentButtonBar-icon" style={{ color: "white", cursor: "pointer" }} onClick={e => this.openContextMenu(e)}>
-                <FontAwesomeIcon className="documentdecorations-icon" icon="bars" />
-            </div></Tooltip >;
+        return !targetDoc ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{`Open Context Menu`}</div>
+                    </>
+                }>
+                <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onClick={e => this.openContextMenu(e)}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon="bars" />
+                </div>
+            </Tooltip>
+        );
     }
 
     @computed
     get moreButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${CurrentUserUtils.propertiesWidth > 0 ? "Close" : "Open"} Properties Panel`}</div></>}>
-            <div className="documentButtonBar-icon" style={{ color: "white", cursor: "e-resize" }} onClick={action(e =>
-                CurrentUserUtils.propertiesWidth = CurrentUserUtils.propertiesWidth > 0 ? 0 : 250)}>
-                <FontAwesomeIcon className="documentdecorations-icon" icon="ellipsis-h"
-                />
-            </div></Tooltip >;
+        return !targetDoc ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{`${SettingsManager.propertiesWidth > 0 ? 'Close' : 'Open'} Properties Panel`}</div>
+                    </>
+                }>
+                <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'e-resize' }} onClick={action(e => (SettingsManager.propertiesWidth = SettingsManager.propertiesWidth > 0 ? 0 : 250))}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon="ellipsis-h" />
+                </div>
+            </Tooltip>
+        );
     }
 
     @computed
     get metadataButton() {
         const view0 = this.view0;
-        return !view0 ? (null) : <Tooltip title={<><div className="dash-tooltip">Show metadata panel</div></>}>
-            <div className="documentButtonBar-linkFlyout">
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP}
-                    content={<MetadataEntryMenu docs={this.props.views().filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction />  /* tfs: @bcz This might need to be the data document? */}>
-                    <div className={"documentButtonBar-linkButton-" + "empty"} onPointerDown={e => e.stopPropagation()} >
-                        {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" />}
-                    </div>
-                </Flyout>
-            </div></Tooltip>;
+        return !view0 ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">Show metadata panel</div>
+                    </>
+                }>
+                <div className="documentButtonBar-linkFlyout">
+                    <Flyout
+                        anchorPoint={anchorPoints.LEFT_TOP}
+                        content={
+                            <MetadataEntryMenu
+                                docs={this.props
+                                    .views()
+                                    .filter(dv => dv)
+                                    .map(dv => dv!.props.Document)}
+                                suggestWithFunction
+                            /> /* tfs: @bcz This might need to be the data document? */
+                        }>
+                        <div className={'documentButtonBar-linkButton-' + 'empty'} onPointerDown={e => e.stopPropagation()}>
+                            {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" />}
+                        </div>
+                    </Flyout>
+                </div>
+            </Tooltip>
+        );
     }
     @observable _aliasDown = false;
     onAliasButtonDown = action((e: React.PointerEvent): void => {
@@ -264,13 +345,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             const dragDocView = this.view0!;
             const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
             const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-            dragData.defaultDropAction = "alias";
+            dragData.defaultDropAction = 'alias';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false });
             return true;
         }
         return false;
-    }
+    };
 
     _ref = React.createRef<HTMLDivElement>();
     @observable _tooltipOpen: boolean = false;
@@ -278,49 +359,60 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     get templateButton() {
         const view0 = this.view0;
         const views = this.props.views();
-        return !view0 ? (null) :
-            <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embeddable alias</div>} open={this._tooltipOpen} onClose={action(() => this._tooltipOpen = false)} placement="bottom">
-                <div className="documentButtonBar-linkFlyout" ref={this._dragRef}
-                    onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))} >
-
-                    <Flyout anchorPoint={anchorPoints.LEFT_TOP} onOpen={action(() => this._aliasDown = true)} onClose={action(() => this._aliasDown = false)}
-                        content={!this._aliasDown ? (null) :
-                            <div ref={this._ref}> <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} /></div>}>
-                        <div className={"documentButtonBar-linkButton-empty"} ref={this._dragRef} onPointerDown={this.onAliasButtonDown} >
+        return !view0 ? null : (
+            <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embeddable alias</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
+                <div className="documentButtonBar-linkFlyout" ref={this._dragRef} onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}>
+                    <Flyout
+                        anchorPoint={anchorPoints.LEFT_TOP}
+                        onOpen={action(() => (this._aliasDown = true))}
+                        onClose={action(() => (this._aliasDown = false))}
+                        content={
+                            !this._aliasDown ? null : (
+                                <div ref={this._ref}>
+                                    {' '}
+                                    <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} />
+                                </div>
+                            )
+                        }>
+                        <div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onAliasButtonDown}>
                             {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
                         </div>
                     </Flyout>
                 </div>
-            </Tooltip>;
+            </Tooltip>
+        );
     }
 
     openContextMenu = (e: React.MouseEvent) => {
         let child = SelectionManager.Views()[0].ContentDiv!.children[0];
         while (child.children.length) {
-            const next = Array.from(child.children).find(c => c.className?.toString().includes("SVGAnimatedString") || typeof (c.className) === "string");
+            const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string');
             if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
             if (next?.className?.toString().includes(DashFieldView.name)) break;
             if (next) child = next;
             else break;
         }
         simulateMouseClick(child, e.clientX, e.clientY - 30, e.screenX, e.screenY - 30);
-    }
+    };
 
     render() {
-        if (!this.view0) return (null);
+        if (!this.view0) return null;
 
         const isText = this.view0.props.Document[this.view0.LayoutFieldKey] instanceof RichTextField;
         const doc = this.view0?.props.Document;
         const considerPull = isText && this.considerGoogleDocsPull;
         const considerPush = isText && this.considerGoogleDocsPush;
-        return <div className="documentButtonBar">
-            <div className="documentButtonBar-button">
-                <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
-            </div>
-            {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink !== doc ? <div className="documentButtonBar-button">
-                <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
-            </div> : (null)}
-            {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
+        return (
+            <div className="documentButtonBar">
+                <div className="documentButtonBar-button">
+                    <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
+                </div>
+                {(DocumentLinksButton.StartLink || Doc.UserDoc()['documentLinksButton-fullMenu']) && DocumentLinksButton.StartLink !== doc ? (
+                    <div className="documentButtonBar-button">
+                        <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
+                    </div>
+                ) : null}
+                {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.templateButton}
             </div>
             /*<div className="documentButtonBar-button">
@@ -329,27 +421,22 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.contextButton}
             </div> */}
-            {!SelectionManager.Views()?.some(v => v.allLinks.length) ? (null) : <div className="documentButtonBar-button">
-                {this.followLinkButton}
-            </div>}
-            <div className="documentButtonBar-button">
-                {this.pinButton}
-            </div>
-            {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
-                {this.shareButton}
-            </div>}
-            {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
-                {this.considerGoogleDocsPush}
-            </div>}
-            <div className="documentButtonBar-button" style={{ display: !considerPull ? "none" : "" }}>
-                {this.considerGoogleDocsPull}
-            </div>
-            <div className="documentButtonBar-button">
-                {this.menuButton}
-            </div>
-            {/* {Doc.noviceMode ? (null) : <div className="documentButtonBar-button">
+                {!SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
+                <div className="documentButtonBar-button">{this.pinButton}</div>
+                {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
+                {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : (
+                    <div className="documentButtonBar-button" style={{ display: !considerPush ? 'none' : '' }}>
+                        {this.considerGoogleDocsPush}
+                    </div>
+                )}
+                <div className="documentButtonBar-button" style={{ display: !considerPull ? 'none' : '' }}>
+                    {this.considerGoogleDocsPull}
+                </div>
+                <div className="documentButtonBar-button">{this.menuButton}</div>
+                {/* {Doc.noviceMode ? (null) : <div className="documentButtonBar-button">
                 {this.moreButton}
             </div>} */}
-        </div>;
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9544c588b..c55daca3f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,13 +7,12 @@ import { DateField } from '../../fields/DateField';
 import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
 import { Document } from '../../fields/documentSchemas';
 import { InkField } from '../../fields/InkField';
-import { ComputedField, ScriptField } from '../../fields/ScriptField';
-import { Cast, FieldValue, NumCast, StrCast } from '../../fields/Types';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
 import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, returnFalse, setupMoveUpEvents, numberValue, numbersAlmostEqual } from '../../Utils';
+import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { DragManager } from '../util/DragManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SnappingManager } from '../util/SnappingManager';
@@ -22,7 +21,7 @@ import { CollectionDockingView } from './collections/CollectionDockingView';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
-import { KeyManager } from './GlobalKeyHandler';
+import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
@@ -30,7 +29,6 @@ import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
-import { Colors } from './global/globalEnums';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -105,10 +103,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(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
+                                Doc.RemoveDocFromList(Doc.MyPublishedDocs, undefined, d.rootDoc);
                             }
                             if (!StrCast(d.rootDoc.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) {
-                                Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
+                                Doc.AddDocToList(Doc.MyPublishedDocs, undefined, d.rootDoc);
                             }
                         }
                         //@ts-ignore
@@ -266,7 +264,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     };
 
     onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
- 
+
     /**
      * Handles setting up events when user clicks on the border radius editor
      * @param e PointerEvent
@@ -284,21 +282,20 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 const y = this.Bounds.y + 3;
                 const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
                 let dist = Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y));
-                if (e.clientX < x && e.clientY < y) dist = 0
+                if (e.clientX < x && e.clientY < y) dist = 0;
                 SelectionManager.Views()
                     .map(dv => dv.props.Document)
                     .map(doc => {
-                        const docMax = Math.min(NumCast(doc.width)/2, NumCast(doc.height)/2);
-                        const ratio = dist/maxDist;
+                        const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2);
+                        const ratio = dist / maxDist;
                         const radius = Math.min(1, ratio) * docMax;
                         doc.borderRounding = `${radius}px`;
-                    }
-                    );
+                    });
                 return false;
             }, // moveEvent
             action(e => {
                 this._isRounding = false;
-                this._resizeUndo?.end()
+                this._resizeUndo?.end();
             }), // upEvent
             e => {} // clickEvent
         );
@@ -325,8 +322,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         // Rotation between -360 and 360
                         let newRotation = (oldRotation - (angle * 180) / Math.PI) % 360;
 
-                        const diff = Math.round(newRotation / 45) - newRotation / 45
-                        if (diff < .05) {
+                        const diff = Math.round(newRotation / 45) - newRotation / 45;
+                        if (diff < 0.05) {
                             console.log('show lines');
                         }
                         dv.rootDoc._jitterRotation = newRotation;
@@ -337,12 +334,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             action(() => {
                 SelectionManager.Views().forEach(dv => {
                     const oldRotation = NumCast(dv.rootDoc._jitterRotation);
-                    const diff = Math.round(oldRotation / 45) - oldRotation / 45
-                    if (diff < .05) {
+                    const diff = Math.round(oldRotation / 45) - oldRotation / 45;
+                    if (diff < 0.05) {
                         let newRotation = Math.round(oldRotation / 45) * 45;
                         dv.rootDoc._jitterRotation = newRotation;
                     }
-                })
+                });
                 this._isRotating = false;
                 rotateUndo?.end();
                 UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
@@ -599,13 +596,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc._isGroup || this._isRounding || this._isRotating;
         const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
-        const hideDocumentButtonBar = seldoc.props.hideDocumentButtonBar || seldoc.rootDoc.hideDocumentButtonBar || this._isRounding || 
-        this._isRotating;
+        const hideDocumentButtonBar = seldoc.props.hideDocumentButtonBar || seldoc.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
         const hideOpenButton =
-            seldoc.props.hideOpenButton || seldoc.rootDoc.hideOpenButton || SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton) || this._isRounding || this._isRotating;
+            seldoc.props.hideOpenButton ||
+            seldoc.rootDoc.hideOpenButton ||
+            SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton) ||
+            this._isRounding ||
+            this._isRotating;
         const hideDeleteButton =
-            this._isRounding || 
+            this._isRounding ||
             this._isRotating ||
             seldoc.props.hideDeleteButton ||
             seldoc.rootDoc.hideDeleteButton ||
@@ -635,7 +635,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             </Tooltip>
         );
 
-        const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
+        const colorScheme = StrCast(Doc.ActiveDashboard?.colorScheme);
         const titleArea = hideTitle ? null : this._editingTitle ? (
             <input
                 ref={this._keyinput}
@@ -665,14 +665,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         // Rotation constants: Only allow rotation on ink and images
         const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
         const rotation = NumCast(seldoc.rootDoc._jitterRotation);
-        
 
         const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
 
         // Radius constants
         const useRounding = seldoc.ComponentView instanceof ImageBox || seldoc.ComponentView instanceof FormattedTextBox;
         const borderRadius = numberValue(StrCast(seldoc.rootDoc.borderRounding));
-        const docMax = Math.min(NumCast(seldoc.rootDoc.width)/2, NumCast(seldoc.rootDoc.height)/2);
+        const docMax = Math.min(NumCast(seldoc.rootDoc.width) / 2, NumCast(seldoc.rootDoc.height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
         const radiusHandle = (borderRadius / docMax) * maxDist;
         const radiusHandleLocation = Math.min(radiusHandle, maxDist);
@@ -722,7 +721,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                     <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} />
                                     <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} />
 
-                                    {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}   
+                                    {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}
                                 </>
                             )}
 
@@ -733,11 +732,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             )}
 
                             {useRounding && (
-                                <div key="rad" style={{
+                                <div
+                                    key="rad"
+                                    style={{
                                         background: `${this._isRounding ? Colors.MEDIUM_BLUE : undefined}`,
-                                        left:`${radiusHandleLocation + 3}`,
-                                        top:`${radiusHandleLocation + 23}`
-                                    }} className={`documentDecorations-borderRadius`} onPointerDown={this.onRadiusDown} onContextMenu={e => e.preventDefault()} 
+                                        left: `${radiusHandleLocation + 3}`,
+                                        top: `${radiusHandleLocation + 23}`,
+                                    }}
+                                    className={`documentDecorations-borderRadius`}
+                                    onPointerDown={this.onRadiusDown}
+                                    onContextMenu={e => e.preventDefault()}
                                 />
                             )}
 
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index e960f5cca..5a68e9091 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -1,37 +1,52 @@
-import React = require("react");
+import React = require('react');
 import * as fitCurve from 'fit-curve';
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc } from "../../fields/Doc";
-import { InkData, InkTool } from "../../fields/InkField";
-import { Cast, FieldValue, NumCast } from "../../fields/Types";
-import MobileInkOverlay from "../../mobile/MobileInkOverlay";
-import { GestureUtils } from "../../pen-gestures/GestureUtils";
-import { MobileInkOverlayContent } from "../../server/Message";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils";
-import { CognitiveServices } from "../cognitive_services/CognitiveServices";
-import { DocUtils } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { InteractionUtils } from "../util/InteractionUtils";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
-import { SelectionManager } from "../util/SelectionManager";
-import { Transform } from "../util/Transform";
-import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
-import "./GestureOverlay.scss";
-import { ActiveArrowEnd, ActiveArrowScale, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "./InkingStroke";
-import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox";
-import { DocumentView } from "./nodes/DocumentView";
-import { RadialMenu } from "./nodes/RadialMenu";
-import HorizontalPalette from "./Palette";
-import { Touchable } from "./Touchable";
-import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
-import { InkTranscription } from "./InkTranscription";
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../fields/Doc';
+import { InkData, InkTool } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, FieldValue, NumCast } from '../../fields/Types';
+import MobileInkOverlay from '../../mobile/MobileInkOverlay';
+import { GestureUtils } from '../../pen-gestures/GestureUtils';
+import { MobileInkOverlayContent } from '../../server/Message';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { CognitiveServices } from '../cognitive_services/CognitiveServices';
+import { Docs, DocUtils } from '../documents/Documents';
+import { InteractionUtils } from '../util/InteractionUtils';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { SelectionManager } from '../util/SelectionManager';
+import { Transform } from '../util/Transform';
+import { CollectionFreeFormViewChrome } from './collections/CollectionMenu';
+import './GestureOverlay.scss';
+import {
+    ActiveArrowEnd,
+    ActiveArrowScale,
+    ActiveArrowStart,
+    ActiveDash,
+    ActiveFillColor,
+    ActiveInkBezierApprox,
+    ActiveInkColor,
+    ActiveInkWidth,
+    SetActiveArrowStart,
+    SetActiveDash,
+    SetActiveFillColor,
+    SetActiveInkColor,
+    SetActiveInkWidth,
+} from './InkingStroke';
+import { InkTranscription } from './InkTranscription';
+import { checkInksToGroup } from './nodes/button/FontIconBox';
+import { DocumentView } from './nodes/DocumentView';
+import { RadialMenu } from './nodes/RadialMenu';
+import HorizontalPalette from './Palette';
+import { Touchable } from './Touchable';
+import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu';
 
 @observer
 export class GestureOverlay extends Touchable {
     static Instance: GestureOverlay;
 
-    @observable public InkShape: string = "";
+    @observable public InkShape: string = '';
     @observable public SavedColor?: string;
     @observable public SavedWidth?: number;
     @observable public Tool: ToolglassTools = ToolglassTools.None;
@@ -42,14 +57,18 @@ export class GestureOverlay extends Touchable {
     @observable private _menuX: number = -300;
     @observable private _menuY: number = -300;
     @observable private _pointerY?: number;
-    @observable private _points: { X: number, Y: number }[] = [];
+    @observable private _points: { X: number; Y: number }[] = [];
     @observable private _strokes: InkData[] = [];
     @observable private _palette?: JSX.Element;
     @observable private _clipboardDoc?: JSX.Element;
     @observable private _possibilities: JSX.Element[] = [];
 
-    @computed private get height(): number { return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100); }
-    @computed private get showBounds() { return this.Tool !== ToolglassTools.None; }
+    @computed private get height(): number {
+        return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100);
+    }
+    @computed private get showBounds() {
+        return this.Tool !== ToolglassTools.None;
+    }
 
     @observable private showMobileInkOverlay: boolean = false;
 
@@ -70,10 +89,73 @@ export class GestureOverlay extends Touchable {
         GestureOverlay.Instance = this;
     }
 
+    static setupThumbButtons(doc: Doc) {
+        const docProtoData: { title: string; icon: string; drag?: string; ignoreClick?: boolean; pointerDown?: string; pointerUp?: string; clipboard?: Doc; backgroundColor?: string; dragFactory?: Doc }[] = [
+            { title: 'use pen', icon: 'pen-nib', pointerUp: 'resetPen()', pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: 'blue' },
+            { title: 'use highlighter', icon: 'highlighter', pointerUp: 'resetPen()', pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: 'yellow' },
+            {
+                title: 'notepad',
+                icon: 'clipboard',
+                pointerUp: 'GestureOverlay.Instance.closeFloatingDoc()',
+                pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)',
+                clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }),
+                backgroundColor: 'orange',
+            },
+            { title: 'interpret text', icon: 'font', pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: 'orange' },
+            { title: 'ignore gestures', icon: 'signature', pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: 'green' },
+        ];
+        return docProtoData.map(data =>
+            Docs.Create.FontIconDocument({
+                _nativeWidth: 10,
+                _nativeHeight: 10,
+                _width: 10,
+                _height: 10,
+                title: data.title,
+                icon: data.icon,
+                _dropAction: data.pointerDown ? 'copy' : undefined,
+                ignoreClick: data.ignoreClick,
+                onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
+                clipboard: data.clipboard,
+                onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
+                onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
+                backgroundColor: data.backgroundColor,
+                _removeDropProperties: new List<string>(['dropAction']),
+                dragFactory: data.dragFactory,
+                system: true,
+            })
+        );
+    }
+
+    static setupThumbDoc(userDoc: Doc) {
+        if (!userDoc.thumbDoc) {
+            const thumbDoc = Docs.Create.LinearDocument(GestureOverlay.setupThumbButtons(userDoc), {
+                _width: 100,
+                _height: 50,
+                ignoreClick: true,
+                _lockedPosition: true,
+                title: 'buttons',
+                _autoHeight: true,
+                _yMargin: 5,
+                linearViewIsExpanded: true,
+                backgroundColor: 'white',
+                system: true,
+            });
+            thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
+                _width: 300,
+                _height: 25,
+                _autoHeight: true,
+                linearViewIsExpanded: true,
+                flexDirection: 'column',
+                system: true,
+            });
+            userDoc.thumbDoc = thumbDoc;
+        }
+        return Cast(userDoc.thumbDoc, Doc);
+    }
     componentDidMount = () => {
-        this._thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc));
+        this._thumbDoc = FieldValue(Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc));
         this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc));
-    }
+    };
 
     // TODO: nda - add dragging groups with one finger drag and have to click into group to scroll within the group
 
@@ -84,24 +166,24 @@ export class GestureOverlay extends Touchable {
         const ntt: (React.Touch | Touch)[] = Array.from(e.targetTouches);
         const nct: (React.Touch | Touch)[] = Array.from(e.changedTouches);
         const nt: (React.Touch | Touch)[] = Array.from(e.touches);
-        this._hands.forEach((hand) => {
+        this._hands.forEach(hand => {
             for (let i = 0; i < e.targetTouches.length; i++) {
                 const pt = e.targetTouches.item(i);
-                if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
+                if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
                     ntt.splice(ntt.indexOf(pt), 1);
                 }
             }
 
             for (let i = 0; i < e.changedTouches.length; i++) {
                 const pt = e.changedTouches.item(i);
-                if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
+                if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
                     nct.splice(nct.indexOf(pt), 1);
                 }
             }
 
             for (let i = 0; i < e.touches.length; i++) {
                 const pt = e.touches.item(i);
-                if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
+                if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
                     nt.splice(nt.indexOf(pt), 1);
                 }
             }
@@ -110,8 +192,8 @@ export class GestureOverlay extends Touchable {
     }
 
     onReactTouchStart = (te: React.TouchEvent) => {
-        document.removeEventListener("touchmove", this.onReactHoldTouchMove);
-        document.removeEventListener("touchend", this.onReactHoldTouchEnd);
+        document.removeEventListener('touchmove', this.onReactHoldTouchMove);
+        document.removeEventListener('touchend', this.onReactHoldTouchEnd);
         if (RadialMenu.Instance?._display === true) {
             te.preventDefault();
             te.stopPropagation();
@@ -129,7 +211,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();
-                CurrentUserUtils.ActiveTool = InkTool.None;
+                Doc.ActiveTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
         }
@@ -147,18 +229,16 @@ export class GestureOverlay extends Touchable {
         if (nts.nt.length < 5) {
             const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY);
             target?.dispatchEvent(
-                new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>("dashOnTouchStart",
-                    {
-                        bubbles: true,
-                        detail: {
-                            fingers: this.prevPoints.size,
-                            targetTouches: nts.ntt,
-                            touches: nts.nt,
-                            changedTouches: nts.nct,
-                            touchEvent: te
-                        }
-                    }
-                )
+                new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>('dashOnTouchStart', {
+                    bubbles: true,
+                    detail: {
+                        fingers: this.prevPoints.size,
+                        targetTouches: nts.ntt,
+                        touches: nts.nt,
+                        changedTouches: nts.nct,
+                        touchEvent: te,
+                    },
+                })
             );
             if (nts.nt.length === 1) {
                 // -- radial menu code --
@@ -167,45 +247,41 @@ export class GestureOverlay extends Touchable {
                     const pt: any = te.touches[te.touches?.length - 1];
                     if (nts.nt.length === 1 && pt.radiusX > 1 && pt.radiusY > 1) {
                         target?.dispatchEvent(
-                            new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>("dashOnTouchHoldStart",
-                                {
-                                    bubbles: true,
-                                    detail: {
-                                        fingers: this.prevPoints.size,
-                                        targetTouches: nts.ntt,
-                                        touches: nts.nt,
-                                        changedTouches: nts.nct,
-                                        touchEvent: te
-                                    }
-                                }
-                            )
+                            new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>('dashOnTouchHoldStart', {
+                                bubbles: true,
+                                detail: {
+                                    fingers: this.prevPoints.size,
+                                    targetTouches: nts.ntt,
+                                    touches: nts.nt,
+                                    changedTouches: nts.nct,
+                                    touchEvent: te,
+                                },
+                            })
                         );
                         this._holdTimer = undefined;
-                        document.removeEventListener("touchmove", this.onReactTouchMove);
-                        document.removeEventListener("touchend", this.onReactTouchEnd);
-                        document.removeEventListener("touchmove", this.onReactHoldTouchMove);
-                        document.removeEventListener("touchend", this.onReactHoldTouchEnd);
-                        document.addEventListener("touchmove", this.onReactHoldTouchMove);
-                        document.addEventListener("touchend", this.onReactHoldTouchEnd);
+                        document.removeEventListener('touchmove', this.onReactTouchMove);
+                        document.removeEventListener('touchend', this.onReactTouchEnd);
+                        document.removeEventListener('touchmove', this.onReactHoldTouchMove);
+                        document.removeEventListener('touchend', this.onReactHoldTouchEnd);
+                        document.addEventListener('touchmove', this.onReactHoldTouchMove);
+                        document.addEventListener('touchend', this.onReactHoldTouchEnd);
                     }
-
-                }, (500));
-            }
-            else {
+                }, 500);
+            } else {
                 this._holdTimer && clearTimeout(this._holdTimer);
             }
-            document.removeEventListener("touchmove", this.onReactTouchMove);
-            document.removeEventListener("touchend", this.onReactTouchEnd);
-            document.addEventListener("touchmove", this.onReactTouchMove);
-            document.addEventListener("touchend", this.onReactTouchEnd);
+            document.removeEventListener('touchmove', this.onReactTouchMove);
+            document.removeEventListener('touchend', this.onReactTouchEnd);
+            document.addEventListener('touchmove', this.onReactTouchMove);
+            document.addEventListener('touchend', this.onReactTouchEnd);
         }
         // otherwise, handle as a hand event
         else {
             this.handleHandDown(te);
-            document.removeEventListener("touchmove", this.onReactTouchMove);
-            document.removeEventListener("touchend", this.onReactTouchEnd);
+            document.removeEventListener('touchmove', this.onReactTouchMove);
+            document.removeEventListener('touchend', this.onReactTouchEnd);
         }
-    }
+    };
 
     onReactTouchMove = (e: TouchEvent) => {
         const nts: any = this.getNewTouches(e);
@@ -213,19 +289,18 @@ export class GestureOverlay extends Touchable {
         this._holdTimer = undefined;
 
         document.dispatchEvent(
-            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchMove",
-                {
-                    bubbles: true,
-                    detail: {
-                        fingers: this.prevPoints.size,
-                        targetTouches: nts.ntt,
-                        touches: nts.nt,
-                        changedTouches: nts.nct,
-                        touchEvent: e
-                    }
-                })
+            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchMove', {
+                bubbles: true,
+                detail: {
+                    fingers: this.prevPoints.size,
+                    targetTouches: nts.ntt,
+                    touches: nts.nt,
+                    changedTouches: nts.nct,
+                    touchEvent: e,
+                },
+            })
         );
-    }
+    };
 
     onReactTouchEnd = (e: TouchEvent) => {
         const nts: any = this.getNewTouches(e);
@@ -233,17 +308,16 @@ export class GestureOverlay extends Touchable {
         this._holdTimer = undefined;
 
         document.dispatchEvent(
-            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchEnd",
-                {
-                    bubbles: true,
-                    detail: {
-                        fingers: this.prevPoints.size,
-                        targetTouches: nts.ntt,
-                        touches: nts.nt,
-                        changedTouches: nts.nct,
-                        touchEvent: e
-                    }
-                })
+            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchEnd', {
+                bubbles: true,
+                detail: {
+                    fingers: this.prevPoints.size,
+                    targetTouches: nts.ntt,
+                    touches: nts.nt,
+                    changedTouches: nts.nct,
+                    touchEvent: e,
+                },
+            })
         );
 
         // cleanup any lingering pointers
@@ -257,11 +331,11 @@ export class GestureOverlay extends Touchable {
         }
 
         if (this.prevPoints.size === 0) {
-            document.removeEventListener("touchmove", this.onReactTouchMove);
-            document.removeEventListener("touchend", this.onReactTouchEnd);
+            document.removeEventListener('touchmove', this.onReactTouchMove);
+            document.removeEventListener('touchend', this.onReactTouchEnd);
         }
         e.stopPropagation();
-    }
+    };
 
     handleHandDown = async (e: React.TouchEvent) => {
         this._holdTimer && clearTimeout(this._holdTimer);
@@ -285,17 +359,17 @@ export class GestureOverlay extends Touchable {
         }
 
         // this chunk of code determines whether this is a left hand or a right hand, as well as which pointer is the thumb and pointer
-        const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
+        const thumb = fingers.reduce((a, v) => (a.clientY > v.clientY ? a : v), fingers[0]);
         const rightMost = Math.max(...fingers.map(f => f.clientX));
         const leftMost = Math.min(...fingers.map(f => f.clientX));
         let pointer: React.Touch | undefined;
         // left hand
         if (thumb.clientX === rightMost) {
-            pointer = fingers.reduce((a, v) => a.clientX > v.clientX || v.identifier === thumb.identifier ? a : v);
+            pointer = fingers.reduce((a, v) => (a.clientX > v.clientX || v.identifier === thumb.identifier ? a : v));
         }
         // right hand
         else if (thumb.clientX === leftMost) {
-            pointer = fingers.reduce((a, v) => a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v);
+            pointer = fingers.reduce((a, v) => (a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v));
         }
         this.pointerIdentifier = pointer?.identifier;
 
@@ -316,7 +390,7 @@ export class GestureOverlay extends Touchable {
         const minY = Math.min(...others.map(f => f.clientY));
 
         // load up the palette collection around the thumb
-        const thumbDoc = await Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc);
+        const thumbDoc = await Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc);
         if (thumbDoc) {
             runInAction(() => {
                 RadialMenu.Instance._display = false;
@@ -331,11 +405,11 @@ export class GestureOverlay extends Touchable {
         }
 
         this.removeMoveListeners();
-        document.removeEventListener("touchmove", this.handleHandMove);
-        document.addEventListener("touchmove", this.handleHandMove);
-        document.removeEventListener("touchend", this.handleHandUp);
-        document.addEventListener("touchend", this.handleHandUp);
-    }
+        document.removeEventListener('touchmove', this.handleHandMove);
+        document.addEventListener('touchmove', this.handleHandMove);
+        document.removeEventListener('touchend', this.handleHandUp);
+        document.addEventListener('touchend', this.handleHandUp);
+    };
 
     @action
     handleHandMove = (e: TouchEvent) => {
@@ -348,18 +422,20 @@ export class GestureOverlay extends Touchable {
                     const tPt = e.targetTouches.item(j);
                     if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) {
                         if (pt && this.prevPoints.has(pt.identifier)) {
-                            this._hands.forEach(hand => hand.some(f => {
-                                if (f.identifier === pt.identifier) {
-                                    fingers.push(pt);
-                                }
-                            }));
+                            this._hands.forEach(hand =>
+                                hand.some(f => {
+                                    if (f.identifier === pt.identifier) {
+                                        fingers.push(pt);
+                                    }
+                                })
+                            );
                         }
                     }
                 }
             }
         }
         // update hand trackers
-        const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
+        const thumb = fingers.reduce((a, v) => (a.clientY > v.clientY ? a : v), fingers[0]);
         if (thumb?.identifier && thumb?.identifier === this.thumbIdentifier) {
             this._hands.set(thumb.identifier, fingers);
         }
@@ -373,12 +449,11 @@ export class GestureOverlay extends Touchable {
                     // moving a thumb horiz. changes the palette collection selection, moving vert. changes the selection of any menus on the current palette item
                     const yOverX = Math.abs(pt.clientX - this._thumbX) < Math.abs(pt.clientY - this._thumbY);
                     if ((yOverX && this._inkToTextDoc) || this._selectedIndex > -1) {
-                        if (Math.abs(pt.clientY - this._thumbY) > (10 * window.devicePixelRatio)) {
-                            this._selectedIndex = Math.min(Math.max(-1, (-Math.ceil((pt.clientY - this._thumbY) / (10 * window.devicePixelRatio)) - 1)), this._possibilities.length - 1);
+                        if (Math.abs(pt.clientY - this._thumbY) > 10 * window.devicePixelRatio) {
+                            this._selectedIndex = Math.min(Math.max(-1, -Math.ceil((pt.clientY - this._thumbY) / (10 * window.devicePixelRatio)) - 1), this._possibilities.length - 1);
                         }
-                    }
-                    else if (this._thumbDoc) {
-                        if (Math.abs(pt.clientX - this._thumbX) > (15 * window.devicePixelRatio)) {
+                    } else if (this._thumbDoc) {
+                        if (Math.abs(pt.clientX - this._thumbX) > 15 * window.devicePixelRatio) {
                             this._thumbDoc.selectedIndex = Math.max(-1, NumCast(this._thumbDoc.selectedIndex) - Math.sign(pt.clientX - this._thumbX));
                             this._thumbX = pt.clientX;
                         }
@@ -390,7 +465,7 @@ export class GestureOverlay extends Touchable {
                 this._pointerY = pt.clientY;
             }
         }
-    }
+    };
 
     @action
     handleHandUp = (e: TouchEvent) => {
@@ -422,38 +497,37 @@ export class GestureOverlay extends Touchable {
             this._strokes = [];
             this._points = [];
             this._possibilities = [];
-            document.removeEventListener("touchend", this.handleHandUp);
+            document.removeEventListener('touchend', this.handleHandUp);
         }
-    }
+    };
 
     /**
      * Code for radial menu
      */
     onReactHoldTouchMove = (e: TouchEvent) => {
-        document.removeEventListener("touchmove", this.onReactTouchMove);
-        document.removeEventListener("touchend", this.onReactTouchEnd);
-        document.removeEventListener("touchmove", this.onReactHoldTouchMove);
-        document.removeEventListener("touchend", this.onReactHoldTouchEnd);
-        document.addEventListener("touchmove", this.onReactHoldTouchMove);
-        document.addEventListener("touchend", this.onReactHoldTouchEnd);
+        document.removeEventListener('touchmove', this.onReactTouchMove);
+        document.removeEventListener('touchend', this.onReactTouchEnd);
+        document.removeEventListener('touchmove', this.onReactHoldTouchMove);
+        document.removeEventListener('touchend', this.onReactHoldTouchEnd);
+        document.addEventListener('touchmove', this.onReactHoldTouchMove);
+        document.addEventListener('touchend', this.onReactHoldTouchEnd);
         const nts: any = this.getNewTouches(e);
         if (this.prevPoints.size === 1 && this._holdTimer) {
             clearTimeout(this._holdTimer);
         }
         document.dispatchEvent(
-            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchHoldMove",
-                {
-                    bubbles: true,
-                    detail: {
-                        fingers: this.prevPoints.size,
-                        targetTouches: nts.ntt,
-                        touches: nts.nt,
-                        changedTouches: nts.nct,
-                        touchEvent: e
-                    }
-                })
+            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchHoldMove', {
+                bubbles: true,
+                detail: {
+                    fingers: this.prevPoints.size,
+                    targetTouches: nts.ntt,
+                    touches: nts.nt,
+                    changedTouches: nts.nct,
+                    touchEvent: e,
+                },
+            })
         );
-    }
+    };
 
     /**
      * Code for radial menu
@@ -465,17 +539,16 @@ export class GestureOverlay extends Touchable {
             this._holdTimer = undefined;
         }
         document.dispatchEvent(
-            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchHoldEnd",
-                {
-                    bubbles: true,
-                    detail: {
-                        fingers: this.prevPoints.size,
-                        targetTouches: nts.ntt,
-                        touches: nts.nt,
-                        changedTouches: nts.nct,
-                        touchEvent: e
-                    }
-                })
+            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchHoldEnd', {
+                bubbles: true,
+                detail: {
+                    fingers: this.prevPoints.size,
+                    targetTouches: nts.ntt,
+                    touches: nts.nt,
+                    changedTouches: nts.nct,
+                    touchEvent: e,
+                },
+            })
         );
         for (let i = 0; i < e.changedTouches.length; i++) {
             const pt = e.changedTouches.item(i);
@@ -486,32 +559,38 @@ export class GestureOverlay extends Touchable {
             }
         }
 
-        document.removeEventListener("touchmove", this.onReactHoldTouchMove);
-        document.removeEventListener("touchend", this.onReactHoldTouchEnd);
+        document.removeEventListener('touchmove', this.onReactHoldTouchMove);
+        document.removeEventListener('touchend', this.onReactHoldTouchEnd);
 
         e.stopPropagation();
-    }
+    };
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
-                if (doubleTap) {
-                    InkTranscription.Instance.createInkGroup();
-                    CurrentUserUtils.ActiveTool = InkTool.None;
-                    return;
-                }
-            }));
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                returnFalse,
+                action((e: PointerEvent, doubleTap?: boolean) => {
+                    if (doubleTap) {
+                        InkTranscription.Instance.createInkGroup();
+                        Doc.ActiveTool = InkTool.None;
+                        return;
+                    }
+                })
+            );
         }
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                CurrentUserUtils.ActiveTool = InkTool.Write;
+                Doc.ActiveTool = InkTool.Write;
             }
             this._points.push({ X: e.clientX, Y: e.clientY });
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
-            // if (CurrentUserUtils.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+            // if (Doc.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
         }
-    }
+    };
 
     @action
     onPointerMove = (e: PointerEvent) => {
@@ -519,17 +598,18 @@ export class GestureOverlay extends Touchable {
 
         if (this._points.length > 1) {
             const B = this.svgBounds;
-            const initialPoint = this._points[0.];
+            const initialPoint = this._points[0];
             const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height;
             const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER);
             if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) {
                 switch (this.Tool) {
-                    case ToolglassTools.RadialMenu: return true;
+                    case ToolglassTools.RadialMenu:
+                        return true;
                 }
             }
         }
         return false;
-    }
+    };
 
     handleLineGesture = (): boolean => {
         const actionPerformed = false;
@@ -541,19 +621,18 @@ export class GestureOverlay extends Touchable {
         const target1 = document.elementFromPoint(ep1.X, ep1.Y);
         const target2 = document.elementFromPoint(ep2.X, ep2.Y);
 
-        const ge = new CustomEvent<GestureUtils.GestureEvent>("dashOnGesture",
-            {
-                bubbles: true,
-                detail: {
-                    points: this._points,
-                    gesture: GestureUtils.Gestures.Line,
-                    bounds: B
-                }
-            });
+        const ge = new CustomEvent<GestureUtils.GestureEvent>('dashOnGesture', {
+            bubbles: true,
+            detail: {
+                points: this._points,
+                gesture: GestureUtils.Gestures.Line,
+                bounds: B,
+            },
+        });
         target1?.dispatchEvent(ge);
         target2?.dispatchEvent(ge);
         return actionPerformed;
-    }
+    };
 
     @action
     onPointerUp = (e: PointerEvent) => {
@@ -563,9 +642,9 @@ export class GestureOverlay extends Touchable {
             //push first points to so interactionUtil knows pointer is up
             this._points.push({ X: this._points[0].X, Y: this._points[0].Y });
 
-            const initialPoint = this._points[0.];
-            const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + (this.height);
-            const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - (this.height) && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER);
+            const initialPoint = this._points[0];
+            const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height;
+            const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER);
 
             // if a toolglass is selected and the stroke starts within the toolglass boundaries
             if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) {
@@ -573,8 +652,8 @@ export class GestureOverlay extends Touchable {
                     case ToolglassTools.InkToText:
                         this._strokes.push(new Array(...this._points));
                         this._points = [];
-                        CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then((results) => {
-                            const wordResults = results.filter((r: any) => r.category === "line");
+                        CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then(results => {
+                            const wordResults = results.filter((r: any) => r.category === 'line');
                             const possibilities: string[] = [];
                             for (const wR of wordResults) {
                                 if (wR?.recognizedText) {
@@ -588,8 +667,7 @@ export class GestureOverlay extends Touchable {
 
                             // if we receive any word results from cognitive services, display them
                             runInAction(() => {
-                                this._possibilities = possibilities.map(p =>
-                                    <TouchScrollableMenuItem text={p} onClick={() => GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Text, [{ X: l, Y: t }], p)} />);
+                                this._possibilities = possibilities.map(p => <TouchScrollableMenuItem text={p} onClick={() => GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Text, [{ X: l, Y: t }], p)} />);
                             });
                         });
                         break;
@@ -605,47 +683,66 @@ export class GestureOverlay extends Touchable {
                 this.dispatchGesture(GestureUtils.Gestures.Stroke);
                 this._points = [];
                 if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
-                    this.InkShape = "";
-                    CurrentUserUtils.ActiveTool = InkTool.None;
+                    this.InkShape = '';
+                    Doc.ActiveTool = InkTool.None;
                 }
             }
             // if we're not drawing in a toolglass try to recognize as gesture
-            else { // need to decide when to turn gestures back on
+            else {
+                // need to decide when to turn gestures back on
                 const result = points.length > 2 && GestureUtils.GestureRecognizer.Recognize(new Array(points));
                 let actionPerformed = false;
                 if (Doc.UserDoc().recognizeGestures && result && result.Score > 0.7) {
                     switch (result.Name) {
-                        case GestureUtils.Gestures.Box: actionPerformed = this.dispatchGesture(GestureUtils.Gestures.Box); break;
-                        case GestureUtils.Gestures.StartBracket: actionPerformed = this.dispatchGesture(GestureUtils.Gestures.StartBracket); break;
-                        case GestureUtils.Gestures.EndBracket: actionPerformed = this.dispatchGesture("endbracket"); break;
-                        case GestureUtils.Gestures.Line: actionPerformed = this.handleLineGesture(); break;
-                        case GestureUtils.Gestures.Triangle: actionPerformed = this.makePolygon("triangle", true); break;
-                        case GestureUtils.Gestures.Circle: actionPerformed = this.makePolygon("circle", true); break;
-                        case GestureUtils.Gestures.Rectangle: actionPerformed = this.makePolygon("rectangle", true); break;
-                        case GestureUtils.Gestures.Scribble: console.log("scribble"); break;
+                        case GestureUtils.Gestures.Box:
+                            actionPerformed = this.dispatchGesture(GestureUtils.Gestures.Box);
+                            break;
+                        case GestureUtils.Gestures.StartBracket:
+                            actionPerformed = this.dispatchGesture(GestureUtils.Gestures.StartBracket);
+                            break;
+                        case GestureUtils.Gestures.EndBracket:
+                            actionPerformed = this.dispatchGesture('endbracket');
+                            break;
+                        case GestureUtils.Gestures.Line:
+                            actionPerformed = this.handleLineGesture();
+                            break;
+                        case GestureUtils.Gestures.Triangle:
+                            actionPerformed = this.makePolygon('triangle', true);
+                            break;
+                        case GestureUtils.Gestures.Circle:
+                            actionPerformed = this.makePolygon('circle', true);
+                            break;
+                        case GestureUtils.Gestures.Rectangle:
+                            actionPerformed = this.makePolygon('rectangle', true);
+                            break;
+                        case GestureUtils.Gestures.Scribble:
+                            console.log('scribble');
+                            break;
                     }
                 }
 
                 // if no gesture (or if the gesture was unsuccessful), "dry" the stroke into an ink document
                 if (!actionPerformed) {
-                    const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
+                    const newPoints = this._points.reduce((p, pts) => {
+                        p.push([pts.X, pts.Y]);
+                        return p;
+                    }, [] as number[][]);
                     newPoints.pop();
-                    const controlPoints: { X: number, Y: number }[] = [];
+                    const controlPoints: { X: number; Y: number }[] = [];
 
                     const bezierCurves = fitCurve(newPoints, 10);
                     for (const curve of bezierCurves) {
-
                         controlPoints.push({ X: curve[0][0], Y: curve[0][1] });
                         controlPoints.push({ X: curve[1][0], Y: curve[1][1] });
                         controlPoints.push({ X: curve[2][0], Y: curve[2][1] });
                         controlPoints.push({ X: curve[3][0], Y: curve[3][1] });
-
                     }
-                    const dist = Math.sqrt((controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) +
-                        (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y));
+                    const dist = Math.sqrt(
+                        (controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) + (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y)
+                    );
                     if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0];
                     this._points = controlPoints;
-                    this.dispatchGesture(GestureUtils.Gestures.Stroke);                    
+                    this.dispatchGesture(GestureUtils.Gestures.Stroke);
                     // TODO: nda - check inks to group here
                     checkInksToGroup();
                 }
@@ -655,7 +752,7 @@ export class GestureOverlay extends Touchable {
             this._points = [];
         }
         CollectionFreeFormViewChrome.Instance?.primCreated();
-    }
+    };
 
     makePolygon = (shape: string, gesture: boolean) => {
         //take off gesture recognition for now
@@ -673,11 +770,15 @@ export class GestureOverlay extends Touchable {
         var lastx = this._points[this._points.length - 2].X;
         var lasty = this._points[this._points.length - 2].Y;
         var fourth = (lastx - firstx) / 4;
-        if (isNaN(fourth) || fourth === 0) { fourth = 0.01; }
+        if (isNaN(fourth) || fourth === 0) {
+            fourth = 0.01;
+        }
         var m = (lasty - firsty) / (lastx - firstx);
-        if (isNaN(m) || m === 0) { m = 0.01; }
+        if (isNaN(m) || m === 0) {
+            m = 0.01;
+        }
         const b = firsty - m * firstx;
-        if (shape === "noRec") {
+        if (shape === 'noRec') {
             return false;
         }
         if (!gesture) {
@@ -687,7 +788,7 @@ export class GestureOverlay extends Touchable {
             left = this._points[0].X;
             bottom = this._points[this._points.length - 2].Y;
             top = this._points[0].Y;
-            if (shape !== "arrow" && shape !== "line" && shape !== "circle") {
+            if (shape !== 'arrow' && shape !== 'line' && shape !== 'circle') {
                 if (left > right) {
                     const temp = right;
                     right = left;
@@ -704,7 +805,7 @@ export class GestureOverlay extends Touchable {
         switch (shape) {
             //must push an extra point in the end so InteractionUtils knows pointer is up.
             //must be (points[0].X,points[0]-1)
-            case "rectangle":
+            case 'rectangle':
                 this._points.push({ X: left, Y: top });
                 this._points.push({ X: left, Y: top });
                 this._points.push({ X: right, Y: top });
@@ -727,7 +828,7 @@ export class GestureOverlay extends Touchable {
 
                 break;
 
-            case "triangle":
+            case 'triangle':
                 this._points.push({ X: left, Y: bottom });
                 this._points.push({ X: left, Y: bottom });
 
@@ -744,9 +845,8 @@ export class GestureOverlay extends Touchable {
                 this._points.push({ X: left, Y: bottom });
                 this._points.push({ X: left, Y: bottom });
 
-
                 break;
-            case "circle":
+            case 'circle':
                 // Approximation of a circle using 4 Bézier curves in which the constant "c" reduces the maximum radial drift to 0.019608%,
                 // making the curves indistinguishable from a circle.
                 // Source: https://spencermortensen.com/articles/bezier-circle/
@@ -755,30 +855,30 @@ export class GestureOverlay extends Touchable {
                 const centerY = (Math.max(top, bottom) + Math.min(top, bottom)) / 2;
                 const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom));
 
-                // Dividing the circle into four equal sections, and fitting each section to a cubic Bézier curve.   
+                // Dividing the circle into four equal sections, and fitting each section to a cubic Bézier curve.
                 this._points.push({ X: centerX, Y: centerY + radius });
-                this._points.push({ X: centerX + (c * radius), Y: centerY + radius });
-                this._points.push({ X: centerX + radius, Y: centerY + (c * radius) });
+                this._points.push({ X: centerX + c * radius, Y: centerY + radius });
+                this._points.push({ X: centerX + radius, Y: centerY + c * radius });
                 this._points.push({ X: centerX + radius, Y: centerY });
 
                 this._points.push({ X: centerX + radius, Y: centerY });
-                this._points.push({ X: centerX + radius, Y: centerY - (c * radius) });
-                this._points.push({ X: centerX + (c * radius), Y: centerY - radius });
+                this._points.push({ X: centerX + radius, Y: centerY - c * radius });
+                this._points.push({ X: centerX + c * radius, Y: centerY - radius });
                 this._points.push({ X: centerX, Y: centerY - radius });
 
                 this._points.push({ X: centerX, Y: centerY - radius });
-                this._points.push({ X: centerX - (c * radius), Y: centerY - radius });
-                this._points.push({ X: centerX - radius, Y: centerY - (c * radius) });
+                this._points.push({ X: centerX - c * radius, Y: centerY - radius });
+                this._points.push({ X: centerX - radius, Y: centerY - c * radius });
                 this._points.push({ X: centerX - radius, Y: centerY });
 
                 this._points.push({ X: centerX - radius, Y: centerY });
-                this._points.push({ X: centerX - radius, Y: centerY + (c * radius) });
-                this._points.push({ X: centerX - (c * radius), Y: centerY + radius });
+                this._points.push({ X: centerX - radius, Y: centerY + c * radius });
+                this._points.push({ X: centerX - c * radius, Y: centerY + radius });
                 this._points.push({ X: centerX, Y: centerY + radius });
 
                 break;
 
-            case "line":
+            case 'line':
                 if (Math.abs(firstx - lastx) < 10 && Math.abs(firsty - lasty) > 10) {
                     lastx = firstx;
                 }
@@ -791,12 +891,12 @@ export class GestureOverlay extends Touchable {
                 this._points.push({ X: lastx, Y: lasty });
                 this._points.push({ X: lastx, Y: lasty });
                 break;
-            case "arrow":
+            case 'arrow':
                 const x1 = left;
                 const y1 = top;
                 const x2 = right;
                 const y2 = bottom;
-                const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + (Math.pow(Math.abs(y1 - y2), 2)));
+                const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2));
                 const L2 = L1 / 5;
                 const angle = 0.785398;
                 const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle));
@@ -811,24 +911,24 @@ export class GestureOverlay extends Touchable {
             // this._points.push({ X: x1, Y: y1 - 1 });
         }
         return true;
-    }
+    };
 
-    dispatchGesture = (gesture: "box" | "line" | "startbracket" | "endbracket" | "stroke" | "scribble" | "text", stroke?: InkData, data?: any) => {
+    dispatchGesture = (gesture: 'box' | 'line' | 'startbracket' | 'endbracket' | 'stroke' | 'scribble' | 'text', stroke?: InkData, data?: any) => {
         const target = document.elementFromPoint((stroke ?? this._points)[0].X, (stroke ?? this._points)[0].Y);
-        return target?.dispatchEvent(
-            new CustomEvent<GestureUtils.GestureEvent>("dashOnGesture",
-                {
+        return (
+            target?.dispatchEvent(
+                new CustomEvent<GestureUtils.GestureEvent>('dashOnGesture', {
                     bubbles: true,
                     detail: {
                         points: stroke ?? this._points,
                         gesture: gesture as any,
                         bounds: this.getBounds(stroke ?? this._points),
                         text: data,
-                    }
-                }
-            )
-        ) || false;
-    }
+                    },
+                })
+            ) || false
+        );
+    };
 
     getBounds = (stroke: InkData, pad?: boolean) => {
         const padding = pad ? [-20000, 20000] : [];
@@ -839,7 +939,7 @@ export class GestureOverlay extends Touchable {
         const bottom = Math.max(...ys);
         const top = Math.min(...ys);
         return { right, left, bottom, top, width: right - left, height: bottom - top };
-    }
+    };
 
     @computed get svgBounds() {
         return this.getBounds(this._points);
@@ -847,7 +947,7 @@ export class GestureOverlay extends Touchable {
 
     @computed get elements() {
         const selView = SelectionManager.Views().lastElement();
-        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1) / (selView?.props.ScreenToLocalTransform().Scale || 1);
+        const width = (Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1)) / (selView?.props.ScreenToLocalTransform().Scale || 1);
         const rect = this._overlayRef.current?.getBoundingClientRect();
         const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true);
         B.left = B.left - width / 2;
@@ -857,30 +957,74 @@ export class GestureOverlay extends Touchable {
         B.width += width;
         B.height += width;
         const fillColor = ActiveFillColor();
-        const strokeColor = fillColor && fillColor !== "transparent" ? fillColor : ActiveInkColor();
+        const strokeColor = fillColor && fillColor !== 'transparent' ? fillColor : ActiveInkColor();
         return [
             this.props.children,
             this._palette,
-            [this._strokes.map((l, i) => {
-                const b = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 };//this.getBounds(l, true);
-                return <svg key={i} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
-                    {InteractionUtils.CreatePolyline(l, b.left, b.top, strokeColor, width, width, "miter", "round",
-                        ActiveInkBezierApprox(), "none" /*ActiveFillColor()*/, ActiveArrowStart(), ActiveArrowEnd(), ActiveArrowScale(),
-                        ActiveDash(), 1, 1, this.InkShape, "none", 1.0, false)}
-                </svg>;
-            }),
-            this._points.length <= 1 ? (null) : <svg key="svg" width={B.width} height={B.height}
-                style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
-                {InteractionUtils.CreatePolyline(this._points.map(p => ({ X: p.X, Y: p.Y - (rect?.y || 0) })), B.left, B.top, ActiveInkColor(), width, width, "miter", "round", "",
-                    "none" /*ActiveFillColor()*/, ActiveArrowStart(), ActiveArrowEnd(), ActiveArrowScale(), ActiveDash(), 1, 1, this.InkShape, "none", 1.0, false)}
-            </svg>]
+            [
+                this._strokes.map((l, i) => {
+                    const b = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(l, true);
+                    return (
+                        <svg key={i} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
+                            {InteractionUtils.CreatePolyline(
+                                l,
+                                b.left,
+                                b.top,
+                                strokeColor,
+                                width,
+                                width,
+                                'miter',
+                                'round',
+                                ActiveInkBezierApprox(),
+                                'none' /*ActiveFillColor()*/,
+                                ActiveArrowStart(),
+                                ActiveArrowEnd(),
+                                ActiveArrowScale(),
+                                ActiveDash(),
+                                1,
+                                1,
+                                this.InkShape,
+                                'none',
+                                1.0,
+                                false
+                            )}
+                        </svg>
+                    );
+                }),
+                this._points.length <= 1 ? null : (
+                    <svg key="svg" width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
+                        {InteractionUtils.CreatePolyline(
+                            this._points.map(p => ({ X: p.X, Y: p.Y - (rect?.y || 0) })),
+                            B.left,
+                            B.top,
+                            ActiveInkColor(),
+                            width,
+                            width,
+                            'miter',
+                            'round',
+                            '',
+                            'none' /*ActiveFillColor()*/,
+                            ActiveArrowStart(),
+                            ActiveArrowEnd(),
+                            ActiveArrowScale(),
+                            ActiveDash(),
+                            1,
+                            1,
+                            this.InkShape,
+                            'none',
+                            1.0,
+                            false
+                        )}
+                    </svg>
+                ),
+            ],
         ];
     }
     screenToLocalTransform = () => new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + this.height, 1);
     return300 = () => 300;
     @action
     public openFloatingDoc = (doc: Doc) => {
-        this._clipboardDoc =
+        this._clipboardDoc = (
             <DocumentView
                 Document={doc}
                 DataDoc={undefined}
@@ -905,61 +1049,65 @@ export class GestureOverlay extends Touchable {
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
-            />;
-    }
+            />
+        );
+    };
 
     @action
     public closeFloatingDoc = () => {
         this._clipboardDoc = undefined;
-    }
+    };
 
     @action
     enableMobileInkOverlay = (content: MobileInkOverlayContent) => {
         this.showMobileInkOverlay = content.enableOverlay;
-    }
+    };
 
     render() {
         return (
-            <div className="gestureOverlay-cont" ref={this._overlayRef}
-                onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}>
+            <div className="gestureOverlay-cont" ref={this._overlayRef} onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}>
                 {this.showMobileInkOverlay ? <MobileInkOverlay /> : <></>}
                 {this.elements}
 
-                <div className="clipboardDoc-cont" style={{
-                    height: this.height,
-                    width: this.height,
-                    pointerEvents: this._clipboardDoc ? "unset" : "none",
-                    touchAction: this._clipboardDoc ? "unset" : "none",
-                    transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height} px)`,
-                }}>
+                <div
+                    className="clipboardDoc-cont"
+                    style={{
+                        height: this.height,
+                        width: this.height,
+                        pointerEvents: this._clipboardDoc ? 'unset' : 'none',
+                        touchAction: this._clipboardDoc ? 'unset' : 'none',
+                        transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height} px)`,
+                    }}>
                     {this._clipboardDoc}
                 </div>
-                <div className="filter-cont" style={{
-                    transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height}px)`,
-                    height: this.height,
-                    width: this.height,
-                    pointerEvents: "none",
-                    touchAction: "none",
-                    display: this.showBounds ? "unset" : "none",
-                }}>
-                </div>
+                <div
+                    className="filter-cont"
+                    style={{
+                        transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height}px)`,
+                        height: this.height,
+                        width: this.height,
+                        pointerEvents: 'none',
+                        touchAction: 'none',
+                        display: this.showBounds ? 'unset' : 'none',
+                    }}></div>
                 <TouchScrollableMenu options={this._possibilities} bounds={this.svgBounds} selectedIndex={this._selectedIndex} x={this._menuX} y={this._menuY} />
-            </div>);
+            </div>
+        );
     }
 }
 
 // export class
 
 export enum ToolglassTools {
-    InkToText = "inktotext",
-    IgnoreGesture = "ignoregesture",
-    RadialMenu = "radialmenu",
-    None = "none",
+    InkToText = 'inktotext',
+    IgnoreGesture = 'ignoregesture',
+    RadialMenu = 'radialmenu',
+    None = 'none',
 }
 
-ScriptingGlobals.add("GestureOverlay", GestureOverlay);
+ScriptingGlobals.add('GestureOverlay', GestureOverlay);
 ScriptingGlobals.add(function setToolglass(tool: any) {
-    runInAction(() => GestureOverlay.Instance.Tool = tool);
+    runInAction(() => (GestureOverlay.Instance.Tool = tool));
 });
 ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
     runInAction(() => {
@@ -975,10 +1123,14 @@ ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowSta
 });
 ScriptingGlobals.add(function resetPen() {
     runInAction(() => {
-        SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)");
-        SetActiveInkWidth(GestureOverlay.Instance.SavedWidth?.toString() ?? "2");
+        SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? 'rgb(0, 0, 0)');
+        SetActiveInkWidth(GestureOverlay.Instance.SavedWidth?.toString() ?? '2');
     });
-}, "resets the pen tool");
-ScriptingGlobals.add(function createText(text: any, x: any, y: any) {
-    GestureOverlay.Instance.dispatchGesture("text", [{ X: x, Y: y }], text);
-}, "creates a text document with inputted text and coordinates", "(text: any, x: any, y: any)");
+}, 'resets the pen tool');
+ScriptingGlobals.add(
+    function createText(text: any, x: any, y: any) {
+        GestureOverlay.Instance.dispatchGesture('text', [{ X: x, Y: y }], text);
+    },
+    'creates a text document with inputted text and coordinates',
+    '(text: any, x: any, y: any)'
+);
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index f5122df3f..73e0c9933 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -1,39 +1,38 @@
-import { random } from "lodash";
-import { action, observable, runInAction } from "mobx";
-import { DateField } from "../../fields/DateField";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { InkTool } from "../../fields/InkField";
-import { List } from "../../fields/List";
-import { ScriptField } from "../../fields/ScriptField";
-import { Cast, PromiseValue } from "../../fields/Types";
-import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager";
-import { DocServer } from "../DocServer";
-import { DocumentType } from "../documents/DocumentTypes";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DragManager } from "../util/DragManager";
-import { GroupManager } from "../util/GroupManager";
-import { SelectionManager } from "../util/SelectionManager";
-import { SettingsManager } from "../util/SettingsManager";
-import { SharingManager } from "../util/SharingManager";
-import { SnappingManager } from "../util/SnappingManager";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-import { CollectionDockingView } from "./collections/CollectionDockingView";
-import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
-import { CollectionStackedTimeline } from "./collections/CollectionStackedTimeline";
-import { ContextMenu } from "./ContextMenu";
-import { DocumentDecorations } from "./DocumentDecorations";
-import { InkStrokeProperties } from "./InkStrokeProperties";
-import { LightboxView } from "./LightboxView";
-import { MainView } from "./MainView";
-import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
-import { AnchorMenu } from "./pdf/AnchorMenu";
+import { random } from 'lodash';
+import { action, observable, runInAction } from 'mobx';
+import { DateField } from '../../fields/DateField';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { InkTool } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, PromiseValue } from '../../fields/Types';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
+import { DocServer } from '../DocServer';
+import { DocumentType } from '../documents/DocumentTypes';
+import { DragManager } from '../util/DragManager';
+import { GroupManager } from '../util/GroupManager';
+import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
+import { SharingManager } from '../util/SharingManager';
+import { SnappingManager } from '../util/SnappingManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionFreeFormViewChrome } from './collections/CollectionMenu';
+import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
+import { ContextMenu } from './ContextMenu';
+import { DocumentDecorations } from './DocumentDecorations';
+import { InkStrokeProperties } from './InkStrokeProperties';
+import { LightboxView } from './LightboxView';
+import { MainView } from './MainView';
+import { DocumentLinksButton } from './nodes/DocumentLinksButton';
+import { AnchorMenu } from './pdf/AnchorMenu';
 
-const modifiers = ["control", "meta", "shift", "alt"];
+const modifiers = ['control', 'meta', 'shift', 'alt'];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo;
 type KeyControlInfo = {
-    preventDefault: boolean,
-    stopPropagation: boolean
+    preventDefault: boolean;
+    stopPropagation: boolean;
 };
 
 export class KeyManager {
@@ -41,22 +40,22 @@ export class KeyManager {
     private router = new Map<string, KeyHandler>();
 
     constructor() {
-        const isMac = navigator.platform.toLowerCase().indexOf("mac") >= 0;
+        const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0;
 
         // SHIFT CONTROL ALT META
-        this.router.set("0000", this.unmodified);
-        this.router.set(isMac ? "0001" : "0100", this.ctrl);
-        this.router.set(isMac ? "0100" : "0010", this.alt);
-        this.router.set(isMac ? "1001" : "1100", this.ctrl_shift);
-        this.router.set("1000", this.shift);
+        this.router.set('0000', this.unmodified);
+        this.router.set(isMac ? '0001' : '0100', this.ctrl);
+        this.router.set(isMac ? '0100' : '0010', this.alt);
+        this.router.set(isMac ? '1001' : '1100', this.ctrl_shift);
+        this.router.set('1000', this.shift);
     }
 
     public unhandle = action((e: KeyboardEvent) => {
-        if (e.key?.toLowerCase() === "shift") runInAction(() => DocumentDecorations.Instance.AddToSelection = false);
+        if (e.key?.toLowerCase() === 'shift') runInAction(() => (DocumentDecorations.Instance.AddToSelection = false));
     });
 
     public handle = action((e: KeyboardEvent) => {
-        if (e.key?.toLowerCase() === "shift") DocumentDecorations.Instance.AddToSelection = true;
+        if (e.key?.toLowerCase() === 'shift') DocumentDecorations.Instance.AddToSelection = true;
         //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
         const keyname = e.key && e.key.toLowerCase();
         this.handleGreedy(keyname);
@@ -65,7 +64,7 @@ export class KeyManager {
             return;
         }
 
-        const bit = (value: boolean) => value ? "1" : "0";
+        const bit = (value: boolean) => (value ? '1' : '0');
         const modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey);
 
         const handleConstrained = this.router.get(modifierIndex);
@@ -86,33 +85,33 @@ export class KeyManager {
 
     private unmodified = action((keyname: string, e: KeyboardEvent) => {
         switch (keyname) {
-            case "u":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'u':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
 
                 const ungroupings = SelectionManager.Views().slice();
-                UndoManager.RunInBatch(() => ungroupings.map(dv => dv.layoutDoc.group = undefined), "ungroup");
+                UndoManager.RunInBatch(() => ungroupings.map(dv => (dv.layoutDoc.group = undefined)), 'ungroup');
                 SelectionManager.DeselectAll();
                 break;
-            case "g":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'g':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
 
                 const groupings = SelectionManager.Views().slice();
                 const randomGroup = random(0, 1000);
-                UndoManager.RunInBatch(() => groupings.map(dv => dv.layoutDoc.group = randomGroup), "group");
+                UndoManager.RunInBatch(() => groupings.map(dv => (dv.layoutDoc.group = randomGroup)), 'group');
                 SelectionManager.DeselectAll();
                 break;
-            case " ":
+            case ' ':
                 // MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI
                 break;
-            case "escape":
+            case 'escape':
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
                 InkStrokeProperties.Instance._controlButton = false;
-                CurrentUserUtils.ActiveTool = InkTool.None;
+                Doc.ActiveTool = InkTool.None;
                 DragManager.CompleteWindowDrag?.(true);
                 var doDeselect = true;
                 if (SnappingManager.GetIsDragging()) {
@@ -138,20 +137,19 @@ export class KeyManager {
                 window.getSelection()?.empty();
                 document.body.focus();
                 break;
-            case "enter": {
+            case 'enter': {
                 DocumentDecorations.Instance.onCloseClick(false);
                 break;
             }
-            case "delete":
-            case "backspace":
-                if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") {
+            case 'delete':
+            case 'backspace':
+                if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
                     UndoManager.RunInBatch(() => {
                         if (LightboxView.LightboxDoc) {
                             LightboxView.SetLightboxDoc(undefined);
                             SelectionManager.DeselectAll();
-                        }
-                        else DocumentDecorations.Instance.onCloseClick(true);
-                    }, "backspace");
+                        } else DocumentDecorations.Instance.onCloseClick(true);
+                    }, 'backspace');
                     // const selected = SelectionManager.Views().filter(dv => !dv.topMost);
                     // UndoManager.RunInBatch(() => {
                     //     SelectionManager.DeselectAll();
@@ -160,15 +158,23 @@ export class KeyManager {
                     return { stopPropagation: true, preventDefault: true };
                 }
                 break;
-            case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge(-1, 0)), "nudge left"); break;
-            case "arrowright": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(1, 0)), "nudge right"); break;
-            case "arrowup": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -1)), "nudge up"); break;
-            case "arrowdown": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 1)), "nudge down"); break;
+            case 'arrowleft':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge(-1, 0)), 'nudge left');
+                break;
+            case 'arrowright':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(1, 0)), 'nudge right');
+                break;
+            case 'arrowup':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -1)), 'nudge up');
+                break;
+            case 'arrowdown':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 1)), 'nudge down');
+                break;
         }
 
         return {
             stopPropagation: false,
-            preventDefault: false
+            preventDefault: false,
         };
     });
 
@@ -177,15 +183,23 @@ export class KeyManager {
         const preventDefault = false;
 
         switch (keyname) {
-            case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(-10, 0)), "nudge left"); break;
-            case "arrowright": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(10, 0)), "nudge right"); break;
-            case "arrowup": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -10)), "nudge up"); break;
-            case "arrowdown": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 10)), "nudge down"); break;
+            case 'arrowleft':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(-10, 0)), 'nudge left');
+                break;
+            case 'arrowright':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(10, 0)), 'nudge right');
+                break;
+            case 'arrowup':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -10)), 'nudge up');
+                break;
+            case 'arrowdown':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 10)), 'nudge down');
+                break;
         }
 
         return {
             stopPropagation: stopPropagation,
-            preventDefault: preventDefault
+            preventDefault: preventDefault,
         };
     });
 
@@ -194,15 +208,15 @@ export class KeyManager {
         const preventDefault = true;
 
         switch (keyname) {
-            case "ƒ":
-            case "f":
+            case 'ƒ':
+            case 'f':
                 const dv = SelectionManager.Views()?.[0];
-                UndoManager.RunInBatch(() => dv.props.CollectionFreeFormDocumentView?.().float(), "float");
+                UndoManager.RunInBatch(() => dv.props.CollectionFreeFormDocumentView?.().float(), 'float');
         }
 
         return {
             stopPropagation: stopPropagation,
-            preventDefault: preventDefault
+            preventDefault: preventDefault,
         };
     });
 
@@ -211,83 +225,97 @@ export class KeyManager {
         let preventDefault = true;
 
         switch (keyname) {
-            case "arrowright":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'arrowright':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
-                MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, "right");
+                MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, 'right');
                 break;
-            case "arrowleft":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'arrowleft':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
                 MainView.Instance.mainFreeform && CollectionDockingView.CloseSplit(MainView.Instance.mainFreeform);
                 break;
-            case "backspace":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'backspace':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
                 break;
-            case "t":
-                PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+            case 't':
+                PromiseValue(Cast(Doc.UserDoc()['tabs-button-tools'], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 break;
-            case "f":
+            case 'f':
                 if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) {
-                    SelectionManager.Views()[0].ComponentView?.search?.("", false, false);
+                    SelectionManager.Views()[0].ComponentView?.search?.('', false, false);
                 } else {
-                    const searchBtn = CurrentUserUtils.MySearcher;
+                    const searchBtn = Doc.MySearcher;
                     if (searchBtn) {
                         MainView.Instance.selectMenu(searchBtn);
                     }
                 }
                 break;
-            case "e": CurrentUserUtils.ActiveTool = InkTool.Eraser;
+            case 'e':
+                Doc.ActiveTool = InkTool.Eraser;
                 break;
-            case "p": CurrentUserUtils.ActiveTool = InkTool.Pen;
+            case 'p':
+                Doc.ActiveTool = InkTool.Pen;
                 break;
-            case "o":
+            case 'o':
                 const target = SelectionManager.Docs().lastElement();
                 target && CollectionDockingView.OpenFullScreen(target);
                 break;
-            case "r":
+            case 'r':
                 preventDefault = false;
                 break;
-            case "y":
+            case 'y':
                 SelectionManager.DeselectAll();
                 UndoManager.Redo();
                 stopPropagation = false;
                 break;
-            case "z":
+            case 'z':
                 SelectionManager.DeselectAll();
                 UndoManager.Undo();
                 stopPropagation = false;
                 break;
-            case "a":
+            case 'a':
                 if (e.target !== document.body) {
                     stopPropagation = false;
                     preventDefault = false;
                 }
                 break;
-            case "v":
+            case 'v':
                 stopPropagation = false;
                 preventDefault = false;
                 break;
-            case "x":
+            case 'x':
                 if (SelectionManager.Views().length) {
                     const bds = DocumentDecorations.Instance.Bounds;
-                    const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
-                    const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":");
+                    const pt = SelectionManager.Views()[0]
+                        .props.ScreenToLocalTransform()
+                        .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
+                    const text =
+                        `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` +
+                        SelectionManager.Views()
+                            .map(dv => dv.Document[Id])
+                            .join(':');
                     SelectionManager.Views().length && navigator.clipboard.writeText(text);
                     DocumentDecorations.Instance.onCloseClick(true);
                     stopPropagation = false;
                     preventDefault = false;
                 }
                 break;
-            case "c":
+            case 'c':
                 if (!AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) {
                     const bds = DocumentDecorations.Instance.Bounds;
-                    const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
-                    const text = `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":");
+                    const pt = SelectionManager.Views()[0]
+                        .props.ScreenToLocalTransform()
+                        .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
+                    const text =
+                        `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` +
+                        SelectionManager.Views()
+                            .map(dv => dv.Document[Id])
+                            .join(':');
                     SelectionManager.Views().length && navigator.clipboard.writeText(text);
                     stopPropagation = false;
                 }
@@ -297,58 +325,63 @@ export class KeyManager {
 
         return {
             stopPropagation: stopPropagation,
-            preventDefault: preventDefault
+            preventDefault: preventDefault,
         };
     });
 
     public paste(e: ClipboardEvent) {
-        const plain = e.clipboardData?.getData("text/plain");
-        const clone = plain?.startsWith("__DashCloneId(");
-        if (plain && (plain.startsWith("__DashDocId(") || clone)) {
+        const plain = e.clipboardData?.getData('text/plain');
+        const clone = plain?.startsWith('__DashCloneId(');
+        if (plain && (plain.startsWith('__DashDocId(') || clone)) {
             const first = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
             if (first?.props.Document.type === DocumentType.COL) {
-                const docids = plain.split(":");
+                const docids = plain.split(':');
                 let count = 1;
                 const list: Doc[] = [];
                 const targetDataDoc = Doc.GetProto(first.props.Document);
                 const fieldKey = first.LayoutFieldKey;
                 const docList = DocListCast(targetDataDoc[fieldKey]);
-                docids.map((did, i) => i && DocServer.GetRefField(did).then(async doc => {
-                    count++;
-                    if (doc instanceof Doc) {
-                        list.push(doc);
-                    }
-                    if (count === docids.length) {
-                        const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => clone ? (await Doc.MakeClone(d)).clone : d));
-                        if (added.length) {
-                            added.map(doc => doc.context = targetDataDoc);
-                            undoBatch(() => {
-                                targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
-                                targetDataDoc[fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
-                            })();
-                        }
-                    }
-                }));
+                docids.map(
+                    (did, i) =>
+                        i &&
+                        DocServer.GetRefField(did).then(async doc => {
+                            count++;
+                            if (doc instanceof Doc) {
+                                list.push(doc);
+                            }
+                            if (count === docids.length) {
+                                const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d)).clone : d)));
+                                if (added.length) {
+                                    added.map(doc => (doc.context = targetDataDoc));
+                                    undoBatch(() => {
+                                        targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
+                                        targetDataDoc[fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
+                                    })();
+                                }
+                            }
+                        })
+                );
             }
         }
     }
 
-    getClipboard() { return navigator.clipboard.readText(); }
+    getClipboard() {
+        return navigator.clipboard.readText();
+    }
 
     private ctrl_shift = action((keyname: string) => {
         const stopPropagation = true;
         const preventDefault = true;
 
         switch (keyname) {
-            case "z":
+            case 'z':
                 UndoManager.Redo();
                 break;
         }
 
         return {
             stopPropagation: stopPropagation,
-            preventDefault: preventDefault
+            preventDefault: preventDefault,
         };
     });
-
 }
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 471ad09e9..821e2f739 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -1,23 +1,23 @@
-import { Bezier } from "bezier-js";
-import { Normalize, Distance } from "../util/bezierFit";
-import { action, observable, reaction } from "mobx";
-import { Doc, NumListCast, Opt } from "../../fields/Doc";
-import { InkData, InkField, InkTool, PointData } from "../../fields/InkField";
-import { List } from "../../fields/List";
-import { listSpec } from "../../fields/Schema";
-import { Cast, NumCast } from "../../fields/Types";
-import { Point } from "../../pen-gestures/ndollar";
-import { DocumentType } from "../documents/DocumentTypes";
-import { FitOneCurve } from "../util/bezierFit";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DocumentManager } from "../util/DocumentManager";
-import { undoBatch } from "../util/UndoManager";
-import { InkingStroke } from "./InkingStroke";
-import { DocumentView } from "./nodes/DocumentView";
+import { Bezier } from 'bezier-js';
+import { action, observable, reaction } from 'mobx';
+import { Doc, NumListCast, Opt } from '../../fields/Doc';
+import { InkData, InkField, InkTool, PointData } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { listSpec } from '../../fields/Schema';
+import { Cast, NumCast } from '../../fields/Types';
+import { Point } from '../../pen-gestures/ndollar';
+import { DocumentType } from '../documents/DocumentTypes';
+import { FitOneCurve } from '../util/bezierFit';
+import { DocumentManager } from '../util/DocumentManager';
+import { undoBatch } from '../util/UndoManager';
+import { InkingStroke } from './InkingStroke';
+import { DocumentView } from './nodes/DocumentView';
 
 export class InkStrokeProperties {
     static _Instance: InkStrokeProperties | undefined;
-    public static get Instance() { return this._Instance || new InkStrokeProperties(); }
+    public static get Instance() {
+        return this._Instance || new InkStrokeProperties();
+    }
 
     @observable _lock = false;
     @observable _controlButton = false;
@@ -25,8 +25,14 @@ export class InkStrokeProperties {
 
     constructor() {
         InkStrokeProperties._Instance = this;
-        reaction(() => this._controlButton, button => button && (CurrentUserUtils.ActiveTool = InkTool.None));
-        reaction(() => CurrentUserUtils.ActiveTool, tool => (tool !== InkTool.None) && (this._controlButton = false));
+        reaction(
+            () => this._controlButton,
+            button => button && (Doc.ActiveTool = InkTool.None)
+        );
+        reaction(
+            () => Doc.ActiveTool,
+            tool => tool !== InkTool.None && (this._controlButton = false)
+        );
     }
 
     /**
@@ -34,35 +40,41 @@ export class InkStrokeProperties {
      * @param func The inputted function.
      * @param requireCurrPoint Indicates whether the current selected point is needed.
      */
-    applyFunction = (strokes: Opt<DocumentView | DocumentView[]>, func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => {
+    applyFunction = (
+        strokes: Opt<DocumentView | DocumentView[]>,
+        func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number; Y: number }[] | undefined,
+        requireCurrPoint: boolean = false
+    ) => {
         var appliedFunc = false;
-        (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach(action(inkView => {
-            if (!requireCurrPoint || this._currentPoint !== -1) {
-                const doc = inkView.rootDoc;
-                if (doc.type === DocumentType.INK && doc.width && doc.height) {
-                    const ink = Cast(doc.data, InkField)?.inkData;
-                    if (ink) {
-                        const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X));
-                        const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y));
-                        const ptsXscale = ((NumCast(doc._width) - NumCast(doc.strokeWidth)) / ((oldXrange.max - oldXrange.min) || 1)) || 1;
-                        const ptsYscale = ((NumCast(doc._height) - NumCast(doc.strokeWidth)) / ((oldYrange.max - oldYrange.min) || 1)) || 1;
-                        const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth));
-                        if (newPoints) {
-                            const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X));
-                            const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y));
-                            doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.strokeWidth);
-                            doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth);
-                            doc.x = (oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale);
-                            doc.y = (oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale);
-                            Doc.GetProto(doc).data = new InkField(newPoints);
-                            appliedFunc = true;
+        (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach(
+            action(inkView => {
+                if (!requireCurrPoint || this._currentPoint !== -1) {
+                    const doc = inkView.rootDoc;
+                    if (doc.type === DocumentType.INK && doc.width && doc.height) {
+                        const ink = Cast(doc.data, InkField)?.inkData;
+                        if (ink) {
+                            const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X));
+                            const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y));
+                            const ptsXscale = (NumCast(doc._width) - NumCast(doc.strokeWidth)) / (oldXrange.max - oldXrange.min || 1) || 1;
+                            const ptsYscale = (NumCast(doc._height) - NumCast(doc.strokeWidth)) / (oldYrange.max - oldYrange.min || 1) || 1;
+                            const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth));
+                            if (newPoints) {
+                                const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X));
+                                const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y));
+                                doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.strokeWidth);
+                                doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth);
+                                doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale;
+                                doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale;
+                                Doc.GetProto(doc).data = new InkField(newPoints);
+                                appliedFunc = true;
+                            }
                         }
                     }
                 }
-            }
-        }));
+            })
+        );
         return appliedFunc;
-    }
+    };
 
     /**
      * Adds a new control point to the ink instance when editing its format.
@@ -72,7 +84,7 @@ export class InkStrokeProperties {
      */
     @undoBatch
     @action
-    addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number, Y: number }[]) => {
+    addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number; Y: number }[]) => {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const doc = view.rootDoc;
             const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]];
@@ -81,12 +93,12 @@ export class InkStrokeProperties {
             controls.splice(i, 4, ...splicepts.map(p => ({ X: p.x, Y: p.y })));
 
             // Updating the indices of the control points whose handle tangency has been broken.
-            doc.brokenInkIndices = new List(Cast(doc.brokenInkIndices, listSpec("number"), []).map(control => control > i ? control + 4 : control));
+            doc.brokenInkIndices = new List(Cast(doc.brokenInkIndices, listSpec('number'), []).map(control => (control > i ? control + 4 : control)));
             this._currentPoint = -1;
 
             return controls;
         });
-    }
+    };
 
     /**
      * Scales a handle point of a control point that is adjacent to a newly added one.
@@ -107,15 +119,15 @@ export class InkStrokeProperties {
      * the tangent vector to a control point is equivalent to the first/last (depending on the direction
      * of the curve) leg of the Bézier curve's derivative.
      * (Source: https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html)
-     * 
+     *
      * @param C The curve represented by all points from the previous control until the newly added point.
      * @param D The curve represented by all points from the newly added point to the next control.
      * @param newControl The newly added control point.
      */
     getNewHandlePoints = (C: PointData[], D: PointData[], newControl: PointData) => {
         const [m, n] = [C.length, D.length];
-        let handleSizeA = Math.sqrt((Math.pow(newControl.X - C[0].X, 2)) + (Math.pow(newControl.Y - C[0].Y, 2)));
-        let handleSizeB = Math.sqrt((Math.pow(D[n - 1].X - newControl.X, 2)) + (Math.pow(D[n - 1].Y - newControl.Y, 2)));
+        let handleSizeA = Math.sqrt(Math.pow(newControl.X - C[0].X, 2) + Math.pow(newControl.Y - C[0].Y, 2));
+        let handleSizeB = Math.sqrt(Math.pow(D[n - 1].X - newControl.X, 2) + Math.pow(D[n - 1].Y - newControl.Y, 2));
         // Scaling adjustments to improve the ratio between the magnitudes of the two handle lines.
         // (Ensures that the new point added doesn't augment the inital shape of the curve much).
         if (handleSizeA < 75 && handleSizeB < 75) {
@@ -131,50 +143,55 @@ export class InkStrokeProperties {
         }
         // Finding the last leg of the derivative curve of C.
         const dC = { X: (handleSizeA / n) * (C[m - 1].X - C[m - 2].X), Y: (handleSizeA / n) * (C[m - 1].Y - C[m - 2].Y) };
-        // Finding the first leg of the derivative curve of D. 
+        // Finding the first leg of the derivative curve of D.
         const dD = { X: (handleSizeB / m) * (D[1].X - D[0].X), Y: (handleSizeB / m) * (D[1].Y - D[0].Y) };
         const handleA = { X: newControl.X - dC.X, Y: newControl.Y - dC.Y };
         const handleB = { X: newControl.X + dD.X, Y: newControl.Y + dD.Y };
         return [handleA, handleB];
-    }
+    };
 
     /**
      * Deletes the current control point of the selected ink instance.
      */
     @undoBatch
     @action
-    deletePoints = (inkView: DocumentView, preserve: boolean) => this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
-        const doc = view.rootDoc;
-        const newPoints = ink.slice();
-        const brokenIndices = NumListCast(doc.brokenInkIndices);
-        if (preserve || this._currentPoint === 0 || this._currentPoint === ink.length - 1 || brokenIndices.includes(this._currentPoint)) {
-            newPoints.splice(this._currentPoint === 0 ? 0 : this._currentPoint === ink.length - 1 ? this._currentPoint - 3 : this._currentPoint - 2, 4);
-        } else {
-            const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
-            const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
-            const samples: Point[] = [];
-            var startDir = { x: 0, y: 0 };
-            var endDir = { x: 0, y: 0 };
-            for (var i = 0; i < splicedPoints.length / 4; i++) {
-                const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
-                if (i === 0) startDir = bez.derivative(0);
-                if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
-                for (var t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) {
-                    const pt = bez.compute(t);
-                    samples.push(new Point(pt.x, pt.y));
+    deletePoints = (inkView: DocumentView, preserve: boolean) =>
+        this.applyFunction(
+            inkView,
+            (view: DocumentView, ink: InkData) => {
+                const doc = view.rootDoc;
+                const newPoints = ink.slice();
+                const brokenIndices = NumListCast(doc.brokenInkIndices);
+                if (preserve || this._currentPoint === 0 || this._currentPoint === ink.length - 1 || brokenIndices.includes(this._currentPoint)) {
+                    newPoints.splice(this._currentPoint === 0 ? 0 : this._currentPoint === ink.length - 1 ? this._currentPoint - 3 : this._currentPoint - 2, 4);
+                } else {
+                    const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
+                    const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
+                    const samples: Point[] = [];
+                    var startDir = { x: 0, y: 0 };
+                    var endDir = { x: 0, y: 0 };
+                    for (var i = 0; i < splicedPoints.length / 4; i++) {
+                        const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
+                        if (i === 0) startDir = bez.derivative(0);
+                        if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
+                        for (var t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) {
+                            const pt = bez.compute(t);
+                            samples.push(new Point(pt.x, pt.y));
+                        }
+                    }
+                    const { finalCtrls, error } = FitOneCurve(samples, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
+                    if (error < 100) {
+                        newPoints.splice(this._currentPoint - 4, 8, ...finalCtrls);
+                    } else {
+                        newPoints.splice(this._currentPoint - 2, 4);
+                    }
                 }
-            }
-            const { finalCtrls, error } = FitOneCurve(samples, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
-            if (error < 100) {
-                newPoints.splice(this._currentPoint - 4, 8, ...finalCtrls);
-            } else {
-                newPoints.splice(this._currentPoint - 2, 4);
-            }
-        }
-        doc.brokenInkIndices = new List(brokenIndices.map(control => control >= this._currentPoint ? control - 4 : control));
-        this._currentPoint = -1;
-        return newPoints.length < 4 ? undefined : newPoints;
-    }, true)
+                doc.brokenInkIndices = new List(brokenIndices.map(control => (control >= this._currentPoint ? control - 4 : control)));
+                this._currentPoint = -1;
+                return newPoints.length < 4 ? undefined : newPoints;
+            },
+            true
+        );
 
     /**
      * Rotates ink stroke(s) about a point
@@ -188,15 +205,16 @@ export class InkStrokeProperties {
         this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number, inkStrokeWidth: number) => {
             view.rootDoc.rotation = NumCast(view.rootDoc.rotation) + angle;
             const inkCenterPt = view.ComponentView?.ptFromScreen?.(scrpt);
-            return !inkCenterPt ? ink :
-                ink.map(i => {
-                    const pt = { X: i.X - inkCenterPt.X, Y: i.Y - inkCenterPt.Y };
-                    const newX = Math.cos(angle) * pt.X - Math.sin(angle) * pt.Y * yScale / xScale;
-                    const newY = Math.sin(angle) * pt.X * xScale / yScale + Math.cos(angle) * pt.Y;
-                    return { X: newX + inkCenterPt.X, Y: newY + inkCenterPt.Y };
-                });
+            return !inkCenterPt
+                ? ink
+                : ink.map(i => {
+                      const pt = { X: i.X - inkCenterPt.X, Y: i.Y - inkCenterPt.Y };
+                      const newX = Math.cos(angle) * pt.X - (Math.sin(angle) * pt.Y * yScale) / xScale;
+                      const newY = (Math.sin(angle) * pt.X * xScale) / yScale + Math.cos(angle) * pt.Y;
+                      return { X: newX + inkCenterPt.X, Y: newY + inkCenterPt.Y };
+                  });
         });
-    }
+    };
 
     /**
      * Rotates ink stroke(s) about a point
@@ -210,16 +228,17 @@ export class InkStrokeProperties {
         this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData) => {
             const ptFromScreen = view.ComponentView?.ptFromScreen;
             const ptToScreen = view.ComponentView?.ptToScreen;
-            return !ptToScreen || !ptFromScreen ? ink :
-                ink.map(ptToScreen).map(i => {
-                    const pvec = { X: i.X - scrpt.X, Y: i.Y - scrpt.Y };
-                    const svec = pvec.X * scrVec.X * scaling + pvec.Y * scrVec.Y * scaling;
-                    const ovec = -pvec.X * scrVec.Y * (scaleUniformly ? scaling : 1) + pvec.Y * scrVec.X * (scaleUniformly ? scaling : 1);
-                    const newscrpt = { X: scrpt.X + svec * scrVec.X - ovec * scrVec.Y, Y: scrpt.Y + svec * scrVec.Y + ovec * scrVec.X };
-                    return ptFromScreen(newscrpt);
-                });
+            return !ptToScreen || !ptFromScreen
+                ? ink
+                : ink.map(ptToScreen).map(i => {
+                      const pvec = { X: i.X - scrpt.X, Y: i.Y - scrpt.Y };
+                      const svec = pvec.X * scrVec.X * scaling + pvec.Y * scrVec.Y * scaling;
+                      const ovec = -pvec.X * scrVec.Y * (scaleUniformly ? scaling : 1) + pvec.Y * scrVec.X * (scaleUniformly ? scaling : 1);
+                      const newscrpt = { X: scrpt.X + svec * scrVec.X - ovec * scrVec.Y, Y: scrpt.Y + svec * scrVec.Y + ovec * scrVec.X };
+                      return ptFromScreen(newscrpt);
+                  });
         });
-    }
+    };
 
     /**
      * Handles the movement/scaling of a control point.
@@ -230,7 +249,7 @@ export class InkStrokeProperties {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const order = controlIndex % 4;
             const closed = InkingStroke.IsClosed(ink);
-            const brokenIndices = Cast(inkView.props.Document.brokenInkIndices, listSpec("number"), []);
+            const brokenIndices = Cast(inkView.props.Document.brokenInkIndices, listSpec('number'), []);
             if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) {
                 const cpt_before = ink[controlIndex];
                 const cpt = { X: cpt_before.X + deltaX, Y: cpt_before.Y + deltaY };
@@ -238,7 +257,7 @@ export class InkStrokeProperties {
                 const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
                 const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
                 const { nearestT, nearestSeg } = InkStrokeProperties.nearestPtToStroke(splicedPoints, cpt);
-                if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && (1 - nearestT) < 1e-1)) return ink.slice();
+                if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && 1 - nearestT < 1e-1)) return ink.slice();
                 const samplesLeft: Point[] = [];
                 const samplesRight: Point[] = [];
                 var startDir = { x: 0, y: 0 };
@@ -247,7 +266,7 @@ export class InkStrokeProperties {
                     const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
                     if (i === 0) startDir = bez.derivative(0);
                     if (i === nearestSeg / 4) endDir = bez.derivative(nearestT);
-                    for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + .05 : 1); t += 0.05) {
+                    for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) {
                         const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t));
                         samplesLeft.push(new Point(pt.x, pt.y));
                     }
@@ -257,7 +276,7 @@ export class InkStrokeProperties {
                     const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
                     if (i === nearestSeg / 4) startDir = bez.derivative(nearestT);
                     if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
-                    for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + .05 + 1e-7 : 1 + 1e-7); t += 0.05) {
+                    for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) {
                         const pt = bez.compute(Math.min(1, t));
                         samplesRight.push(new Point(pt.x, pt.y));
                     }
@@ -271,12 +290,11 @@ export class InkStrokeProperties {
             return ink.map((pt, i) => {
                 const leftHandlePoint = order === 0 && i === controlIndex + 1;
                 const rightHandlePoint = order === 0 && controlIndex !== 0 && i === controlIndex - 2;
-                if (controlIndex === i ||
-                    (order === 0 && controlIndex !== 0 && i === controlIndex - 1) ||
-                    (order === 3 && i === controlIndex - 1)) {
-                    return ({ X: pt.X + deltaX, Y: pt.Y + deltaY });
+                if (controlIndex === i || (order === 0 && controlIndex !== 0 && i === controlIndex - 1) || (order === 3 && i === controlIndex - 1)) {
+                    return { X: pt.X + deltaX, Y: pt.Y + deltaY };
                 }
-                if (controlIndex === i ||
+                if (
+                    controlIndex === i ||
                     leftHandlePoint ||
                     rightHandlePoint ||
                     (order === 0 && controlIndex !== 0 && i === controlIndex - 1) ||
@@ -284,15 +302,15 @@ export class InkStrokeProperties {
                     (order === 3 && i === controlIndex - 1) ||
                     (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 1) ||
                     (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 2) ||
-                    ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1))) {
-                    return ({ X: pt.X + deltaX, Y: pt.Y + deltaY });
+                    (ink[0].X === ink[ink.length - 1].X && ink[0].Y === ink[ink.length - 1].Y && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1))
+                ) {
+                    return { X: pt.X + deltaX, Y: pt.Y + deltaY };
                 }
                 return pt;
             });
-        })
-
+        });
 
-    public static nearestPtToStroke(ctrlPoints: { X: number, Y: number }[], refInkSpacePt: { X: number, Y: number }, excludeSegs?: number[]) {
+    public static nearestPtToStroke(ctrlPoints: { X: number; Y: number }[], refInkSpacePt: { X: number; Y: number }, excludeSegs?: number[]) {
         var distance = Number.MAX_SAFE_INTEGER;
         var nearestT = -1;
         var nearestSeg = -1;
@@ -326,16 +344,16 @@ export class InkStrokeProperties {
             if (screenDragPt) {
                 const snapData = this.snapToAllCurves(screenDragPt, inkView, { nearestPt: { X: 0, Y: 0 }, distance: 10 }, ink, controlIndex);
                 if (snapData.distance < 10) {
-                    const deltaX = (snapData.nearestPt.X - ink[controlIndex].X);
-                    const deltaY = (snapData.nearestPt.Y - ink[controlIndex].Y);
+                    const deltaX = snapData.nearestPt.X - ink[controlIndex].X;
+                    const deltaY = snapData.nearestPt.Y - ink[controlIndex].Y;
                     const res = this.moveControlPtHandle(inkView, deltaX, deltaY, controlIndex, ink.slice());
-                    console.log("X = " + snapData.nearestPt.X + " " + snapData.nearestPt.Y);
+                    console.log('X = ' + snapData.nearestPt.X + ' ' + snapData.nearestPt.Y);
                     return res;
                 }
             }
         }
         return false;
-    }
+    };
 
     excludeSelfSnapSegs = (ink: InkData, controlIndex: number) => {
         const closed = InkingStroke.IsClosed(ink);
@@ -346,9 +364,9 @@ export class InkStrokeProperties {
         const nextseg = which > 1 && (closed || controlIndex < ink.length - 1) ? (thisseg + 4) % ink.length : -1;
         const prevseg = which < 2 && (closed || controlIndex > 0) ? (thisseg - 4 + ink.length) % ink.length : -1;
         return [thisseg, prevseg, nextseg];
-    }
+    };
 
-    snapToAllCurves = (screenDragPt: { X: number, Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number, Y: number }, distance: number }, ink: InkData, controlIndex: number) => {
+    snapToAllCurves = (screenDragPt: { X: number; Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number; Y: number }; distance: number }, ink: InkData, controlIndex: number) => {
         const containingCollection = inkView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
         containingCollection?.childDocs
             .filter(doc => doc.type === DocumentType.INK)
@@ -356,8 +374,7 @@ export class InkStrokeProperties {
                 const testInkView = DocumentManager.Instance.getDocumentView(doc, containingCollection?.props.CollectionView);
                 const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.rootDoc ? this.excludeSelfSnapSegs(ink, controlIndex) : []);
                 if (snapped && snapped.distance < snapData.distance) {
-                    const snappedInkPt = doc === inkView.rootDoc ? snapped.nearestPt :
-                        inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 });  // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space
+                    const snappedInkPt = doc === inkView.rootDoc ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space
 
                     if (snappedInkPt) {
                         snapData = { nearestPt: snappedInkPt, distance: snapped.distance };
@@ -365,7 +382,7 @@ export class InkStrokeProperties {
                 }
             });
         return snapData;
-    }
+    };
 
     /**
      * Snaps a control point with broken tangency back to synced rotation.
@@ -375,7 +392,7 @@ export class InkStrokeProperties {
     snapHandleTangent = (inkView: DocumentView, controlIndex: number, handleIndexA: number, handleIndexB: number) => {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const doc = view.rootDoc;
-            const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"), []);
+            const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number'), []);
             const ind = brokenIndices.findIndex(value => value === controlIndex);
             if (ind !== -1) {
                 brokenIndices.splice(ind, 1);
@@ -387,7 +404,7 @@ export class InkStrokeProperties {
                 return inkCopy;
             }
         });
-    }
+    };
 
     /**
      * Rotates the target point about the origin point for a given angle (radians).
@@ -398,11 +415,11 @@ export class InkStrokeProperties {
         const newX = Math.cos(angle) * rotatedTarget.X - Math.sin(angle) * rotatedTarget.Y;
         const newY = Math.sin(angle) * rotatedTarget.X + Math.cos(angle) * rotatedTarget.Y;
         return { X: newX + origin.X, Y: newY + origin.Y };
-    }
+    };
 
     /**
      * Finds the angle (in radians) between two inputted vectors.
-     * 
+     *
      * α = arccos(a·b / |a|·|b|), where a and b are both vectors.
      */
     public static angleBetweenTwoVectors(vectorA: PointData, vectorB: PointData) {
@@ -444,14 +461,13 @@ export class InkStrokeProperties {
             const newHandlePoint = { X: ink[handleIndex].X - deltaX, Y: ink[handleIndex].Y - deltaY };
             const inkCopy = ink.slice();
             inkCopy[handleIndex] = newHandlePoint;
-            const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"));
+            const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number'));
             const equivIndex = closed ? (controlIndex === 0 ? ink.length - 1 : controlIndex === ink.length - 1 ? 0 : -1) : -1;
             // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle).
-            if ((!brokenIndices || (!brokenIndices?.includes(controlIndex) && !brokenIndices?.includes(equivIndex))) &&
-                (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) {
+            if ((!brokenIndices || (!brokenIndices?.includes(controlIndex) && !brokenIndices?.includes(equivIndex))) && (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) {
                 const angle = InkStrokeProperties.angleChange(oldHandlePoint, newHandlePoint, controlPoint);
                 inkCopy[oppositeHandleIndex] = this.rotatePoint(oppositeHandlePoint, controlPoint, angle);
             }
             return inkCopy;
-        })
-}
\ No newline at end of file
+        });
+}
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 5936ea32d..bf0e8081d 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -9,7 +9,6 @@ import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { InkingStroke } from './InkingStroke';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import './InkTranscription.scss';
 
 /**
@@ -256,7 +255,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.ActiveTool === InkTool.Write) {
+        if (Doc.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/LightboxView.tsx b/src/client/views/LightboxView.tsx
index a1e71b5f4..99d50b4a2 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import "normalize.css";
+import 'normalize.css';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
@@ -13,7 +13,7 @@ import { SelectionManager } from '../util/SelectionManager';
 import { Transform } from '../util/Transform';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { TabDocView } from './collections/TabDocView';
-import "./LightboxView.scss";
+import './LightboxView.scss';
 import { DocumentView } from './nodes/DocumentView';
 import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
 
@@ -25,20 +25,21 @@ interface LightboxViewProps {
 
 @observer
 export class LightboxView extends React.Component<LightboxViewProps> {
-
-    @computed public static get LightboxDoc() { return this._doc; }
+    @computed public static get LightboxDoc() {
+        return this._doc;
+    }
     private static LightboxDocTemplate = () => LightboxView._layoutTemplate;
     @observable private static _layoutTemplate: Opt<Doc>;
     @observable private static _doc: Opt<Doc>;
     @observable private static _docTarget: Opt<Doc>;
     @observable private static _docFilters: string[] = []; // filters
-    @observable private static _tourMap: Opt<Doc[]> = [];   // list of all tours available from the current target
-    private static _savedState: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number>, scrollTop: Opt<number> }>;
-    private static _history: Opt<{ doc: Doc, target?: Doc }[]> = [];
+    @observable private static _tourMap: Opt<Doc[]> = []; // list of all tours available from the current target
+    private static _savedState: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }>;
+    private static _history: Opt<{ doc: Doc; target?: Doc }[]> = [];
     @observable private static _future: Opt<Doc[]> = [];
     private static _docView: Opt<DocumentView>;
     private static openInTabFunc: any;
-    static path: { doc: Opt<Doc>, target: Opt<Doc>, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt<Doc[]>, saved: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number>, scrollTop: Opt<number> }> }[] = [];
+    static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }> }[] = [];
     @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc) {
         if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
             this.LightboxDoc._panX = this._savedState.panX;
@@ -53,52 +54,71 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         } else {
             if (doc) {
                 const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement();
-                l && (Cast(l.anchor2, Doc, null).backgroundColor = "lightgreen");
+                l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
             }
             //TabDocView.PinDoc(doc, { hidePresBox: true });
-            this._history ? this._history.push({ doc, target }) : this._history = [{ doc, target }];
+            this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]);
             if (doc !== LightboxView.LightboxDoc) {
                 this._savedState = {
-                    panX: Cast(doc._panX, "number", null),
-                    panY: Cast(doc._panY, "number", null),
-                    scale: Cast(doc._viewScale, "number", null),
-                    scrollTop: Cast(doc._scrollTop, "number", null),
+                    panX: Cast(doc._panX, 'number', null),
+                    panY: Cast(doc._panY, 'number', null),
+                    scale: Cast(doc._viewScale, 'number', null),
+                    scrollTop: Cast(doc._scrollTop, 'number', null),
                 };
             }
         }
         if (future) {
-            this._future = [...(this._future ?? []), ...(this.LightboxDoc ? [this.LightboxDoc] : []), ...future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length),];
+            this._future = [
+                ...(this._future ?? []),
+                ...(this.LightboxDoc ? [this.LightboxDoc] : []),
+                ...future
+                    .slice()
+                    .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow))
+                    .sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length),
+            ];
         }
         this._doc = doc;
         this._layoutTemplate = layoutTemplate;
         this._docTarget = target || doc;
-        this._tourMap = DocListCast(doc?.links).map(link => {
-            const opp = LinkManager.getOppositeAnchor(link, doc!);
-            return opp?.TourMap ? opp : undefined;
-        }).filter(m => m).map(m => m!);
+        this._tourMap = DocListCast(doc?.links)
+            .map(link => {
+                const opp = LinkManager.getOppositeAnchor(link, doc!);
+                return opp?.TourMap ? opp : undefined;
+            })
+            .filter(m => m)
+            .map(m => m!);
 
         return true;
     }
-    public static IsLightboxDocView(path: DocumentView[]) { return path.includes(this._docView!); }
-    @computed get leftBorder() { return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]); }
-    @computed get topBorder() { return Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]); }
+    public static IsLightboxDocView(path: DocumentView[]) {
+        return path.includes(this._docView!);
+    }
+    @computed get leftBorder() {
+        return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]);
+    }
+    @computed get topBorder() {
+        return Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]);
+    }
     lightboxWidth = () => this.props.PanelWidth - this.leftBorder * 2;
     lightboxHeight = () => this.props.PanelHeight - this.topBorder * 2;
     lightboxScreenToLocal = () => new Transform(-this.leftBorder, -this.topBorder, 1);
     navBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => {
-        return <div className="lightboxView-navBtn-frame" style={{
-            display: display(),
-            left,
-            width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
-            bottom
-        }}>
-            <div className="lightboxView-navBtn" title={color} style={{ top, color: color ? "red" : "white", background: color ? "white" : undefined }}
-                onClick={click}>
-                <div style={{ height: 10 }}>{color}</div>
-                <FontAwesomeIcon icon={icon as any} size="3x" />
+        return (
+            <div
+                className="lightboxView-navBtn-frame"
+                style={{
+                    display: display(),
+                    left,
+                    width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
+                    bottom,
+                }}>
+                <div className="lightboxView-navBtn" title={color} style={{ top, color: color ? 'red' : 'white', background: color ? 'white' : undefined }} onClick={click}>
+                    <div style={{ height: 10 }}>{color}</div>
+                    <FontAwesomeIcon icon={icon as any} size="3x" />
+                </div>
             </div>
-        </div>;
-    }
+        );
+    };
     public static GetSavedState(doc: Doc) {
         return this.LightboxDoc === doc && this._savedState ? this._savedState : undefined;
     }
@@ -107,27 +127,28 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     @action
     public static SetCookie(cookie: string) {
         if (this.LightboxDoc && cookie) {
-            this._docFilters = (f => this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f])(`cookies:${cookie}:provide`);
+            this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`);
         }
     }
     public static AddDocTab = (doc: Doc, location: string, layoutTemplate?: Doc, openInTabFunc?: any) => {
         LightboxView.openInTabFunc = openInTabFunc;
         SelectionManager.DeselectAll();
-        return LightboxView.SetLightboxDoc(doc, undefined,
-            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]),
-            ...DocListCast(doc[Doc.LayoutFieldKey(doc) + "-annotations"]),
-            ...(LightboxView._future ?? [])
-            ].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)), layoutTemplate);
-    }
+        return LightboxView.SetLightboxDoc(
+            doc,
+            undefined,
+            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']), ...(LightboxView._future ?? [])].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)),
+            layoutTemplate
+        );
+    };
     docFilters = () => LightboxView._docFilters || [];
     addDocTab = LightboxView.AddDocTab;
     @action public static Next() {
         const doc = LightboxView._doc!;
-        const target = LightboxView._docTarget = this._future?.pop();
+        const target = (LightboxView._docTarget = this._future?.pop());
         const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
         if (targetDocView && target) {
             const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement();
-            l && (Cast(l.anchor2, Doc, null).backgroundColor = "lightgreen");
+            l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
             targetDocView.focus(target, { originalTarget: target, willZoom: true, scale: 0.9 });
             if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target });
         } else {
@@ -152,10 +173,13 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 LightboxView.SetLightboxDoc(target);
             }
         }
-        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links).map(link => {
-            const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
-            return opp?.TourMap ? opp : undefined;
-        }).filter(m => m).map(m => m!);
+        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links)
+            .map(link => {
+                const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
+                return opp?.TourMap ? opp : undefined;
+            })
+            .filter(m => m)
+            .map(m => m!);
     }
 
     @action public static Previous() {
@@ -170,15 +194,17 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             LightboxView._docTarget = target;
             if (!target) docView.ComponentView?.shrinkWrap?.();
             else docView.focus(target, { willZoom: true, scale: 0.9 });
-        }
-        else {
+        } else {
             LightboxView.SetLightboxDoc(doc, target);
         }
         if (LightboxView._future?.lastElement() !== previous.target || previous.doc) LightboxView._future?.push(previous.target || previous.doc);
-        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links).map(link => {
-            const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
-            return opp?.TourMap ? opp : undefined;
-        }).filter(m => m).map(m => m!);
+        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links)
+            .map(link => {
+                const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
+                return opp?.TourMap ? opp : undefined;
+            })
+            .filter(m => m)
+            .map(m => m!);
     }
     @action
     stepInto = () => {
@@ -187,7 +213,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             target: LightboxView._docTarget,
             future: LightboxView._future,
             history: LightboxView._history,
-            saved: LightboxView._savedState
+            saved: LightboxView._savedState,
         });
         const tours = LightboxView._tourMap;
         if (tours && tours.length) {
@@ -197,45 +223,58 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             const coll = LightboxView._docTarget;
             if (coll) {
                 const fieldKey = Doc.LayoutFieldKey(coll);
-                const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + "-annotations"])];
-                const links = DocListCast(coll.links).map(link => LinkManager.getOppositeAnchor(link, coll)).filter(doc => doc).map(doc => doc!);
+                const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
+                const links = DocListCast(coll.links)
+                    .map(link => LinkManager.getOppositeAnchor(link, coll))
+                    .filter(doc => doc)
+                    .map(doc => doc!);
                 LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links);
                 TabDocView.PinDoc(coll, { hidePresBox: true });
             }
         }
-    }
+    };
 
     future = () => LightboxView._future;
     tourMap = () => LightboxView._tourMap;
     render() {
-        let downx = 0, downy = 0;
-        return !LightboxView.LightboxDoc ? (null) :
-            <div className="lightboxView-frame"
-                onPointerDown={e => { downx = e.clientX; downy = e.clientY; }}
+        let downx = 0,
+            downy = 0;
+        return !LightboxView.LightboxDoc ? null : (
+            <div
+                className="lightboxView-frame"
+                onPointerDown={e => {
+                    downx = e.clientX;
+                    downy = e.clientY;
+                }}
                 onClick={e => {
                     if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) {
                         LightboxView.SetLightboxDoc(undefined);
                     }
-                }}  >
-
-                <div className="lightboxView-contents" style={{
-                    left: this.leftBorder,
-                    top: this.topBorder,
-                    width: this.lightboxWidth(),
-                    height: this.lightboxHeight(),
-                    clipPath: `path('${Doc.UserDoc().renderStyle === "comic" ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`
                 }}>
+                <div
+                    className="lightboxView-contents"
+                    style={{
+                        left: this.leftBorder,
+                        top: this.topBorder,
+                        width: this.lightboxWidth(),
+                        height: this.lightboxHeight(),
+                        clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`,
+                    }}>
                     {/* <CollectionMenu /> TODO:glr This is where it would go*/}
-                    <DocumentView ref={action((r: DocumentView | null) => {
-                        LightboxView._docView = r !== null ? r : undefined;
-                        r && setTimeout(action(() => {
-                            const target = LightboxView._docTarget;
-                            const doc = LightboxView._doc;
-                            const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target);
-                            if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.();
-                            //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here?  it breaks smooth navigation in lightbox using 'next' button
-                        }));
-                    })}
+                    <DocumentView
+                        ref={action((r: DocumentView | null) => {
+                            LightboxView._docView = r !== null ? r : undefined;
+                            r &&
+                                setTimeout(
+                                    action(() => {
+                                        const target = LightboxView._docTarget;
+                                        const doc = LightboxView._doc;
+                                        const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target);
+                                        if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.();
+                                        //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here?  it breaks smooth navigation in lightbox using 'next' button
+                                    })
+                                );
+                        })}
                         Document={LightboxView.LightboxDoc}
                         DataDoc={undefined}
                         LayoutTemplate={LightboxView.LightboxDocTemplate}
@@ -259,35 +298,57 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         searchFilterDocs={returnEmptyDoclist}
                         ContainingCollectionView={undefined}
                         ContainingCollectionDoc={undefined}
-                        renderDepth={0} />
+                        renderDepth={0}
+                    />
                 </div>
 
-                {this.navBtn(0, undefined, this.props.PanelHeight / 2 - 12.50, "chevron-left",
-                    () => LightboxView.LightboxDoc && LightboxView._history?.length ? "" : "none", e => {
+                {this.navBtn(
+                    0,
+                    undefined,
+                    this.props.PanelHeight / 2 - 12.5,
+                    'chevron-left',
+                    () => (LightboxView.LightboxDoc && LightboxView._history?.length ? '' : 'none'),
+                    e => {
                         e.stopPropagation();
                         LightboxView.Previous();
-                    })}
-                {this.navBtn(this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]), undefined, this.props.PanelHeight / 2 - 12.50, "chevron-right",
-                    () => LightboxView.LightboxDoc && LightboxView._future?.length ? "" : "none", e => {
+                    }
+                )}
+                {this.navBtn(
+                    this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
+                    undefined,
+                    this.props.PanelHeight / 2 - 12.5,
+                    'chevron-right',
+                    () => (LightboxView.LightboxDoc && LightboxView._future?.length ? '' : 'none'),
+                    e => {
                         e.stopPropagation();
                         LightboxView.Next();
-                    }, this.future()?.length.toString())}
+                    },
+                    this.future()?.length.toString()
+                )}
                 <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} tourMap={this.tourMap} />
-                <div className="lightboxView-tabBtn" title={"open in tab"}
+                <div
+                    className="lightboxView-tabBtn"
+                    title={'open in tab'}
                     onClick={e => {
                         e.stopPropagation();
-                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "");
+                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, '');
                         //LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace");
                         SelectionManager.DeselectAll();
                         LightboxView.SetLightboxDoc(undefined);
                     }}>
-                    <FontAwesomeIcon icon={"file-download"} size="2x" />
+                    <FontAwesomeIcon icon={'file-download'} size="2x" />
                 </div>
-                <div className="lightboxView-navBtn" title={"toggle fit width"}
-                    onClick={e => { e.stopPropagation(); LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; }}>
-                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? "arrows-alt-h" : "arrows-alt-v"} size="2x" />
+                <div
+                    className="lightboxView-navBtn"
+                    title={'toggle fit width'}
+                    onClick={e => {
+                        e.stopPropagation();
+                        LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth;
+                    }}>
+                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
                 </div>
-            </div>;
+            </div>
+        );
     }
 }
 interface LightboxTourBtnProps {
@@ -299,12 +360,17 @@ interface LightboxTourBtnProps {
 @observer
 export class LightboxTourBtn extends React.Component<LightboxTourBtnProps> {
     render() {
-        return this.props.navBtn("50%", 0, 0, "chevron-down",
-            () => LightboxView.LightboxDoc /*&& this.props.future()?.length*/ ? "" : "none", e => {
+        return this.props.navBtn(
+            '50%',
+            0,
+            0,
+            'chevron-down',
+            () => (LightboxView.LightboxDoc /*&& this.props.future()?.length*/ ? '' : 'none'),
+            e => {
                 e.stopPropagation();
                 this.props.stepInto();
             },
             StrCast(this.props.tourMap()?.lastElement()?.TourMap)
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 542f85228..e998f1fb9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -7,7 +7,7 @@ import * as ReactDOM from 'react-dom';
 import { AssignAllExtensions } from '../../extensions/General/Extensions';
 import { Docs } from '../documents/Documents';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { LinkManager } from '../util/LinkManager';
+import { LinkManager } from '../util/LinkManager'; // this must come before importing Docs and CurrentUserUtils
 import { ReplayMovements } from '../util/ReplayMovements';
 import { TrackMovements } from '../util/TrackMovements';
 import { CollectionView } from './collections/CollectionView';
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6c0a67de2..edc16d9a6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -9,13 +9,13 @@ import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { ScriptField } from '../../fields/ScriptField';
-import { PromiseValue, StrCast } from '../../fields/Types';
+import { 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';
 import { Docs, DocUtils } from '../documents/Documents';
+import { CollectionViewType } from '../documents/DocumentTypes';
 import { CaptureManager } from '../util/CaptureManager';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
 import { HistoryUtil } from '../util/History';
@@ -31,7 +31,6 @@ import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
 import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
-import { CollectionViewType } from './collections/CollectionView';
 import './collections/TreeView.scss';
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
@@ -76,7 +75,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 = CurrentUserUtils.MyLeftSidebarPanel;
+    @observable private _sidebarContent: any = Doc.MyLeftSidebarPanel;
     @observable private _leftMenuFlyoutWidth: number = 0;
 
     @computed private get dashboardTabHeight() {
@@ -104,27 +103,27 @@ export class MainView extends React.Component {
         return Doc.UserDoc();
     }
     @computed private get colorScheme() {
-        return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
+        return StrCast(Doc.ActiveDashboard?.colorScheme);
     }
     @computed private get mainContainer() {
-        return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard;
+        return this.userDoc ? Doc.ActiveDashboard : Doc.GuestDashboard;
     }
     @computed private get headerBarDoc() {
-        return CurrentUserUtils.MyHeaderBar;
+        return Doc.MyHeaderBar;
     }
     @computed public get mainFreeform(): Opt<Doc> {
         return (docs => (docs?.length > 1 ? docs[1] : undefined))(DocListCast(this.mainContainer!.data));
     }
 
     headerBarDocWidth = () => this.mainDocViewWidth();
-    headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0;
+    headerBarDocHeight = () => SettingsManager.headerBarHeight ?? 0;
     topMenuHeight = () => 35;
     topMenuWidth = returnZero; // value is ignored ...
     leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace('px', ''));
     leftMenuHeight = () => this._dashUIHeight;
     leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
     leftMenuFlyoutHeight = () => this._dashUIHeight;
-    propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
+    propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, SettingsManager.propertiesWidth || 0));
     propertiesHeight = () => this._dashUIHeight;
     mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth() - this.leftMenuFlyoutWidth();
     mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight();
@@ -204,7 +203,7 @@ export class MainView extends React.Component {
     constructor(props: Readonly<{}>) {
         super(props);
         MainView.Instance = this;
-        CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || ({} as any);
+        DashboardView._urlState = HistoryUtil.parseUrl(window.location) || ({} as any);
 
         // causes errors to be generated when modifying an observable outside of an action
         configure({ enforceActions: 'observed' });
@@ -212,8 +211,8 @@ export class MainView extends React.Component {
         if (window.location.pathname !== '/home') {
             const pathname = window.location.pathname.substr(1).split('/');
             if (pathname.length > 1 && pathname[0] === 'doc') {
-                CurrentUserUtils.MainDocId = pathname[1];
-                !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field)));
+                Doc.MainDocId = pathname[1];
+                !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (Doc.GuestTarget = field)));
             }
         }
 
@@ -483,18 +482,18 @@ export class MainView extends React.Component {
     };
 
     initAuthenticationRouters = async () => {
-        const received = CurrentUserUtils.MainDocId;
+        const received = Doc.MainDocId;
         if (received && !this.userDoc) {
             reaction(
-                () => CurrentUserUtils.GuestTarget,
-                target => target && CurrentUserUtils.createNewDashboard(),
+                () => Doc.GuestTarget,
+                target => target && DashboardView.createNewDashboard(),
                 { fireImmediately: true }
             );
         }
         // else {
         //     PromiseValue(this.userDoc.activeDashboard).then(dash => {
-        //         if (dash instanceof Doc) CurrentUserUtils.openDashboard(dash);
-        //         else CurrentUserUtils.createNewDashboard();
+        //         if (dash instanceof Doc) DashboardView.openDashboard(dash);
+        //         else Doc.createNewDashboard();
         //     });
         // }
     };
@@ -503,14 +502,14 @@ export class MainView extends React.Component {
     createNewPresentation = async () => {
         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');
-        CurrentUserUtils.ActivePresentation = pres;
-        Doc.AddDocToList(CurrentUserUtils.MyTrails, 'data', pres);
+        Doc.ActivePresentation = pres;
+        Doc.AddDocToList(Doc.MyTrails, 'data', pres);
     };
 
     @action
     createNewFolder = async () => {
         const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
-        Doc.AddDocToList(CurrentUserUtils.MyFilesystem, 'data', folder);
+        Doc.AddDocToList(Doc.MyFilesystem, 'data', folder);
     };
 
     @observable _exploreMode = false;
@@ -612,9 +611,9 @@ export class MainView extends React.Component {
         setupMoveUpEvents(
             this,
             e,
-            action(e => ((CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false)),
-            action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
-            action(() => (CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0)),
+            action(e => ((SettingsManager.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false)),
+            action(() => SettingsManager.propertiesWidth < 5 && (SettingsManager.propertiesWidth = 0)),
+            action(() => (SettingsManager.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0)),
             false
         );
     };
@@ -635,10 +634,10 @@ 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);
+        if (doc.dockingConfig) return DashboardView.openDashboard(doc);
         switch (locationFields[0]) {
             case 'dashboard':
-                return CurrentUserUtils.openDashboard(doc);
+                return DashboardView.openDashboard(doc);
             case 'close':
                 return CollectionDockingView.CloseSplit(doc, locationParams);
             case 'fullScreen':
@@ -669,7 +668,7 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        styleProvider={this._sidebarContent.proto === CurrentUserUtils.MyDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -697,7 +696,7 @@ export class MainView extends React.Component {
         return (
             <div key="menu" className="mainView-leftMenuPanel">
                 <DocumentView
-                    Document={CurrentUserUtils.MyLeftSidebarMenu}
+                    Document={Doc.MyLeftSidebarMenu}
                     DataDoc={undefined}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
@@ -815,9 +814,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(CurrentUserUtils.MyDockedBtns, 'data', doc), true);
+    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.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(CurrentUserUtils.MyDockedBtns, 'data', doc), true);
+    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.MyDockedBtns, 'data', doc), true);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
@@ -826,10 +825,10 @@ export class MainView extends React.Component {
     };
 
     @computed get docButtons() {
-        return !CurrentUserUtils.MyDockedBtns ? null : (
-            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? '42px' : undefined }}>
+        return !Doc.MyDockedBtns ? null : (
+            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !Doc.MyDockedBtns.linearViewIsExpanded ? '42px' : undefined }}>
                 <CollectionLinearView
-                    Document={CurrentUserUtils.MyDockedBtns}
+                    Document={Doc.MyDockedBtns}
                     DataDoc={undefined}
                     fieldKey={'data'}
                     dropAction={'alias'}
@@ -981,7 +980,7 @@ export class MainView extends React.Component {
                         case 'home':
                             return <DashboardView />;
                     }
-                })(CurrentUserUtils.ActivePage)}
+                })(Doc.ActivePage)}
 
                 <PreviewCursor />
                 <TaskCompletionBox />
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 20b99788c..b01ee5f42 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,21 +1,20 @@
-import { action, observable, ObservableMap, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt, AclSelfEdit } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { List } from "../../fields/List";
-import { NumCast } from "../../fields/Types";
-import { GetEffectiveAcl } from "../../fields/util";
-import { unimplementedFunction, Utils } from "../../Utils";
-import { Docs } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DragManager } from "../util/DragManager";
-import { undoBatch } from "../util/UndoManager";
-import "./MarqueeAnnotator.scss";
-import { DocumentView } from "./nodes/DocumentView";
-import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
-import { AnchorMenu } from "./pdf/AnchorMenu";
-import React = require("react");
-const _global = (window /* browser */ || global /* node */) as any;
+import { action, observable, ObservableMap, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
+import { NumCast } from '../../fields/Types';
+import { GetEffectiveAcl } from '../../fields/util';
+import { unimplementedFunction, Utils } from '../../Utils';
+import { Docs, DocUtils } from '../documents/Documents';
+import { DragManager } from '../util/DragManager';
+import { undoBatch } from '../util/UndoManager';
+import './MarqueeAnnotator.scss';
+import { DocumentView } from './nodes/DocumentView';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { AnchorMenu } from './pdf/AnchorMenu';
+import React = require('react');
+const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
     rootDoc: Doc;
@@ -46,7 +45,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
 
     @action
     static clearAnnotations(savedAnnotations: ObservableMap<number, HTMLDivElement[]>) {
-        AnchorMenu.Instance.Status = "marquee";
+        AnchorMenu.Instance.Status = 'marquee';
         AnchorMenu.Instance.fadeOut(true);
         // clear out old marquees and initialize menu for new selection
         Array.from(savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
@@ -60,30 +59,30 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         this._startY = this._top = (this.props.down[1] - boundingRect.top) * (this.props.mainCont.offsetHeight / boundingRect.height) + this.props.mainCont.scrollTop;
         this._height = this._width = 0;
 
-        const doc = (this.props.iframe?.()?.contentDocument ?? document);
-        doc.addEventListener("pointermove", this.onSelectMove);
-        doc.addEventListener("pointerup", this.onSelectEnd);
+        const doc = this.props.iframe?.()?.contentDocument ?? document;
+        doc.addEventListener('pointermove', this.onSelectMove);
+        doc.addEventListener('pointerup', this.onSelectEnd);
 
-        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight("rgba(173, 216, 230, 0.75)", true), true);
-        AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true));
+        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight('rgba(173, 216, 230, 0.75)', true), true);
+        AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight('rgba(173, 216, 230, 0.75)', true));
         AnchorMenu.Instance.Highlight = this.highlight;
-        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations);
+        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations);
         AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor;
 
         /**
-         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.  
+         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.
          * It also initiates a Drag/Drop interaction to place the text annotation.
          */
         AnchorMenu.Instance.StartDrag = action((e: PointerEvent, ele: HTMLElement) => {
             e.preventDefault();
             e.stopPropagation();
             const sourceAnchorCreator = () => {
-                const annoDoc = this.highlight("rgba(173, 216, 230, 0.75)", true); // hyperlink color
+                const annoDoc = this.highlight('rgba(173, 216, 230, 0.75)', true); // hyperlink color
                 annoDoc && this.props.addDocument(annoDoc);
                 return annoDoc;
             };
             const targetCreator = (annotationOn: Doc | undefined) => {
-                const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, "yellow");
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
                 FormattedTextBox.SelectOnLoad = target[Id];
                 return target;
             };
@@ -92,37 +91,39 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                     if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
                         e.annoDragData.linkSourceDoc.isPushpin = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
                     }
-                }
+                },
             });
         });
         /**
-         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.  
+         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.
          * It also initiates a Drag/Drop interaction to place the text annotation.
          */
-        AnchorMenu.Instance.StartCropDrag = !this.props.anchorMenuCrop ? unimplementedFunction : action((e: PointerEvent, ele: HTMLElement) => {
-            e.preventDefault();
-            e.stopPropagation();
-            var cropRegion: Doc | undefined;
-            const sourceAnchorCreator = () => {
-                cropRegion = this.highlight("rgba(173, 216, 230, 0.75)", true); // hyperlink color
-                cropRegion && this.props.addDocument(cropRegion);
-                return cropRegion;
-            };
-            const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
-            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
-                dragComplete: e => {
-                    if (!e.aborted && e.linkDocument) {
-                        Doc.GetProto(e.linkDocument).linkRelationship = "cropped image";
-                        Doc.GetProto(e.linkDocument).title = "crop: " + this.props.docView.rootDoc.title;
-                    }
-                }
-            });
-        });
+        AnchorMenu.Instance.StartCropDrag = !this.props.anchorMenuCrop
+            ? unimplementedFunction
+            : action((e: PointerEvent, ele: HTMLElement) => {
+                  e.preventDefault();
+                  e.stopPropagation();
+                  var cropRegion: Doc | undefined;
+                  const sourceAnchorCreator = () => {
+                      cropRegion = this.highlight('rgba(173, 216, 230, 0.75)', true); // hyperlink color
+                      cropRegion && this.props.addDocument(cropRegion);
+                      return cropRegion;
+                  };
+                  const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
+                  DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+                      dragComplete: e => {
+                          if (!e.aborted && e.linkDocument) {
+                              Doc.GetProto(e.linkDocument).linkRelationship = 'cropped image';
+                              Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView.rootDoc.title;
+                          }
+                      },
+                  });
+              });
     }
     componentWillUnmount() {
-        const doc = (this.props.iframe?.()?.contentDocument ?? document);
-        doc.removeEventListener("pointermove", this.onSelectMove);
-        doc.removeEventListener("pointerup", this.onSelectEnd);
+        const doc = this.props.iframe?.()?.contentDocument ?? document;
+        doc.removeEventListener('pointermove', this.onSelectMove);
+        doc.removeEventListener('pointerup', this.onSelectEnd);
     }
 
     @undoBatch
@@ -132,40 +133,42 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         if (savedAnnoMap.size === 0) return undefined;
         const savedAnnos = Array.from(savedAnnoMap.values())[0];
         if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) {
-            const scale = (this.props.scaling?.() || 1);
+            const scale = this.props.scaling?.() || 1;
             const anno = savedAnnos[0];
             const containerOffset = this.props.containerOffset?.() || [0, 0];
-            const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title });
-            marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || "0") - containerOffset[0]) / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
-            marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || "0") - containerOffset[1]) / scale/ NumCast(this.props.docView.props.Document._viewScale,1) + NumCast(this.props.scrollTop);
-            marqueeAnno._height = parseInt(anno.style.height || "0") / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
-            marqueeAnno._width = parseInt(anno.style.width || "0") / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
+            const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: 'Annotation on ' + this.props.rootDoc.title });
+            marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
+            marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1) + NumCast(this.props.scrollTop);
+            marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
+            marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
             anno.remove();
             savedAnnoMap.clear();
             return marqueeAnno;
         }
 
-        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, backgroundColor: "transparent", title: "Selection on " + this.props.rootDoc.title });
+        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, backgroundColor: 'transparent', title: 'Selection on ' + this.props.rootDoc.title });
         let minX = Number.MAX_VALUE;
         let maxX = -Number.MAX_VALUE;
         let minY = Number.MAX_VALUE;
         let maxY = -Number.MIN_VALUE;
         const annoDocs: Doc[] = [];
-        savedAnnoMap.forEach((value: HTMLDivElement[], key: number) => value.map(anno => {
-            const textRegion = new Doc();
-            textRegion.x = parseInt(anno.style.left ?? "0");
-            textRegion.y = parseInt(anno.style.top ?? "0");
-            textRegion._height = parseInt(anno.style.height ?? "0");
-            textRegion._width = parseInt(anno.style.width ?? "0");
-            textRegion.annoTextRegion = textRegionAnno;
-            textRegion.backgroundColor = color;
-            annoDocs.push(textRegion);
-            anno.remove();
-            minY = Math.min(NumCast(textRegion.y), minY);
-            minX = Math.min(NumCast(textRegion.x), minX);
-            maxY = Math.max(NumCast(textRegion.y) + NumCast(textRegion._height), maxY);
-            maxX = Math.max(NumCast(textRegion.x) + NumCast(textRegion._width), maxX);
-        }));
+        savedAnnoMap.forEach((value: HTMLDivElement[], key: number) =>
+            value.map(anno => {
+                const textRegion = new Doc();
+                textRegion.x = parseInt(anno.style.left ?? '0');
+                textRegion.y = parseInt(anno.style.top ?? '0');
+                textRegion._height = parseInt(anno.style.height ?? '0');
+                textRegion._width = parseInt(anno.style.width ?? '0');
+                textRegion.annoTextRegion = textRegionAnno;
+                textRegion.backgroundColor = color;
+                annoDocs.push(textRegion);
+                anno.remove();
+                minY = Math.min(NumCast(textRegion.y), minY);
+                minX = Math.min(NumCast(textRegion.x), minX);
+                maxY = Math.max(NumCast(textRegion.y) + NumCast(textRegion._height), maxY);
+                maxX = Math.max(NumCast(textRegion.x) + NumCast(textRegion._width), maxX);
+            })
+        );
 
         const textRegionAnnoProto = Doc.GetProto(textRegionAnno);
         textRegionAnnoProto.y = Math.max(minY, 0);
@@ -176,29 +179,29 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         textRegionAnnoProto.textInlineAnnotations = new List<Doc>(annoDocs);
         savedAnnoMap.clear();
         return textRegionAnno;
-    }
+    };
     @action
     highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
         const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
         !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
-        return annotationDoc as Doc ?? undefined;
-    }
+        return (annotationDoc as Doc) ?? undefined;
+    };
 
     public static previewNewAnnotation = action((savedAnnotations: ObservableMap<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => {
         if (div.style.top) {
-            div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString();
+            div.style.top = parseInt(div.style.top) /*+ this.getScrollFromPage(page)*/
+                .toString();
         }
         annotationLayer.append(div);
-        div.style.backgroundColor = "#ACCEF7";
-        div.style.opacity = "0.5";
+        div.style.backgroundColor = '#ACCEF7';
+        div.style.opacity = '0.5';
         const savedPage = savedAnnotations.get(page);
         if (savedPage) {
             savedPage.push(div);
             savedAnnotations.set(page, savedPage);
-        }
-        else {
+        } else {
             savedAnnotations.set(page, [div]);
         }
     });
@@ -210,58 +213,65 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         const mainRect = this.props.mainCont.getBoundingClientRect();
         const cliX = e.clientX * (this.props.iframeScaling?.() || 1) - boundingRect.left;
         const cliY = e.clientY * (this.props.iframeScaling?.() || 1) - boundingRect.top;
-        this._width = (cliX * (this.props.mainCont.offsetWidth / mainRect.width)) - this._startX;
-        this._height = (cliY * (this.props.mainCont.offsetHeight / mainRect.height)) - this._startY + this.props.mainCont.scrollTop;
+        this._width = cliX * (this.props.mainCont.offsetWidth / mainRect.width) - this._startX;
+        this._height = cliY * (this.props.mainCont.offsetHeight / mainRect.height) - this._startY + this.props.mainCont.scrollTop;
         this._left = Math.min(this._startX, this._startX + this._width);
         this._top = Math.min(this._startY, this._startY + this._height);
         this._width = Math.abs(this._width);
         this._height = Math.abs(this._height);
         e.stopPropagation();
-    }
+    };
 
     onSelectEnd = (e: PointerEvent) => {
         const mainRect = this.props.mainCont.getBoundingClientRect();
         const cliX = e.clientX * (this.props.iframeScaling?.() || 1) + (this.props.iframe ? mainRect.left : 0);
         const cliY = e.clientY * (this.props.iframeScaling?.() || 1) + (this.props.iframe ? mainRect.top : 0);
-        if (this._width > 10 || this._height > 10) {  // configure and show the annotation/link menu if a the drag region is big enough
-            const marquees = this.props.mainCont.getElementsByClassName("marqueeAnnotator-dragBox");
-            if (marquees?.length) { // copy the temporary marquee to allow for multiple selections (not currently available though).
-                const copy = document.createElement("div");
-                ["border", "opacity"].forEach(prop => copy.style[prop as any] = (marquees[0] as HTMLDivElement).style[prop as any]);
+        if (this._width > 10 || this._height > 10) {
+            // configure and show the annotation/link menu if a the drag region is big enough
+            const marquees = this.props.mainCont.getElementsByClassName('marqueeAnnotator-dragBox');
+            if (marquees?.length) {
+                // copy the temporary marquee to allow for multiple selections (not currently available though).
+                const copy = document.createElement('div');
+                ['border', 'opacity'].forEach(prop => (copy.style[prop as any] = (marquees[0] as HTMLDivElement).style[prop as any]));
                 const bounds = (marquees[0] as HTMLDivElement).getBoundingClientRect();
                 const uitls = Utils.GetScreenTransform(marquees[0] as HTMLDivElement);
-                const rbounds = { top: uitls.translateY, left: uitls.translateX, width: (bounds.right - bounds.left), height: (bounds.bottom - bounds.top) };
+                const rbounds = { top: uitls.translateY, left: uitls.translateX, width: bounds.right - bounds.left, height: bounds.bottom - bounds.top };
                 const otls = Utils.GetScreenTransform(this.props.annotationLayer);
                 const fbounds = { top: (rbounds.top - otls.translateY) / otls.scale, left: (rbounds.left - otls.translateX) / otls.scale, width: rbounds.width / otls.scale, height: rbounds.height / otls.scale };
-                copy.style.top = fbounds.top.toString() + "px";
-                copy.style.left = fbounds.left.toString() + "px";
-                copy.style.width = fbounds.width.toString() + "px";
-                copy.style.height = fbounds.height.toString() + "px";
-                copy.className = "marqueeAnnotator-annotationBox";
+                copy.style.top = fbounds.top.toString() + 'px';
+                copy.style.left = fbounds.left.toString() + 'px';
+                copy.style.width = fbounds.width.toString() + 'px';
+                copy.style.height = fbounds.height.toString() + 'px';
+                copy.className = 'marqueeAnnotator-annotationBox';
                 (copy as any).marqueeing = true;
                 MarqueeAnnotator.previewNewAnnotation(this.props.savedAnnotations(), this.props.annotationLayer, copy, this.props.getPageFromScroll?.(this._top) || 0);
             }
 
             AnchorMenu.Instance.jumpTo(cliX, cliY);
 
-            if (AnchorMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
-                this.highlight("rgba(245, 230, 95, 0.75)", false);  // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
+            if (AnchorMenu.Instance.Highlighting) {
+                // when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
+                this.highlight('rgba(245, 230, 95, 0.75)', false); // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
             }
             this.props.finishMarquee(undefined, undefined, e);
         } else {
-            runInAction(() => this._width = this._height = 0);
+            runInAction(() => (this._width = this._height = 0));
             this.props.finishMarquee(cliX, cliY, e);
         }
-    }
+    };
 
     render() {
-        return <div className="marqueeAnnotator-dragBox"
-            style={{
-                left: `${this._left}px`, top: `${this._top}px`,
-                width: `${this._width}px`, height: `${this._height}px`,
-                border: `${this._width === 0 ? "" : "2px dashed black"}`,
-                opacity: 0.2
-            }}>
-        </div>;
+        return (
+            <div
+                className="marqueeAnnotator-dragBox"
+                style={{
+                    left: `${this._left}px`,
+                    top: `${this._top}px`,
+                    width: `${this._width}px`,
+                    height: `${this._height}px`,
+                    border: `${this._width === 0 ? '' : '2px dashed black'}`,
+                    opacity: 0.2,
+                }}></div>
+        );
     }
 }
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 598fff29a..5242fabb8 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,23 +1,21 @@
-import { docs } from "googleapis/build/src/apis/docs";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
-import * as React from "react";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import * as React from 'react';
 import ReactLoading from 'react-loading';
-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";
-import { DocUtils } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DragManager } from "../util/DragManager";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
-import { Transform } from "../util/Transform";
-import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
-import { DocumentView } from "./nodes/DocumentView";
+import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { NumCast } from '../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils';
+import { DocUtils } from '../documents/Documents';
+import { DragManager } from '../util/DragManager';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { Transform } from '../util/Transform';
+import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView';
+import { DocumentView } from './nodes/DocumentView';
 import './OverlayView.scss';
 import { ScriptingRepl } from './ScriptingRepl';
-import { DefaultStyleProvider } from "./StyleProvider";
+import { DefaultStyleProvider } from './StyleProvider';
 
 export type OverlayDisposer = () => void;
 
@@ -52,18 +50,18 @@ export class OverlayWindow extends React.Component<OverlayWindowProps> {
     }
 
     onPointerDown = (_: React.PointerEvent) => {
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-        document.addEventListener("pointermove", this.onPointerMove);
-        document.addEventListener("pointerup", this.onPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointerup', this.onPointerUp);
+        document.addEventListener('pointermove', this.onPointerMove);
+        document.addEventListener('pointerup', this.onPointerUp);
+    };
 
     onResizerPointerDown = (_: React.PointerEvent) => {
-        document.removeEventListener("pointermove", this.onResizerPointerMove);
-        document.removeEventListener("pointerup", this.onResizerPointerUp);
-        document.addEventListener("pointermove", this.onResizerPointerMove);
-        document.addEventListener("pointerup", this.onResizerPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onResizerPointerMove);
+        document.removeEventListener('pointerup', this.onResizerPointerUp);
+        document.addEventListener('pointermove', this.onResizerPointerMove);
+        document.addEventListener('pointerup', this.onResizerPointerUp);
+    };
 
     @action
     onPointerMove = (e: PointerEvent) => {
@@ -71,7 +69,7 @@ export class OverlayWindow extends React.Component<OverlayWindowProps> {
         this.x = Math.max(Math.min(this.x, window.innerWidth - this.width), 0);
         this.y += e.movementY;
         this.y = Math.max(Math.min(this.y, window.innerHeight - this.height), 0);
-    }
+    };
 
     @action
     onResizerPointerMove = (e: PointerEvent) => {
@@ -79,28 +77,28 @@ export class OverlayWindow extends React.Component<OverlayWindowProps> {
         this.width = Math.max(this.width, 30);
         this.height += e.movementY;
         this.height = Math.max(this.height, 30);
-    }
+    };
 
     onPointerUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointerup', this.onPointerUp);
+    };
 
     onResizerPointerUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.onResizerPointerMove);
-        document.removeEventListener("pointerup", this.onResizerPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onResizerPointerMove);
+        document.removeEventListener('pointerup', this.onResizerPointerUp);
+    };
 
     render() {
         return (
             <div className="overlayWindow-outerDiv" style={{ transform: `translate(${this.x}px, ${this.y}px)`, width: this.width, height: this.height }}>
-                <div className="overlayWindow-titleBar" onPointerDown={this.onPointerDown} >
-                    {this.props.overlayOptions.title || "Untitled"}
-                    <button onClick={this.props.onClick} className="overlayWindow-closeButton">X</button>
-                </div>
-                <div className="overlayWindow-content">
-                    {this.props.children}
+                <div className="overlayWindow-titleBar" onPointerDown={this.onPointerDown}>
+                    {this.props.overlayOptions.title || 'Untitled'}
+                    <button onClick={this.props.onClick} className="overlayWindow-closeButton">
+                        X
+                    </button>
                 </div>
+                <div className="overlayWindow-content">{this.props.children}</div>
                 <div className="overlayWindow-resizeDragger" onPointerDown={this.onResizerPointerDown}></div>
             </div>
         );
@@ -126,13 +124,20 @@ export class OverlayView extends React.Component {
             const index = this._elements.indexOf(ele);
             if (index !== -1) this._elements.splice(index, 1);
         });
-        ele = <div key={Utils.GenerateGuid()} className="overlayView-wrapperDiv" style={{
-            transform: `translate(${options.x}px, ${options.y}px)`,
-            width: options.width,
-            height: options.height,
-            top: 0,
-            left: 0
-        }}>{ele}</div>;
+        ele = (
+            <div
+                key={Utils.GenerateGuid()}
+                className="overlayView-wrapperDiv"
+                style={{
+                    transform: `translate(${options.x}px, ${options.y}px)`,
+                    width: options.width,
+                    height: options.height,
+                    top: 0,
+                    left: 0,
+                }}>
+                {ele}
+            </div>
+        );
         this._elements.push(ele);
         return remove;
     }
@@ -143,23 +148,30 @@ export class OverlayView extends React.Component {
             const index = this._elements.indexOf(contents);
             if (index !== -1) this._elements.splice(index, 1);
         });
-        contents = <OverlayWindow onClick={remove} key={Utils.GenerateGuid()} overlayOptions={options}>{contents}</OverlayWindow>;
+        contents = (
+            <OverlayWindow onClick={remove} key={Utils.GenerateGuid()} overlayOptions={options}>
+                {contents}
+            </OverlayWindow>
+        );
         this._elements.push(contents);
         return remove;
     }
 
     removeOverlayDoc = (doc: Doc | Doc[]) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc));
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc));
         return true;
-    }
-    
-    docScreenToLocalXf = computedFn(function docScreenToLocalXf(this: any, doc: Doc) {
-        return () => new Transform(-NumCast(doc.x), -NumCast(doc.y), 1);
-    }.bind(this));
+    };
+
+    docScreenToLocalXf = computedFn(
+        function docScreenToLocalXf(this: any, doc: Doc) {
+            return () => new Transform(-NumCast(doc.x), -NumCast(doc.y), 1);
+        }.bind(this)
+    );
 
     @computed get overlayDocs() {
-        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).map(d => {
-            let offsetx = 0, offsety = 0;
+        return DocListCast(Doc.MyOverlayDocs?.data).map(d => {
+            let offsetx = 0,
+                offsety = 0;
             const dref = React.createRef<HTMLDivElement>();
             const onPointerMove = action((e: PointerEvent, down: number[]) => {
                 if (e.buttons === 1) {
@@ -169,10 +181,10 @@ export class OverlayView extends React.Component {
                 if (e.metaKey) {
                     const dragData = new DragManager.DocumentDragData([d]);
                     dragData.offset = [-offsetx, -offsety];
-                    dragData.dropAction = "move";
+                    dragData.dropAction = 'move';
                     dragData.removeDocument = (doc: Doc | Doc[]) => {
-                        const docs = (doc instanceof Doc) ? [doc] : doc;
-                        docs.forEach(d => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, d));
+                        const docs = doc instanceof Doc ? [doc] : doc;
+                        docs.forEach(d => Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, d));
                         return true;
                     };
                     dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
@@ -189,31 +201,39 @@ export class OverlayView extends React.Component {
                 offsetx = NumCast(d.x) - e.clientX;
                 offsety = NumCast(d.y) - e.clientY;
             };
-            return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
-                <DocumentView
-                    Document={d}
-                    rootSelected={returnTrue}
-                    bringToFront={emptyFunction}
-                    addDocument={undefined}
-                    removeDocument={this.removeOverlayDoc}
-                    PanelWidth={d[WidthSym]}
-                    PanelHeight={d[HeightSym]}
-                    ScreenToLocalTransform={this.docScreenToLocalXf(d)}
-                    renderDepth={1}
-                    isDocumentActive={returnTrue}
-                    isContentActive={returnTrue}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    focus={DocUtils.DefaultFocus}
-                    styleProvider={DefaultStyleProvider}
-                    docViewPath={returnEmptyDoclist}
-                    addDocTab={returnFalse}
-                    pinToPres={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined} />
-            </div>;
+            return (
+                <div
+                    className="overlayView-doc"
+                    ref={dref}
+                    key={d[Id]}
+                    onPointerDown={onPointerDown}
+                    style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
+                    <DocumentView
+                        Document={d}
+                        rootSelected={returnTrue}
+                        bringToFront={emptyFunction}
+                        addDocument={undefined}
+                        removeDocument={this.removeOverlayDoc}
+                        PanelWidth={d[WidthSym]}
+                        PanelHeight={d[HeightSym]}
+                        ScreenToLocalTransform={this.docScreenToLocalXf(d)}
+                        renderDepth={1}
+                        isDocumentActive={returnTrue}
+                        isContentActive={returnTrue}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        focus={DocUtils.DefaultFocus}
+                        styleProvider={DefaultStyleProvider}
+                        docViewPath={returnEmptyDoclist}
+                        addDocTab={returnFalse}
+                        pinToPres={emptyFunction}
+                        docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                    />
+                </div>
+            );
         });
     }
 
@@ -221,13 +241,10 @@ export class OverlayView extends React.Component {
         return OverlayView.Instance.addElement(<ReactLoading type="spinningBubbles" color="green" height={250} width={250} />, { x: 300, y: 200 });
     }
 
-
     render() {
         return (
             <div className="overlayView" id="overlayView">
-                <div>
-                    {this._elements}
-                </div>
+                <div>{this._elements}</div>
                 <CollectionFreeFormLinksView key="freeformLinks" />
                 {this.overlayDocs}
             </div>
@@ -237,4 +254,4 @@ export class OverlayView extends React.Component {
 // bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error
 ScriptingGlobals.add(function addOverlayWindow(type: string, options: OverlayElementOptions) {
     OverlayView.Instance.addWindow(<ScriptingRepl />, options);
-});
\ No newline at end of file
+});
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index ef1360ef1..68f5f072d 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -1,17 +1,16 @@
 import { action, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import "normalize.css";
+import 'normalize.css';
 import * as React from 'react';
 import { Doc } from '../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { returnFalse } from '../../Utils';
 import { DocServer } from '../DocServer';
 import { Docs, DocUtils } from '../documents/Documents';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { Transform } from "../util/Transform";
+import { Transform } from '../util/Transform';
 import { undoBatch, UndoManager } from '../util/UndoManager';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import "./PreviewCursor.scss";
-import { returnFalse } from '../../Utils';
+import './PreviewCursor.scss';
 
 @observer
 export class PreviewCursor extends React.Component<{}> {
@@ -24,47 +23,66 @@ export class PreviewCursor extends React.Component<{}> {
     @observable public static Visible = false;
     constructor(props: any) {
         super(props);
-        document.addEventListener("keydown", this.onKeyPress);
-        document.addEventListener("paste", this.paste);
+        document.addEventListener('keydown', this.onKeyPress);
+        document.addEventListener('paste', this.paste);
     }
 
     paste = async (e: ClipboardEvent) => {
         if (PreviewCursor.Visible && e.clipboardData) {
             const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
-            runInAction(() => PreviewCursor.Visible = false);
+            runInAction(() => (PreviewCursor.Visible = false));
 
             // tests for URL and makes web document
             const re: any = /^https?:\/\//g;
-            const plain = e.clipboardData.getData("text/plain");
+            const plain = e.clipboardData.getData('text/plain');
             if (plain) {
                 // tests for youtube and makes video document
-                if (plain.indexOf("www.youtube.com/watch") !== -1) {
-                    const url = plain.replace("youtube.com/watch?v=", "youtube.com/embed/");
-                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.VideoDocument(url, {
-                        title: url, _width: 400, _height: 315, _nativeWidth: 600, _nativeHeight: 472.5,
-                        x: newPoint[0], y: newPoint[1]
-                    })))();
-                }
-
-                else if (re.test(plain)) {
+                if (plain.indexOf('www.youtube.com/watch') !== -1) {
+                    const url = plain.replace('youtube.com/watch?v=', 'youtube.com/embed/');
+                    undoBatch(() =>
+                        PreviewCursor._addDocument(
+                            Docs.Create.VideoDocument(url, {
+                                title: url,
+                                _width: 400,
+                                _height: 315,
+                                _nativeWidth: 600,
+                                _nativeHeight: 472.5,
+                                x: newPoint[0],
+                                y: newPoint[1],
+                            })
+                        )
+                    )();
+                } else if (re.test(plain)) {
                     const url = plain;
-                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
-                        title: url, _width: 500, _height: 300, useCors: true, x: newPoint[0], y: newPoint[1]
-                    })))();
-                }
-                else if (plain.startsWith("__DashDocId(") || plain.startsWith("__DashCloneId(")) {
-                    const clone = plain.startsWith("__DashCloneId(");
-                    const docids = plain.split(":");
-                    const strs = docids[0].split(",");
-                    const ptx = Number(strs[0].substring((clone ? "__DashCloneId(" : "__DashDocId(").length));
+                    undoBatch(() =>
+                        PreviewCursor._addDocument(
+                            Docs.Create.WebDocument(url, {
+                                title: url,
+                                _width: 500,
+                                _height: 300,
+                                useCors: true,
+                                x: newPoint[0],
+                                y: newPoint[1],
+                            })
+                        )
+                    )();
+                } else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) {
+                    const clone = plain.startsWith('__DashCloneId(');
+                    const docids = plain.split(':');
+                    const strs = docids[0].split(',');
+                    const ptx = Number(strs[0].substring((clone ? '__DashCloneId(' : '__DashDocId(').length));
                     const pty = Number(strs[1].substring(0, strs[1].length - 1));
 
-                    const batch = UndoManager.StartBatch("cloning");
+                    const batch = UndoManager.StartBatch('cloning');
                     {
-                        const docs = await Promise.all(docids.filter((did, i) => i).map(async (did) => {
-                            const doc = Cast(await DocServer.GetRefField(did), Doc, null);
-                            return clone ? (await Doc.MakeClone(doc)).clone : doc;
-                        }));
+                        const docs = await Promise.all(
+                            docids
+                                .filter((did, i) => i)
+                                .map(async did => {
+                                    const doc = Cast(await DocServer.GetRefField(did), Doc, null);
+                                    return clone ? (await Doc.MakeClone(doc)).clone : doc;
+                                })
+                        );
                         const firstx = docs.length ? NumCast(docs[0].x) + ptx - newPoint[0] : 0;
                         const firsty = docs.length ? NumCast(docs[0].y) + pty - newPoint[1] : 0;
                         docs.map(doc => {
@@ -75,79 +93,99 @@ export class PreviewCursor extends React.Component<{}> {
                     }
                     batch.end();
                     e.stopPropagation();
-                }
-                else {
+                } else {
                     // creates text document
                     FormattedTextBox.PasteOnLoad = e;
-                    UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(CurrentUserUtils.GetNewTextDoc("-pasted text-", newPoint[0], newPoint[1], 500, undefined, undefined, undefined, 750)), "paste");
+                    UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(DocUtils.GetNewTextDoc('-pasted text-', newPoint[0], newPoint[1], 500, undefined, undefined, undefined, 750)), 'paste');
                 }
-            } else
-                //pasting in images
-                if (e.clipboardData.getData("text/html") !== "" && e.clipboardData.getData("text/html").includes("<img src=")) {
-                    const re: any = /<img src="(.*?)"/g;
-                    const arr: any[] = re.exec(e.clipboardData.getData("text/html"));
+            }
+            //pasting in images
+            else if (e.clipboardData.getData('text/html') !== '' && e.clipboardData.getData('text/html').includes('<img src=')) {
+                const re: any = /<img src="(.*?)"/g;
+                const arr: any[] = re.exec(e.clipboardData.getData('text/html'));
 
-                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.ImageDocument(
-                        arr[1], {
-                        _width: 300, title: arr[1],
-                        x: newPoint[0],
-                        y: newPoint[1],
-                    })))();
-                } else if (e.clipboardData.items.length) {
-                    const batch = UndoManager.StartBatch("collection view drop");
-                    const files: File[] = [];
-                    Array.from(e.clipboardData.items).forEach(item => {
-                        const file = item.getAsFile();
-                        file && files.push(file);
-                    });
-                    const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] });
-                    generatedDocuments.forEach(PreviewCursor._addDocument);
-                    batch.end();
-                }
+                undoBatch(() =>
+                    PreviewCursor._addDocument(
+                        Docs.Create.ImageDocument(arr[1], {
+                            _width: 300,
+                            title: arr[1],
+                            x: newPoint[0],
+                            y: newPoint[1],
+                        })
+                    )
+                )();
+            } else if (e.clipboardData.items.length) {
+                const batch = UndoManager.StartBatch('collection view drop');
+                const files: File[] = [];
+                Array.from(e.clipboardData.items).forEach(item => {
+                    const file = item.getAsFile();
+                    file && files.push(file);
+                });
+                const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] });
+                generatedDocuments.forEach(PreviewCursor._addDocument);
+                batch.end();
+            }
         }
-    }
+    };
 
     @action
     onKeyPress = (e: KeyboardEvent) => {
-        // Mixing events between React and Native is finicky. 
+        // Mixing events between React and Native is finicky.
         //if not these keys, make a textbox if preview cursor is active!
-        if (e.key !== "Escape" && e.key !== "Backspace" && e.key !== "Delete" && e.key !== "CapsLock" &&
-            e.key !== "Alt" && e.key !== "Shift" && e.key !== "Meta" && e.key !== "Control" &&
-            e.key !== "Insert" && e.key !== "Home" && e.key !== "End" && e.key !== "PageUp" && e.key !== "PageDown" &&
-            e.key !== "NumLock" && e.key !== " " &&
+        if (
+            e.key !== 'Escape' &&
+            e.key !== 'Backspace' &&
+            e.key !== 'Delete' &&
+            e.key !== 'CapsLock' &&
+            e.key !== 'Alt' &&
+            e.key !== 'Shift' &&
+            e.key !== 'Meta' &&
+            e.key !== 'Control' &&
+            e.key !== 'Insert' &&
+            e.key !== 'Home' &&
+            e.key !== 'End' &&
+            e.key !== 'PageUp' &&
+            e.key !== 'PageDown' &&
+            e.key !== 'NumLock' &&
+            e.key !== ' ' &&
             (e.keyCode < 112 || e.keyCode > 123) && // F1 thru F12 keys
-            (e.keyCode < 173 || e.keyCode > 183 || e.key === "-") && // mute, volume up/down etc, - is there specifically because its keycode is 173 in Firefox so shouldn't be avoided
-            !e.key.startsWith("Arrow") &&
-            !e.defaultPrevented) {
-            if ((!e.metaKey && !e.ctrlKey) || (e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90)) {//  /^[a-zA-Z0-9$*^%#@+-=_|}{[]"':;?/><.,}]$/.test(e.key)) {
+            (e.keyCode < 173 || e.keyCode > 183 || e.key === '-') && // mute, volume up/down etc, - is there specifically because its keycode is 173 in Firefox so shouldn't be avoided
+            !e.key.startsWith('Arrow') &&
+            !e.defaultPrevented
+        ) {
+            if ((!e.metaKey && !e.ctrlKey) || (e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90)) {
+                //  /^[a-zA-Z0-9$*^%#@+-=_|}{[]"':;?/><.,}]$/.test(e.key)) {
                 PreviewCursor.Visible && PreviewCursor._onKeyPress?.(e);
-                ((!e.ctrlKey && !e.metaKey) || e.key !== "v") && (PreviewCursor.Visible = false);
+                ((!e.ctrlKey && !e.metaKey) || e.key !== 'v') && (PreviewCursor.Visible = false);
             }
         } else if (PreviewCursor.Visible) {
-            if (e.key === "ArrowRight") {
+            if (e.key === 'ArrowRight') {
                 PreviewCursor._nudge?.(1 * (e.shiftKey ? 2 : 1), 0) && e.stopPropagation();
-            } else if (e.key === "ArrowLeft") {
+            } else if (e.key === 'ArrowLeft') {
                 PreviewCursor._nudge?.(-1 * (e.shiftKey ? 2 : 1), 0) && e.stopPropagation();
-            } else if (e.key === "ArrowUp") {
+            } else if (e.key === 'ArrowUp') {
                 PreviewCursor._nudge?.(0, 1 * (e.shiftKey ? 2 : 1)) && e.stopPropagation();
-            } else if (e.key === "ArrowDown") {
+            } else if (e.key === 'ArrowDown') {
                 PreviewCursor._nudge?.(0, -1 * (e.shiftKey ? 2 : 1)) && e.stopPropagation();
             }
         }
-    }
+    };
 
     //when focus is lost, this will remove the preview cursor
     @action onBlur = (): void => {
         PreviewCursor.Visible = false;
-    }
+    };
 
     @action
-    public static Show(x: number, y: number,
+    public static Show(
+        x: number,
+        y: number,
         onKeyPress: (e: KeyboardEvent) => void,
         addLiveText: (doc: Doc) => void,
         getTransform: () => Transform,
         addDocument: undefined | ((doc: Doc | Doc[]) => boolean),
-        nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean)) {
+        nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean)
+    ) {
         this._clickPoint = [x, y];
         this._onKeyPress = onKeyPress;
         this._addLiveTextDoc = addLiveText;
@@ -157,10 +195,10 @@ export class PreviewCursor extends React.Component<{}> {
         this.Visible = true;
     }
     render() {
-        return (!PreviewCursor._clickPoint || !PreviewCursor.Visible) ? (null) :
-            <div className={`previewCursor${StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme)}`} onBlur={this.onBlur} tabIndex={0} ref={e => e?.focus()}
-                style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}>
+        return !PreviewCursor._clickPoint || !PreviewCursor.Visible ? null : (
+            <div className={`previewCursor${StrCast(Doc.ActiveDashboard?.colorScheme)}`} onBlur={this.onBlur} tabIndex={0} ref={e => e?.focus()} style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}>
                 I
-        </div >;
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 9c6d9a108..8c4c1d00b 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -1,221 +1,345 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Opt } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
-import { BoolCast, StrCast } from "../../fields/Types";
-import { ImageField } from "../../fields/URLField";
+import { BoolCast, StrCast } from '../../fields/Types';
+import { ImageField } from '../../fields/URLField';
 import { DocUtils } from '../documents/Documents';
-import { DocumentType } from '../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { SelectionManager } from '../util/SelectionManager';
 import { undoBatch } from '../util/UndoManager';
-import { CollectionViewType } from './collections/CollectionView';
-import { Colors } from "./global/globalEnums";
+import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { DocumentView } from './nodes/DocumentView';
-import { VideoBox } from "./nodes/VideoBox";
-import { pasteImageBitmap } from "./nodes/WebBoxRenderer";
+import { VideoBox } from './nodes/VideoBox';
+import { pasteImageBitmap } from './nodes/WebBoxRenderer';
 import './PropertiesButtons.scss';
-import React = require("react");
-const higflyout = require("@hig/flyout");
+import React = require('react');
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
 enum UtilityButtonState {
     Default,
     OpenRight,
-    OpenExternally
+    OpenExternally,
 }
 @observer
 export class PropertiesButtons extends React.Component<{}, {}> {
     @observable public static Instance: PropertiesButtons;
 
-    @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.rootDoc; }
-    @computed get selectedTabView() { return !SelectionManager.SelectedSchemaDoc() && SelectionManager.Views().lastElement()?.topMost; }
+    @computed get selectedDoc() {
+        return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.rootDoc;
+    }
+    @computed get selectedTabView() {
+        return !SelectionManager.SelectedSchemaDoc() && SelectionManager.Views().lastElement()?.topMost;
+    }
 
     propertyToggleBtn = (label: string, property: string, tooltip: (on?: any) => string, icon: (on: boolean) => string, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
         const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedDoc;
-        const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => (dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true;
-        return !targetDoc ? (null) :
+        const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => ((dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true);
+        return !targetDoc ? null : (
             <Tooltip title={<div className={`dash-tooltip`}>{tooltip(targetDoc?.[property])} </div>} placement="top">
                 <div>
-                    <div className={`propertiesButtons-linkButton-empty toggle-${StrCast(targetDoc[property]).includes(":hover") ? "hover" : targetDoc[property] ? "on" : "off"}`}
+                    <div
+                        className={`propertiesButtons-linkButton-empty toggle-${StrCast(targetDoc[property]).includes(':hover') ? 'hover' : targetDoc[property] ? 'on' : 'off'}`}
                         onPointerDown={e => e.stopPropagation()}
                         onClick={undoBatch(() => {
                             if (SelectionManager.Views().length > 1) {
                                 SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property));
                             } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
-                        })} >
+                        })}>
                         <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon={icon(BoolCast(targetDoc?.[property])) as any} />
                     </div>
                     <div className="propertiesButtons-title">{label}</div>
                 </div>
-            </Tooltip>;
-    }
+            </Tooltip>
+        );
+    };
     @computed get lockButton() {
-        return this.propertyToggleBtn("No\xA0Drag", "_lockedPosition", on => `${on ? "Unlock" : "Lock"} position to prevent dragging`, on => "thumbtack");
+        return this.propertyToggleBtn(
+            'No\xA0Drag',
+            '_lockedPosition',
+            on => `${on ? 'Unlock' : 'Lock'} position to prevent dragging`,
+            on => 'thumbtack'
+        );
     }
     @computed get dictationButton() {
-        return this.propertyToggleBtn("Dictate", "_showAudio", on => `${on ? "Hide" : "Show"} dictation/recording controls`, on => "microphone");
+        return this.propertyToggleBtn(
+            'Dictate',
+            '_showAudio',
+            on => `${on ? 'Hide' : 'Show'} dictation/recording controls`,
+            on => 'microphone'
+        );
     }
     @computed get maskButton() {
-        return this.propertyToggleBtn("Mask", "isInkMask", on => on ? "Make plain ink" : "Make highlight mask", on => "paint-brush", (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc));
+        return this.propertyToggleBtn(
+            'Mask',
+            'isInkMask',
+            on => (on ? 'Make plain ink' : 'Make highlight mask'),
+            on => 'paint-brush',
+            (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
+        );
     }
     @computed get clustersButton() {
-        return this.propertyToggleBtn("Clusters", "_useClusters", on => `${on ? "Hide" : "Show"} clusters`, on => "braille");
+        return this.propertyToggleBtn(
+            'Clusters',
+            '_useClusters',
+            on => `${on ? 'Hide' : 'Show'} clusters`,
+            on => 'braille'
+        );
     }
     @computed get panButton() {
-        return this.propertyToggleBtn("Lock\xA0View", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => "lock");
+        return this.propertyToggleBtn(
+            'Lock\xA0View',
+            '_lockedTransform',
+            on => `${on ? 'Unlock' : 'Lock'} panning of view`,
+            on => 'lock'
+        );
     }
     @computed get fitContentButton() {
-        return this.propertyToggleBtn("View All", "_fitContentsToBox", 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");
+        return this.propertyToggleBtn(
+            'Fit\xA0Width',
+            '_fitWidth',
+            on => `${on ? "Don't" : 'Do'} fit content to width of container`,
+            on => 'arrows-alt-h'
+        );
     }
     @computed get captionButton() {
-        return this.propertyToggleBtn("Caption", "_showCaption", on => `${on ? "Hide" : "Show"} caption footer`, on => "closed-captioning", (dv, doc) => (dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? "caption" : undefined);
+        return this.propertyToggleBtn(
+            'Caption',
+            '_showCaption',
+            on => `${on ? 'Hide' : 'Show'} caption footer`,
+            on => 'closed-captioning',
+            (dv, doc) => ((dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? 'caption' : undefined)
+        );
     }
     @computed get chromeButton() {
-        return this.propertyToggleBtn("Controls", "_chromeHidden", on => `${on ? "Show" : "Hide"} editing UI`, on => "edit", (dv, doc) => (dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden);
+        return this.propertyToggleBtn(
+            'Controls',
+            '_chromeHidden',
+            on => `${on ? 'Show' : 'Hide'} editing UI`,
+            on => 'edit',
+            (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden)
+        );
     }
     @computed get titleButton() {
-        return this.propertyToggleBtn("Title", "_showTitle", on => "Switch between title styles", on => "text-width", (dv, doc) => (dv?.rootDoc || doc)._showTitle = !(dv?.rootDoc || doc)._showTitle ? "title" : (dv?.rootDoc || doc)._showTitle === "title" ? "title:hover" : undefined);
+        return this.propertyToggleBtn(
+            'Title',
+            '_showTitle',
+            on => 'Switch between title styles',
+            on => 'text-width',
+            (dv, doc) => ((dv?.rootDoc || doc)._showTitle = !(dv?.rootDoc || doc)._showTitle ? 'title' : (dv?.rootDoc || doc)._showTitle === 'title' ? 'title:hover' : undefined)
+        );
     }
     @computed get autoHeightButton() {
-        return this.propertyToggleBtn("Auto\xA0Size", "_autoHeight", on => `Automatical vertical sizing to show all content`, on => "arrows-alt-v");
+        return this.propertyToggleBtn(
+            'Auto\xA0Size',
+            '_autoHeight',
+            on => `Automatical vertical sizing to show all content`,
+            on => 'arrows-alt-v'
+        );
     }
     @computed get gridButton() {
-        return this.propertyToggleBtn("Grid", "_backgroundGridShow", on => `Display background grid in collection`, on => "border-all");
+        return this.propertyToggleBtn(
+            'Grid',
+            '_backgroundGridShow',
+            on => `Display background grid in collection`,
+            on => 'border-all'
+        );
     }
     @computed get groupButton() {
-        return this.propertyToggleBtn("Group", "isGroup", on => `Display collection as a Group`, on => "object-group", (dv, doc) => { doc.isGroup = !doc.isGroup; doc.forceActive = doc.isGroup; });
+        return this.propertyToggleBtn(
+            'Group',
+            'isGroup',
+            on => `Display collection as a Group`,
+            on => 'object-group',
+            (dv, doc) => {
+                doc.isGroup = !doc.isGroup;
+                doc.forceActive = doc.isGroup;
+            }
+        );
     }
     @computed get freezeThumb() {
-        return this.propertyToggleBtn("Freeze\Thumb", "_thumb-frozen", on => `${on ? "Freeze" : "Unfreeze"} thumbnail`, on => "arrows-alt-h", (dv, doc) => {
-            if (doc["thumb-frozen"]) doc["thumb-frozen"] = undefined;
-            else {
-                document.body.focus(); // so that we can access the clipboard without an error
-                setTimeout(() =>
-                    pasteImageBitmap((data_url: any, error: any) => {
-                        error && console.log(error);
-                        data_url && VideoBox.convertDataUri(data_url, doc[Id] + "-thumb-frozen", true).then(
-                            returnedfilename => doc["thumb-frozen"] = new ImageField(returnedfilename));
-                    }));
+        return this.propertyToggleBtn(
+            'FreezeThumb',
+            '_thumb-frozen',
+            on => `${on ? 'Freeze' : 'Unfreeze'} thumbnail`,
+            on => 'arrows-alt-h',
+            (dv, doc) => {
+                if (doc['thumb-frozen']) doc['thumb-frozen'] = undefined;
+                else {
+                    document.body.focus(); // so that we can access the clipboard without an error
+                    setTimeout(() =>
+                        pasteImageBitmap((data_url: any, error: any) => {
+                            error && console.log(error);
+                            data_url && VideoBox.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename)));
+                        })
+                    );
+                }
             }
-        });
+        );
     }
     @computed get snapButton() {
-        return this.propertyToggleBtn("Snap\xA0Lines", "showSnapLines", on => `Display snapping lines when objects are dragged`, on => "border-all", undefined, true);
+        return this.propertyToggleBtn(
+            'Snap\xA0Lines',
+            'showSnapLines',
+            on => `Display snapping lines when objects are dragged`,
+            on => 'border-all',
+            undefined,
+            true
+        );
     }
 
     @computed
     get onClickButton() {
-        return !this.selectedDoc ? (null) : <Tooltip title={<div className="dash-tooltip">Choose onClick behavior</div>} placement="top">
-            <div>
-                <div className="propertiesButtons-linkFlyout">
-                    <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onClickFlyout}>
-                        <div className={"propertiesButtons-linkButton-empty"} onPointerDown={e => e.stopPropagation()} >
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                        </div>
-                    </Flyout>
+        return !this.selectedDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">Choose onClick behavior</div>} placement="top">
+                <div>
+                    <div className="propertiesButtons-linkFlyout">
+                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onClickFlyout}>
+                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
+                            </div>
+                        </Flyout>
+                    </div>
+                    <div className="propertiesButtons-title"> onclick </div>
                 </div>
-                <div className="propertiesButtons-title"> onclick </div>
-            </div>
-        </Tooltip>;
+            </Tooltip>
+        );
     }
     @computed
     get perspectiveButton() {
-        return !this.selectedDoc ? (null) : <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
-            <div>
-                <div className="propertiesButtons-linkFlyout">
-                    <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
-                        <div className={"propertiesButtons-linkButton-empty"} onPointerDown={e => e.stopPropagation()} >
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                        </div>
-                    </Flyout>
+        return !this.selectedDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
+                <div>
+                    <div className="propertiesButtons-linkFlyout">
+                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
+                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
+                            </div>
+                        </Flyout>
+                    </div>
+                    <div className="propertiesButtons-title"> Perspective </div>
                 </div>
-                <div className="propertiesButtons-title"> Perspective </div>
-            </div>
-        </Tooltip>;
+            </Tooltip>
+        );
     }
 
     @undoBatch
     handlePerspectiveChange = (e: any) => {
         this.selectedDoc && (this.selectedDoc._viewType = e.target.value);
-        SelectionManager.Views().filter(dv => dv.docView).map(dv => dv.docView!).forEach(docView => docView.layoutDoc._viewType = e.target.value);
-    }
+        SelectionManager.Views()
+            .filter(dv => dv.docView)
+            .map(dv => dv.docView!)
+            .forEach(docView => (docView.layoutDoc._viewType = e.target.value));
+    };
 
     @undoBatch
     @action
     handleOptionChange = (onClick: string) => {
         this.selectedDoc && (this.selectedDoc.onClickBehavior = onClick);
-        SelectionManager.Views().filter(dv => dv.docView).map(dv => dv.docView!).forEach(docView => {
-            docView.noOnClick();
-            switch (onClick) {
-                case "enterPortal": docView.makeIntoPortal(); break;
-                case "toggleDetail": docView.setToggleDetail(); break;
-                case "linkInPlace": docView.toggleFollowLink("inPlace", true, false); break;
-                case "linkOnRight": docView.toggleFollowLink("add:right", false, false); break;
-            }
-        });
-    }
+        SelectionManager.Views()
+            .filter(dv => dv.docView)
+            .map(dv => dv.docView!)
+            .forEach(docView => {
+                docView.noOnClick();
+                switch (onClick) {
+                    case 'enterPortal':
+                        docView.makeIntoPortal();
+                        break;
+                    case 'toggleDetail':
+                        docView.setToggleDetail();
+                        break;
+                    case 'linkInPlace':
+                        docView.toggleFollowLink('inPlace', true, false);
+                        break;
+                    case 'linkOnRight':
+                        docView.toggleFollowLink('add:right', false, false);
+                        break;
+                }
+            });
+    };
 
     @undoBatch
     editOnClickScript = () => {
-        if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, undefined, "onClick"));
-        else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, "onClick");
-    }
+        if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, undefined, 'onClick'));
+        else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick');
+    };
 
     @computed
     get onClickFlyout() {
         const buttonList = [
-            ["nothing", "Select Document"],
-            ["enterPortal", "Enter Portal"],
-            ["toggleDetail", "Toggle Detail"],
-            ["linkInPlace", "Follow Link"],
-            ["linkOnRight", "Open Link on Right"]
+            ['nothing', 'Select Document'],
+            ['enterPortal', 'Enter Portal'],
+            ['toggleDetail', 'Toggle Detail'],
+            ['linkInPlace', 'Follow Link'],
+            ['linkOnRight', 'Open Link on Right'],
         ];
         const currentSelection = this.selectedDoc.onClickBehavior;
         // Get items to place into the list
 
-        const list = buttonList.map((value) => {
+        const list = buttonList.map(value => {
             const click = () => {
                 this.handleOptionChange(value[0]);
             };
-            return <div className="list-item" key={`${value}`}
-                style={{
-                    backgroundColor: value[0] === currentSelection ? Colors.LIGHT_BLUE : undefined
-                }}
-                onClick={click}>
-                {value[1]}
-            </div>;
+            return (
+                <div
+                    className="list-item"
+                    key={`${value}`}
+                    style={{
+                        backgroundColor: value[0] === currentSelection ? Colors.LIGHT_BLUE : undefined,
+                    }}
+                    onClick={click}>
+                    {value[1]}
+                </div>
+            );
         });
-        return <div>
+        return (
             <div>
-                <div className="propertiesButton-dropdownList">
-                    {list}
+                <div>
+                    <div className="propertiesButton-dropdownList">{list}</div>
                 </div>
+                {Doc.noviceMode ? null : (
+                    <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript">
+                        {' '}
+                        Edit onClick Script
+                    </div>
+                )}
             </div>
-            {Doc.noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
-        </div>;
+        );
     }
     @computed
     get onPerspectiveFlyout() {
         const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear];
 
-        const makeLabel = (value: string, label: string) => <div className="radio" key={label}>
-            <label>
-                <input type="radio" value={value} checked={(this.selectedDoc?._viewType ?? "invalid") === value} onChange={this.handlePerspectiveChange} />
-                {label}
-            </label>
-        </div>;
-        return <form>
-            {Object.values(CollectionViewType).filter(type => !excludedViewTypes.includes(type)).map(type => makeLabel(type, type))}
-        </form>;
+        const makeLabel = (value: string, label: string) => (
+            <div className="radio" key={label}>
+                <label>
+                    <input type="radio" value={value} checked={(this.selectedDoc?._viewType ?? 'invalid') === value} onChange={this.handlePerspectiveChange} />
+                    {label}
+                </label>
+            </div>
+        );
+        return (
+            <form>
+                {Object.values(CollectionViewType)
+                    .filter(type => !excludedViewTypes.includes(type))
+                    .map(type => makeLabel(type, type))}
+            </form>
+        );
     }
 
     render() {
@@ -228,27 +352,33 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isFreeForm = this.selectedDoc?._viewType === CollectionViewType.Freeform;
         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 toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => (
+            <div className="propertiesButtons-button" style={style}>
+                {' '}
+                {ele}{' '}
+            </div>
+        );
         const isNovice = Doc.noviceMode;
-        return !this.selectedDoc ? (null) :
+        return !this.selectedDoc ? null : (
             <div className="propertiesButtons">
                 {toggle(this.titleButton)}
                 {toggle(this.captionButton)}
                 {toggle(this.lockButton)}
-                {toggle(this.dictationButton, { display: isNovice ? "none" : "" })}
+                {toggle(this.dictationButton, { display: isNovice ? 'none' : '' })}
                 {toggle(this.onClickButton)}
                 {toggle(this.fitWidthButton)}
                 {toggle(this.freezeThumb)}
-                {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? "none" : "" })}
-                {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })}
-                {toggle(this.maskButton, { display: !isInk ? "none" : "" })}
-                {toggle(this.chromeButton, { display: !isCollection || isNovice ? "none" : "" })}
-                {toggle(this.gridButton, { display: !isCollection ? "none" : "" })}
-                {toggle(this.groupButton, { display: isTabView || !isCollection ? "none" : "" })}
-                {toggle(this.snapButton, { display: !isCollection ? "none" : "" })}
-                {toggle(this.clustersButton, { display: !isFreeForm ? "none" : "" })}
-                {toggle(this.panButton, { display: !isFreeForm ? "none" : "" })}
-                {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? "none" : "" })}
-            </div>;
-    }
-}
\ No newline at end of file
+                {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
+                {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
+                {toggle(this.maskButton, { display: !isInk ? 'none' : '' })}
+                {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })}
+                {toggle(this.gridButton, { display: !isCollection ? 'none' : '' })}
+                {toggle(this.groupButton, { display: isTabView || !isCollection ? 'none' : '' })}
+                {toggle(this.snapButton, { display: !isCollection ? 'none' : '' })}
+                {toggle(this.clustersButton, { display: !isFreeForm ? 'none' : '' })}
+                {toggle(this.panButton, { display: !isFreeForm ? 'none' : '' })}
+                {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? 'none' : '' })}
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 1af706bb5..0f63ebc1d 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -1,20 +1,20 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { DocFocusOrOpen } from "../util/DocumentManager";
-import { CollectionDockingView } from "./collections/CollectionDockingView";
-import { CollectionViewType } from "./collections/CollectionView";
-import { DocumentView } from "./nodes/DocumentView";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { CollectionViewType } from '../documents/DocumentTypes';
+import { DocFocusOrOpen } from '../util/DocumentManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { DocumentView } from './nodes/DocumentView';
 import './PropertiesDocContextSelector.scss';
 
 type PropertiesDocContextSelectorProps = {
-    DocView?: DocumentView,
-    Stack?: any,
-    hideTitle?: boolean,
-    addDocTab(doc: Doc, location: string): void
+    DocView?: DocumentView;
+    Stack?: any;
+    hideTitle?: boolean;
+    addDocTab(doc: Doc, location: string): void;
 };
 
 @observer
@@ -26,10 +26,23 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
         const aliases = DocListCast(target.aliases);
         const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc && Cast(alias.context, Doc, null) !== targetContext).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases));
-        const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
+        const containers = containerSets.reduce((p, set) => {
+            set.map(s => p.add(s));
+            return p;
+        }, new Set<Doc>());
         const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.aliases));
-        const doclayouts = Array.from(doclayoutSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>()).keys());
-        return doclayouts.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).filter(doc => !Doc.IsSystem(doc)).map(doc => ({ col: doc, target }));
+        const doclayouts = Array.from(
+            doclayoutSets
+                .reduce((p, set) => {
+                    set.map(s => p.add(s));
+                    return p;
+                }, new Set<Doc>())
+                .keys()
+        );
+        return doclayouts
+            .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document))
+            .filter(doc => !Doc.IsSystem(doc))
+            .map(doc => ({ col: doc, target }));
     }
 
     getOnClick = (col: Doc, target: Doc) => {
@@ -40,14 +53,20 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
             col._panY = NumCast(target.y) + NumCast(target._height) / 2;
         }
         col.hidden = false;
-        this.props.addDocTab(col, "toggle:right");
+        this.props.addDocTab(col, 'toggle:right');
         setTimeout(() => DocFocusOrOpen(Doc.GetProto(this.props.DocView!.props.Document), col), 100);
-    }
+    };
 
     render() {
-        return <div>
-            {this.props.hideTitle ? (null) : <p key="contexts">Contexts:</p>}
-            {this._docs.map(doc => <p key={doc.col[Id] + doc.target[Id]}><a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a></p>)}
-        </div>;
+        return (
+            <div>
+                {this.props.hideTitle ? null : <p key="contexts">Contexts:</p>}
+                {this._docs.map(doc => (
+                    <p key={doc.col[Id] + doc.target[Id]}>
+                        <a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a>
+                    </p>
+                ))}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index faab2ed26..aecbc4255 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,44 +1,42 @@
-import React = require("react");
+import React = require('react');
+import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Checkbox, Tooltip } from "@material-ui/core";
-import { intersection } from "lodash";
-import { action, autorun, computed, Lambda, observable } from "mobx";
-import { observer } from "mobx-react";
-import { ColorState, SketchPicker } from "react-color";
-import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSelfEdit, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { InkField } from "../../fields/InkField";
-import { List } from "../../fields/List";
-import { ComputedField } from "../../fields/ScriptField";
-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";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DocumentManager } from "../util/DocumentManager";
-import { LinkManager } from "../util/LinkManager";
-import { SelectionManager } from "../util/SelectionManager";
-import { SharingManager } from "../util/SharingManager";
-import { Transform } from "../util/Transform";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-import { CollectionDockingView } from "./collections/CollectionDockingView";
-import { CollectionViewType } from "./collections/CollectionView";
-import { EditableView } from "./EditableView";
-import { InkStrokeProperties } from "./InkStrokeProperties";
-import { DocumentView, StyleProviderFunc } from "./nodes/DocumentView";
-import { KeyValueBox } from "./nodes/KeyValueBox";
-import { PropertiesButtons } from "./PropertiesButtons";
-import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
-import "./PropertiesView.scss";
-import { DefaultStyleProvider } from "./StyleProvider";
-import { PresBox } from "./nodes/trails";
-import { IconLookup } from "@fortawesome/fontawesome-svg-core";
-import { PropertiesDocBacklinksSelector } from "./PropertiesDocBacklinksSelector";
-const higflyout = require("@hig/flyout");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Checkbox, Tooltip } from '@material-ui/core';
+import { intersection } from 'lodash';
+import { action, autorun, computed, Lambda, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { ColorState, SketchPicker } from 'react-color';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSelfEdit, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { InkField } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { ComputedField } from '../../fields/ScriptField';
+import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
+import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from '../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { DocumentManager } from '../util/DocumentManager';
+import { LinkManager } from '../util/LinkManager';
+import { SelectionManager } from '../util/SelectionManager';
+import { SharingManager } from '../util/SharingManager';
+import { Transform } from '../util/Transform';
+import { undoBatch, UndoManager } from '../util/UndoManager';
+import { EditableView } from './EditableView';
+import { InkStrokeProperties } from './InkStrokeProperties';
+import { DocumentView, StyleProviderFunc } from './nodes/DocumentView';
+import { FilterBox } from './nodes/FilterBox';
+import { KeyValueBox } from './nodes/KeyValueBox';
+import { PresBox } from './nodes/trails';
+import { PropertiesButtons } from './PropertiesButtons';
+import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector';
+import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
+import './PropertiesView.scss';
+import { DefaultStyleProvider } from './StyleProvider';
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
-const _global = (window /* browser */ || global /* node */) as any;
+const _global = (window /* browser */ || global) /* node */ as any;
 
 interface PropertiesViewProps {
     width: number;
@@ -51,9 +49,13 @@ interface PropertiesViewProps {
 export class PropertiesView extends React.Component<PropertiesViewProps> {
     private _widthUndo?: UndoManager.Batch;
 
-    @computed get MAX_EMBED_HEIGHT() { return 200; }
+    @computed get MAX_EMBED_HEIGHT() {
+        return 200;
+    }
 
-    @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc || CurrentUserUtils.ActiveDashboard; }
+    @computed get selectedDoc() {
+        return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc || Doc.ActiveDashboard;
+    }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
         if (PresBox.Instance?._selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
@@ -65,7 +67,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get isLink(): boolean {
         return this.selectedDoc?.type === DocumentType.LINK;
     }
-    @computed get dataDoc() { return this.selectedDoc?.[DataSym]; }
+    @computed get dataDoc() {
+        return this.selectedDoc?.[DataSym];
+    }
 
     @observable layoutFields: boolean = false;
 
@@ -107,14 +111,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         this.selectedDocListenerDisposer?.();
     }
 
-    @computed get isInk() { return this.selectedDoc?.type === DocumentType.INK; }
+    @computed get isInk() {
+        return this.selectedDoc?.type === DocumentType.INK;
+    }
 
     rtfWidth = () => {
         return !this.selectedDoc ? 0 : Math.min(this.selectedDoc?.[WidthSym](), this.props.width - 20);
-    }
+    };
     rtfHeight = () => {
         return !this.selectedDoc ? 0 : this.rtfWidth() <= this.selectedDoc?.[WidthSym]() ? Math.min(this.selectedDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
-    }
+    };
 
     @action
     docWidth = () => {
@@ -126,62 +132,74 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         } else {
             return 0;
         }
-    }
+    };
 
     @action
     docHeight = () => {
         if (this.selectedDoc && this.dataDoc) {
             const layoutDoc = this.selectedDoc;
-            return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT,
-                Doc.NativeAspect(layoutDoc, undefined, true) ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true) :
-                    layoutDoc._fitWidth ? (!Doc.NativeHeight(this.dataDoc) ? NumCast(this.props.height) :
-                        Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))) :
-                        NumCast(layoutDoc._height) || 50));
+            return Math.max(
+                70,
+                Math.min(
+                    this.MAX_EMBED_HEIGHT,
+                    Doc.NativeAspect(layoutDoc, undefined, true)
+                        ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true)
+                        : layoutDoc._fitWidth
+                        ? !Doc.NativeHeight(this.dataDoc)
+                            ? NumCast(this.props.height)
+                            : Math.min((this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))
+                        : NumCast(layoutDoc._height) || 50
+                )
+            );
         }
         return 0;
-    }
+    };
 
     @computed get expandedField() {
         if (this.dataDoc && this.selectedDoc) {
             const ids: { [key: string]: string } = {};
-            const docs = SelectionManager.Views().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] :
-                SelectionManager.Views().map(dv => this.layoutFields ? dv.layoutDoc : dv.dataDoc);
+            const docs = SelectionManager.Views().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
             docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)));
             const rows: JSX.Element[] = [];
             for (const key of Object.keys(ids).slice().sort()) {
                 const docvals = new Set<any>();
                 docs.forEach(doc => docvals.add(doc[key]));
-                const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key];
-                if (key[0] === "#") {
-                    rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "2px" }} key={key}>
-                        <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
-                        &nbsp;
-                    </div>);
+                const contents = Array.from(docvals.keys()).length > 1 ? '-multiple' : docs[0][key];
+                if (key[0] === '#') {
+                    rows.push(
+                        <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '2px' }} key={key}>
+                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key}</span>
+                            &nbsp;
+                        </div>
+                    );
                 } else {
-                    const contentElement = <EditableView key="editableView"
-                        contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
-                        height={13}
-                        fontSize={10}
-                        GetValue={() => contents !== undefined ? Field.toString(contents as Field) : "null"}
-                        SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }}
-                    />;
-                    rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
-                        <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
-                        &nbsp;
-                        {contentElement}
-                    </div>);
+                    const contentElement = (
+                        <EditableView
+                            key="editableView"
+                            contents={contents !== undefined ? Field.toString(contents as Field) : 'null'}
+                            height={13}
+                            fontSize={10}
+                            GetValue={() => (contents !== undefined ? Field.toString(contents as Field) : 'null')}
+                            SetValue={(value: string) => {
+                                docs.map(doc => KeyValueBox.SetField(doc, key, value, true));
+                                return true;
+                            }}
+                        />
+                    );
+                    rows.push(
+                        <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '-1px' }} key={key}>
+                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ':'}</span>
+                            &nbsp;
+                            {contentElement}
+                        </div>
+                    );
                 }
             }
-            rows.push(<div className="propertiesView-field" key={"newKeyValue"} style={{ marginTop: "3px" }}>
-                <EditableView
-                    key="editableView"
-                    oneLine
-                    contents={"add key:value or #tags"}
-                    height={13}
-                    fontSize={10}
-                    GetValue={() => ""}
-                    SetValue={this.setKeyValue} />
-            </div>);
+            rows.push(
+                <div className="propertiesView-field" key={'newKeyValue'} style={{ marginTop: '3px' }}>
+                    <EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
+                </div>
+            );
             return rows;
         }
     }
@@ -192,74 +210,80 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             const docs = SelectionManager.Views().length < 2 ? [this.dataDoc] : SelectionManager.Views().map(dv => dv.dataDoc);
             docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)));
             const rows: JSX.Element[] = [];
-            const noviceReqFields = ["author", "creationDate", "tags"];
-            const noviceLayoutFields = ["_curPage"];
-            const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("acl"))),
-            ...noviceReqFields, ...noviceLayoutFields];
+            const noviceReqFields = ['author', 'creationDate', 'tags'];
+            const noviceLayoutFields = ['_curPage'];
+            const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl'))), ...noviceReqFields, ...noviceLayoutFields];
             for (const key of noviceKeys.sort()) {
                 const docvals = new Set<any>();
                 docs.forEach(doc => docvals.add(doc[key]));
-                const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key];
-                if (key[0] === "#") {
-                    rows.push(<div className="propertiesView-uneditable-field" key={key}>
-                        <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
-                        &nbsp;
-                    </div>);
+                const contents = Array.from(docvals.keys()).length > 1 ? '-multiple' : docs[0][key];
+                if (key[0] === '#') {
+                    rows.push(
+                        <div className="propertiesView-uneditable-field" key={key}>
+                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key}</span>
+                            &nbsp;
+                        </div>
+                    );
                 } else if (contents !== undefined) {
                     const value = Field.toString(contents as Field);
-                    if (noviceReqFields.includes(key) || key.indexOf("lastModified") !== -1) {
-                        rows.push(<div className="propertiesView-uneditable-field" key={key}>
-                            <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span>
-                            <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div>
-                        </div>);
+                    if (noviceReqFields.includes(key) || key.indexOf('lastModified') !== -1) {
+                        rows.push(
+                            <div className="propertiesView-uneditable-field" key={key}>
+                                <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ': '}</span>
+                                <div style={{ whiteSpace: 'nowrap', overflowX: 'hidden' }}>{value}</div>
+                            </div>
+                        );
                     } else {
-                        const contentElement = <EditableView key="editableView"
-                            contents={value}
-                            height={13}
-                            fontSize={10}
-                            GetValue={() => contents !== undefined ? Field.toString(contents as Field) : "null"}
-                            SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }}
-                        />;
+                        const contentElement = (
+                            <EditableView
+                                key="editableView"
+                                contents={value}
+                                height={13}
+                                fontSize={10}
+                                GetValue={() => (contents !== undefined ? Field.toString(contents as Field) : 'null')}
+                                SetValue={(value: string) => {
+                                    docs.map(doc => KeyValueBox.SetField(doc, key, value, true));
+                                    return true;
+                                }}
+                            />
+                        );
 
-                        rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
-                            <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
-                            &nbsp;
-                            {contentElement}
-                        </div>);
+                        rows.push(
+                            <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '-1px' }} key={key}>
+                                <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ':'}</span>
+                                &nbsp;
+                                {contentElement}
+                            </div>
+                        );
                     }
                 }
             }
-            rows.push(<div className="propertiesView-field" key={"newKeyValue"} style={{ marginTop: "3px" }}>
-                <EditableView
-                    key="editableView"
-                    oneLine
-                    contents={"add key:value or #tags"}
-                    height={13}
-                    fontSize={10}
-                    GetValue={() => ""}
-                    SetValue={this.setKeyValue} />
-            </div>);
+            rows.push(
+                <div className="propertiesView-field" key={'newKeyValue'} style={{ marginTop: '3px' }}>
+                    <EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
+                </div>
+            );
             return rows;
         }
     }
 
     @undoBatch
     setKeyValue = (value: string) => {
-        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => this.layoutFields ? dv.layoutDoc : dv.dataDoc);
+        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
         docs.forEach(doc => {
-            if (value.indexOf(":") !== -1) {
+            if (value.indexOf(':') !== -1) {
                 const newVal = value[0].toUpperCase() + value.substring(1, value.length);
-                const splits = newVal.split(":");
+                const splits = newVal.split(':');
                 KeyValueBox.SetField(doc, splits[0], splits[1], true);
-                const tags = StrCast(doc.tags, ":");
-                if (tags.includes(`${splits[0]}:`) && splits[1] === "undefined") {
-                    KeyValueBox.SetField(doc, "tags", `"${tags.replace(splits[0] + ":", "")}"`, true);
+                const tags = StrCast(doc.tags, ':');
+                if (tags.includes(`${splits[0]}:`) && splits[1] === 'undefined') {
+                    KeyValueBox.SetField(doc, 'tags', `"${tags.replace(splits[0] + ':', '')}"`, true);
                 }
                 return true;
-            } else if (value[0] === "#") {
+            } else if (value[0] === '#') {
                 const newVal = value + `:'${value}'`;
                 doc[DataSym][value] = value;
-                const tags = StrCast(doc.tags, ":");
+                const tags = StrCast(doc.tags, ':');
                 if (!tags.includes(`${value}:`)) {
                     doc[DataSym].tags = `${tags + value + ':'}`;
                 }
@@ -267,68 +291,72 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             }
         });
         return false;
-    }
+    };
 
     @observable transform: Transform = Transform.Identity();
     getTransform = () => this.transform;
     propertiesDocViewRef = (ref: HTMLDivElement) => {
-        const observer = new _global.ResizeObserver(action((entries: any) => {
-            const cliRect = ref.getBoundingClientRect();
-            this.transform = new Transform(-cliRect.x, -cliRect.y, 1);
-        }));
+        const observer = new _global.ResizeObserver(
+            action((entries: any) => {
+                const cliRect = ref.getBoundingClientRect();
+                this.transform = new Transform(-cliRect.x, -cliRect.y, 1);
+            })
+        );
         ref && observer.observe(ref);
-    }
+    };
 
     @computed get contexts() {
-        return !this.selectedDoc ? (null) : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
+        return !this.selectedDoc ? null : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
     @computed get links() {
-        return !this.selectedDoc ? (null) : <PropertiesDocBacklinksSelector Document={this.selectedDoc} hideTitle={true} addDocTab={this.props.addDocTab} />;
+        return !this.selectedDoc ? null : <PropertiesDocBacklinksSelector Document={this.selectedDoc} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
     @computed get layoutPreview() {
         if (SelectionManager.Views().length > 1) {
-            return "-- multiple selected --";
+            return '-- multiple selected --';
         }
         if (this.selectedDoc) {
             const layoutDoc = Doc.Layout(this.selectedDoc);
-            const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
-            const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
-            return <div ref={this.propertiesDocViewRef} style={{ pointerEvents: "none", display: "inline-block", height: panelHeight() }} key={this.selectedDoc[Id]}>
-                <DocumentView
-                    Document={layoutDoc}
-                    DataDoc={this.dataDoc}
-                    renderDepth={1}
-                    fitContentsToBox={returnTrue}
-                    rootSelected={returnFalse}
-                    styleProvider={DefaultStyleProvider}
-                    docViewPath={returnEmptyDoclist}
-                    dontCenter={"y"}
-                    isDocumentActive={returnFalse}
-                    isContentActive={emptyFunction}
-                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
-                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
-                    PanelWidth={panelWidth}
-                    PanelHeight={panelHeight}
-                    focus={returnFalse}
-                    ScreenToLocalTransform={this.getTransform}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionDoc={undefined}
-                    ContainingCollectionView={undefined}
-                    addDocument={returnFalse}
-                    moveDocument={undefined}
-                    removeDocument={returnFalse}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    addDocTab={returnFalse}
-                    pinToPres={emptyFunction}
-                    bringToFront={returnFalse}
-                    dontRegisterView={true}
-                    dropAction={undefined}
-                />
-            </div>;
+            const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes('FormattedTextBox') ? this.rtfHeight : this.docHeight;
+            const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes('FormattedTextBox') ? this.rtfWidth : this.docWidth;
+            return (
+                <div ref={this.propertiesDocViewRef} style={{ pointerEvents: 'none', display: 'inline-block', height: panelHeight() }} key={this.selectedDoc[Id]}>
+                    <DocumentView
+                        Document={layoutDoc}
+                        DataDoc={this.dataDoc}
+                        renderDepth={1}
+                        fitContentsToBox={returnTrue}
+                        rootSelected={returnFalse}
+                        styleProvider={DefaultStyleProvider}
+                        docViewPath={returnEmptyDoclist}
+                        dontCenter={'y'}
+                        isDocumentActive={returnFalse}
+                        isContentActive={emptyFunction}
+                        NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
+                        NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
+                        PanelWidth={panelWidth}
+                        PanelHeight={panelHeight}
+                        focus={returnFalse}
+                        ScreenToLocalTransform={this.getTransform}
+                        docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
+                        ContainingCollectionDoc={undefined}
+                        ContainingCollectionView={undefined}
+                        addDocument={returnFalse}
+                        moveDocument={undefined}
+                        removeDocument={returnFalse}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        addDocTab={returnFalse}
+                        pinToPres={emptyFunction}
+                        bringToFront={returnFalse}
+                        dontRegisterView={true}
+                        dropAction={undefined}
+                    />
+                </div>
+            );
         } else {
             return null;
         }
@@ -339,67 +367,85 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     changePermissions = (e: any, user: string) => {
-        const docs = SelectionManager.Views().length < 2 ? (this.selectedDoc ? [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);
-    }
+    };
 
     /**
      * @returns the options for the permissions dropdown.
      */
     getPermissionsSelect(user: string, permission: string) {
         const dropdownValues: string[] = Object.values(SharingPermissions);
-        if (permission === "-multiple-") dropdownValues.unshift(permission);
-        if (user === "Override") dropdownValues.unshift("None");
-        return <select className="permissions-select"
-            value={permission}
-            onChange={e => this.changePermissions(e, user)}>
-            {dropdownValues.filter(permission =>
-                !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
-                    <option key={permission} value={permission}>  {permission}  </option>)}
-        </select>;
+        if (permission === '-multiple-') dropdownValues.unshift(permission);
+        if (user === 'Override') dropdownValues.unshift('None');
+        return (
+            <select className="permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
+                {dropdownValues
+                    .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
+                    .map(permission => (
+                        <option key={permission} value={permission}>
+                            {' '}
+                            {permission}{' '}
+                        </option>
+                    ))}
+            </select>
+        );
     }
 
     /**
      * @returns the notification icon. On clicking, it should notify someone of a document been shared with them.
      */
     @computed get notifyIcon() {
-        return <Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
-            <div className="notify-button">
-                <FontAwesomeIcon className="notify-button-icon" icon="bell" color="white" size="sm" />
-            </div>
-        </Tooltip>;
+        return (
+            <Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
+                <div className="notify-button">
+                    <FontAwesomeIcon className="notify-button-icon" icon="bell" color="white" size="sm" />
+                </div>
+            </Tooltip>
+        );
     }
 
     /**
      * ... next to the owner that opens the main SharingManager interface on click.
      */
     @computed get expansionIcon() {
-        return <Tooltip title={<div className="dash-tooltip">{"Show more permissions"}</div>}>
-            <div className="expansion-button" onPointerDown={() => {
-                if (this.selectedDocumentView || this.selectedDoc) {
-                    SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
-                }
-            }}>
-                <FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
-            </div>
-        </Tooltip>;
+        return (
+            <Tooltip title={<div className="dash-tooltip">{'Show more permissions'}</div>}>
+                <div
+                    className="expansion-button"
+                    onPointerDown={() => {
+                        if (this.selectedDocumentView || this.selectedDoc) {
+                            SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
+                        }
+                    }}>
+                    <FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
+                </div>
+            </Tooltip>
+        );
     }
 
     /**
      * @returns a row of the permissions panel
      */
     sharingItem(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
-        return <div className="propertiesView-sharingTable-item" key={name + permission}
-        // style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }}
-        // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)}
-        >
-            <div className="propertiesView-sharingTable-item-name" style={{ width: name !== "Me" ? "85px" : "80px" }}> {name} </div>
-            {/* {name !== "Me" ? this.notifyIcon : null} */}
-            <div className="propertiesView-sharingTable-item-permission">
-                {admin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission}
-                {permission === "Owner" || showExpansionIcon ? this.expansionIcon : null}
+        return (
+            <div
+                className="propertiesView-sharingTable-item"
+                key={name + permission}
+                // style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }}
+                // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)}
+            >
+                <div className="propertiesView-sharingTable-item-name" style={{ width: name !== 'Me' ? '85px' : '80px' }}>
+                    {' '}
+                    {name}{' '}
+                </div>
+                {/* {name !== "Me" ? this.notifyIcon : null} */}
+                <div className="propertiesView-sharingTable-item-permission">
+                    {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
+                    {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null}
+                </div>
             </div>
-        </div>;
+        );
     }
 
     /**
@@ -407,19 +453,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @computed get sharingTable() {
         const AclMap = new Map<symbol, string>([
-            [AclUnset, "None"],
+            [AclUnset, 'None'],
             [AclPrivate, SharingPermissions.None],
             [AclReadonly, SharingPermissions.View],
             [AclAugment, SharingPermissions.Augment],
             [AclSelfEdit, SharingPermissions.SelfEdit],
             [AclEdit, SharingPermissions.Edit],
-            [AclAdmin, SharingPermissions.Admin]
+            [AclAdmin, SharingPermissions.Admin],
         ]);
 
         // all selected docs
-        const docs = SelectionManager.Views().length < 2 ?
-            [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]]
-            : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]);
+        const docs =
+            SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]));
 
         const target = docs[0];
 
@@ -428,7 +473,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
 
         // users in common between all docs
-        const commonKeys: string[] = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym])));
+        const commonKeys: string[] = intersection(...docs.map(doc => (this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym]))));
 
         const tableEntries = [];
 
@@ -436,9 +481,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (commonKeys.length) {
             for (const key of commonKeys) {
                 const name = denormalizeEmail(key.substring(4));
-                const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]);
-                if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public" && name !== "Override"/* && sidebarUsersDisplayed![name] !== false*/) {
-                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : "-multiple-"));
+                const uniform = docs.every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]));
+                if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
+                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : '-multiple-'));
                 }
             }
         }
@@ -446,62 +491,53 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
         // shifts the current user, owner, public to the top of the doc.
         // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
-        tableEntries.unshift(this.sharingItem("Public", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Public"] === docs[0]["acl-Public"]) ? (AclMap.get(target[AclSym]?.["acl-Public"]) || SharingPermissions.None) : "-multiple-"));
-        tableEntries.unshift(this.sharingItem("Me", showAdmin, docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-", !ownerSame));
-        if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner"));
+        tableEntries.unshift(this.sharingItem('Public', showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === docs[0]['acl-Public']) ? AclMap.get(target[AclSym]?.['acl-Public']) || SharingPermissions.None : '-multiple-'));
+        tableEntries.unshift(
+            this.sharingItem('Me', showAdmin, docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : '-multiple-', !ownerSame)
+        );
+        if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
 
-        return <div className="propertiesView-sharingTable">
-            {tableEntries}
-        </div>;
+        return <div className="propertiesView-sharingTable">{tableEntries}</div>;
     }
 
     @computed get fieldsCheckbox() {
-        return <Checkbox
-            color="primary"
-            onChange={this.toggleCheckbox}
-            checked={this.layoutFields}
-        />;
+        return <Checkbox color="primary" onChange={this.toggleCheckbox} checked={this.layoutFields} />;
     }
 
     @action
     toggleCheckbox = () => {
         this.layoutFields = !this.layoutFields;
-    }
+    };
 
     @computed get editableTitle() {
         const titles = new Set<string>();
         SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
-        const title = Array.from(titles.keys()).length > 1 ? "--multiple selected--" : StrCast(this.selectedDoc?.title);
-        return <div className="editable-title">
-            <EditableView
-                key="editableView"
-                contents={title}
-                height={25}
-                fontSize={14}
-                GetValue={() => title}
-                SetValue={this.setTitle} />
-        </div>;
+        const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
+        return (
+            <div className="editable-title">
+                <EditableView key="editableView" contents={title} height={25} fontSize={14} GetValue={() => title} SetValue={this.setTitle} />
+            </div>
+        );
     }
 
     @undoBatch
     @action
     setTitle = (value: string) => {
         if (SelectionManager.Views().length > 1) {
-            SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, "title", value, true));
+            SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true));
             return true;
         } else if (this.dataDoc) {
-            if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, "title", value, true);
-            else KeyValueBox.SetField(this.dataDoc, "title", value, true);
+            if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true);
+            else KeyValueBox.SetField(this.dataDoc, 'title', value, true);
             return true;
         }
         return false;
-    }
-
+    };
 
     @undoBatch
     @action
     rotate = (angle: number) => {
-        const _centerPoints: { X: number, Y: number }[] = [];
+        const _centerPoints: { X: number; Y: number }[] = [];
         if (this.selectedDoc) {
             const doc = this.selectedDoc;
             if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
@@ -522,7 +558,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 doc.rotation = NumCast(doc.rotation) + angle;
                 const inks = Cast(doc.data, InkField)?.inkData;
                 if (inks) {
-                    const newPoints: { X: number, Y: number }[] = [];
+                    const newPoints: { X: number; Y: number }[] = [];
                     inks.forEach(ink => {
                         const newX = Math.cos(angle) * (ink.X - _centerPoints[index].X) - Math.sin(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].X;
                         const newY = Math.sin(angle) * (ink.X - _centerPoints[index].X) + Math.cos(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].Y;
@@ -536,117 +572,134 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     const right = Math.max(...xs);
                     const bottom = Math.max(...ys);
 
-                    doc._height = (bottom - top);
-                    doc._width = (right - left);
+                    doc._height = bottom - top;
+                    doc._width = right - left;
                 }
                 index++;
             }
         }
-    }
+    };
 
     @computed
     get controlPointsButton() {
-        return <div className="inking-button">
-            <Tooltip title={<div className="dash-tooltip">{"Edit points"}</div>}>
-                <div className="inking-button-points"
-                    style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? "black" : "" }}
-                    onPointerDown={action(() => InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton)} >
-                    <FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
-                </div>
-            </Tooltip>
-            <Tooltip title={<div className="dash-tooltip">{InkStrokeProperties.Instance._lock ? "Unlock ratio" : "Lock ratio"}</div>}>
-                <div className="inking-button-lock" onPointerDown={action(() => InkStrokeProperties.Instance._lock = !InkStrokeProperties.Instance._lock)} >
-                    <FontAwesomeIcon icon={InkStrokeProperties.Instance._lock ? "lock" : "unlock"} color="white" size="lg" />
-                </div>
-            </Tooltip>
-            <Tooltip title={<div className="dash-tooltip">{"Rotate 90˚"}</div>}>
-                <div className="inking-button-rotate" onPointerDown={action(() => this.rotate(Math.PI / 2))}>
-                    <FontAwesomeIcon icon="undo" color="white" size="lg" />
-                </div>
-            </Tooltip>
-        </div>;
+        return (
+            <div className="inking-button">
+                <Tooltip title={<div className="dash-tooltip">{'Edit points'}</div>}>
+                    <div
+                        className="inking-button-points"
+                        style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? 'black' : '' }}
+                        onPointerDown={action(() => (InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton))}>
+                        <FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
+                    </div>
+                </Tooltip>
+                <Tooltip title={<div className="dash-tooltip">{InkStrokeProperties.Instance._lock ? 'Unlock ratio' : 'Lock ratio'}</div>}>
+                    <div className="inking-button-lock" onPointerDown={action(() => (InkStrokeProperties.Instance._lock = !InkStrokeProperties.Instance._lock))}>
+                        <FontAwesomeIcon icon={InkStrokeProperties.Instance._lock ? 'lock' : 'unlock'} color="white" size="lg" />
+                    </div>
+                </Tooltip>
+                <Tooltip title={<div className="dash-tooltip">{'Rotate 90˚'}</div>}>
+                    <div className="inking-button-rotate" onPointerDown={action(() => this.rotate(Math.PI / 2))}>
+                        <FontAwesomeIcon icon="undo" color="white" size="lg" />
+                    </div>
+                </Tooltip>
+            </div>
+        );
     }
 
     inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => {
-        return <div className="inputBox"
-            style={{
-                marginRight: title === "X:" ? "19px" : "",
-                marginLeft: title === "∠:" ? "39px" : ""
-            }}>
-            <div className="inputBox-title"> {title} </div>
-            <input className="inputBox-input"
-                type="text" value={value}
-                onChange={e => {
-                    setter(e.target.value);
-                }}
-                onKeyPress={e => {
-                    e.stopPropagation();
-                }} />
-            <div className="inputBox-button">
-                <div className="inputBox-button-up" key="up2"
-                    onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} >
-                    <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
-                </div>
-                <div className="inputbox-Button-down" key="down2"
-                    onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} >
-                    <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+        return (
+            <div
+                className="inputBox"
+                style={{
+                    marginRight: title === 'X:' ? '19px' : '',
+                    marginLeft: title === '∠:' ? '39px' : '',
+                }}>
+                <div className="inputBox-title"> {title} </div>
+                <input
+                    className="inputBox-input"
+                    type="text"
+                    value={value}
+                    onChange={e => {
+                        setter(e.target.value);
+                    }}
+                    onKeyPress={e => {
+                        e.stopPropagation();
+                    }}
+                />
+                <div className="inputBox-button">
+                    <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
+                        <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
+                    </div>
+                    <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
+                        <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+                    </div>
                 </div>
             </div>
-        </div>;
-    }
+        );
+    };
 
     inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => {
-        return <div className="inputBox-duo">
-            {this.inputBox(key, value, setter, title1)}
-            {title2 === "" ? (null) : this.inputBox(key2, value2, setter2, title2)}
-        </div>;
-    }
+        return (
+            <div className="inputBox-duo">
+                {this.inputBox(key, value, setter, title1)}
+                {title2 === '' ? null : this.inputBox(key2, value2, setter2, title2)}
+            </div>
+        );
+    };
 
     @action
     upDownButtons = (dirs: string, field: string) => {
         switch (field) {
-            case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break;
-            case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break;
-            case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break;
-            case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
-            case "wid":
+            case 'rot':
+                this.rotate(dirs === 'up' ? 0.1 : -0.1);
+                break;
+            case 'Xps':
+                this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === 'up' ? 10 : -10));
+                break;
+            case 'Yps':
+                this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === 'up' ? 10 : -10));
+                break;
+            case 'stk':
+                this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === 'up' ? 0.1 : -0.1));
+                break;
+            case 'wid':
                 const oldWidth = NumCast(this.selectedDoc?._width);
                 const oldHeight = NumCast(this.selectedDoc?._height);
                 const oldX = NumCast(this.selectedDoc?.x);
                 const oldY = NumCast(this.selectedDoc?.y);
-                this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10));
-                InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height)));
+                this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === 'up' ? 10 : -10));
+                InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth) * NumCast(this.selectedDoc?._height));
                 const doc = this.selectedDoc;
                 if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
                     const ink = Cast(doc.data, InkField)?.inkData;
                     if (ink) {
-                        const newPoints: { X: number, Y: number }[] = [];
+                        const newPoints: { X: number; Y: number }[] = [];
                         for (var j = 0; j < ink.length; j++) {
-                            // (new x — oldx) + (oldxpoint * newWidt)/oldWidth 
-                            const newX = (NumCast(doc.x) - oldX) + (ink[j].X * NumCast(doc._width)) / oldWidth;
-                            const newY = (NumCast(doc.y) - oldY) + (ink[j].Y * NumCast(doc._height)) / oldHeight;
+                            // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+                            const newX = NumCast(doc.x) - oldX + (ink[j].X * NumCast(doc._width)) / oldWidth;
+                            const newY = NumCast(doc.y) - oldY + (ink[j].Y * NumCast(doc._height)) / oldHeight;
                             newPoints.push({ X: newX, Y: newY });
                         }
                         doc.data = new InkField(newPoints);
                     }
                 }
                 break;
-            case "hgt":
+            case 'hgt':
                 const oWidth = NumCast(this.selectedDoc?._width);
                 const oHeight = NumCast(this.selectedDoc?._height);
                 const oX = NumCast(this.selectedDoc?.x);
                 const oY = NumCast(this.selectedDoc?.y);
-                this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10));
-                InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width)));
+                this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === 'up' ? 10 : -10));
+                InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight) * NumCast(this.selectedDoc?._width));
                 const docu = this.selectedDoc;
                 if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) {
                     const ink = Cast(docu.data, InkField)?.inkData;
                     if (ink) {
-                        const newPoints: { X: number, Y: number }[] = [];
+                        const newPoints: { X: number; Y: number }[] = [];
                         for (var j = 0; j < ink.length; j++) {
-                            // (new x — oldx) + (oldxpoint * newWidt)/oldWidth 
-                            const newX = (NumCast(docu.x) - oX) + (ink[j].X * NumCast(docu._width)) / oWidth;
-                            const newY = (NumCast(docu.y) - oY) + (ink[j].Y * NumCast(docu._height)) / oHeight;
+                            // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+                            const newX = NumCast(docu.x) - oX + (ink[j].X * NumCast(docu._width)) / oWidth;
+                            const newY = NumCast(docu.y) - oY + (ink[j].Y * NumCast(docu._height)) / oHeight;
                             newPoints.push({ X: newX, Y: newY });
                         }
                         docu.data = new InkField(newPoints);
@@ -654,7 +707,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 }
                 break;
         }
-    }
+    };
 
     getField(key: string) {
         //if (this.selectedDoc) {
@@ -664,14 +717,30 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // }
     }
 
-    @computed get shapeXps() { return this.getField("x"); }
-    @computed get shapeYps() { return this.getField("y"); }
-    @computed get shapeRot() { return this.getField("rotation"); }
-    @computed get shapeHgt() { return this.getField("_height"); }
-    @computed get shapeWid() { return this.getField("_width"); }
-    set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Number(value)); }
-    set shapeYps(value) { this.selectedDoc && (this.selectedDoc.y = Number(value)); }
-    set shapeRot(value) { this.selectedDoc && (this.selectedDoc.rotation = Number(value)); }
+    @computed get shapeXps() {
+        return this.getField('x');
+    }
+    @computed get shapeYps() {
+        return this.getField('y');
+    }
+    @computed get shapeRot() {
+        return this.getField('rotation');
+    }
+    @computed get shapeHgt() {
+        return this.getField('_height');
+    }
+    @computed get shapeWid() {
+        return this.getField('_width');
+    }
+    set shapeXps(value) {
+        this.selectedDoc && (this.selectedDoc.x = Number(value));
+    }
+    set shapeYps(value) {
+        this.selectedDoc && (this.selectedDoc.y = Number(value));
+    }
+    set shapeRot(value) {
+        this.selectedDoc && (this.selectedDoc.rotation = Number(value));
+    }
     set shapeWid(value) {
         const oldWidth = NumCast(this.selectedDoc?._width);
         this.selectedDoc && (this.selectedDoc._width = Number(value));
@@ -683,38 +752,122 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight);
     }
 
-    @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => { if (!isNaN(Number(val))) { this.shapeHgt = val; } return true; }, "H:", "wid", this.shapeWid, (val: string) => { if (!isNaN(Number(val))) { this.shapeWid = val; } return true; }, "W:"); }
-    @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => { if (val !== "0" && !isNaN(Number(val))) { this.shapeXps = val; } return true; }, "X:", "Yps", this.shapeYps, (val: string) => { if (val !== "0" && !isNaN(Number(val))) { this.shapeYps = val; } return true; }, "Y:"); }
-    @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { if (!isNaN(Number(val))) { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; } return true; }, "∠:", "rot", this.shapeRot, (val: string) => { if (!isNaN(Number(val))) { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; } return true; }, ""); }
-
+    @computed get hgtInput() {
+        return this.inputBoxDuo(
+            'hgt',
+            this.shapeHgt,
+            (val: string) => {
+                if (!isNaN(Number(val))) {
+                    this.shapeHgt = val;
+                }
+                return true;
+            },
+            'H:',
+            'wid',
+            this.shapeWid,
+            (val: string) => {
+                if (!isNaN(Number(val))) {
+                    this.shapeWid = val;
+                }
+                return true;
+            },
+            'W:'
+        );
+    }
+    @computed get XpsInput() {
+        return this.inputBoxDuo(
+            'Xps',
+            this.shapeXps,
+            (val: string) => {
+                if (val !== '0' && !isNaN(Number(val))) {
+                    this.shapeXps = val;
+                }
+                return true;
+            },
+            'X:',
+            'Yps',
+            this.shapeYps,
+            (val: string) => {
+                if (val !== '0' && !isNaN(Number(val))) {
+                    this.shapeYps = val;
+                }
+                return true;
+            },
+            'Y:'
+        );
+    }
+    @computed get rotInput() {
+        return this.inputBoxDuo(
+            'rot',
+            this.shapeRot,
+            (val: string) => {
+                if (!isNaN(Number(val))) {
+                    this.rotate(Number(val) - Number(this.shapeRot));
+                    this.shapeRot = val;
+                }
+                return true;
+            },
+            '∠:',
+            'rot',
+            this.shapeRot,
+            (val: string) => {
+                if (!isNaN(Number(val))) {
+                    this.rotate(Number(val) - Number(this.shapeRot));
+                    this.shapeRot = val;
+                }
+                return true;
+            },
+            ''
+        );
+    }
 
     @observable private _fillBtn = false;
     @observable private _lineBtn = false;
 
-    private _lastFill = "#D0021B";
-    private _lastLine = "#D0021B";
-    private _lastDash: any = "2";
+    private _lastFill = '#D0021B';
+    private _lastLine = '#D0021B';
+    private _lastDash: any = '2';
 
-    @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; }
-    @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
-    set colorFil(value) { value && (this._lastFill = value); this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined); }
-    set colorStk(value) { value && (this._lastLine = value); this.selectedDoc && (this.selectedDoc.color = value ? value : undefined); }
+    @computed get colorFil() {
+        const ccol = this.getField('fillColor') || '';
+        ccol && (this._lastFill = ccol);
+        return ccol;
+    }
+    @computed get colorStk() {
+        const ccol = this.getField('color') || '';
+        ccol && (this._lastLine = ccol);
+        return ccol;
+    }
+    set colorFil(value) {
+        value && (this._lastFill = value);
+        this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined);
+    }
+    set colorStk(value) {
+        value && (this._lastLine = value);
+        this.selectedDoc && (this.selectedDoc.color = value ? value : undefined);
+    }
 
     colorButton(value: string, type: string, setter: () => {}) {
         // return <div className="properties-flyout" onPointerEnter={e => this.changeScrolling(false)}
         //     onPointerLeave={e => this.changeScrolling(true)}>
         //     <Flyout anchorPoint={anchorPoints.LEFT_TOP}
         //         content={type === "fill" ? this.fillPicker : this.linePicker}>
-        return <div className="color-button" key="color" onPointerDown={undoBatch(action(e => setter()))}>
-            <div className="color-button-preview" style={{
-                backgroundColor: value ?? "121212", width: 15, height: 15,
-                display: value === "" || value === "transparent" ? "none" : ""
-            }} />
-            {value === "" || value === "transparent" ? <p style={{ fontSize: 25, color: "red", marginTop: -14 }}>☒</p> : ""}
-        </div>;
+        return (
+            <div className="color-button" key="color" onPointerDown={undoBatch(action(e => setter()))}>
+                <div
+                    className="color-button-preview"
+                    style={{
+                        backgroundColor: value ?? '121212',
+                        width: 15,
+                        height: 15,
+                        display: value === '' || value === 'transparent' ? 'none' : '',
+                    }}
+                />
+                {value === '' || value === 'transparent' ? <p style={{ fontSize: 25, color: 'red', marginTop: -14 }}>☒</p> : ''}
+            </div>
+        );
         //     </Flyout>
         // </div>;
-
     }
 
     @undoBatch
@@ -723,206 +876,271 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const val = String(color.hex);
         this.colorStk = val;
         return true;
-    }
+    };
     @undoBatch
     @action
     switchFil = (color: ColorState) => {
         const val = String(color.hex);
         this.colorFil = val;
         return true;
-    }
+    };
 
     colorPicker(setter: (color: string) => {}, type: string) {
-        return <SketchPicker onChange={type === "stk" ? this.switchStk : this.switchFil}
-            presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-                '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-                '#FFFFFF', '#f1efeb', 'transparent']}
-            color={type === "stk" ? this.colorStk : this.colorFil} />;
+        return (
+            <SketchPicker
+                onChange={type === 'stk' ? this.switchStk : this.switchFil}
+                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+                color={type === 'stk' ? this.colorStk : this.colorFil}
+            />
+        );
+    }
+
+    @computed get fillButton() {
+        return this.colorButton(this.colorFil, 'fill', () => {
+            this._fillBtn = !this._fillBtn;
+            this._lineBtn = false;
+            return true;
+        });
+    }
+    @computed get lineButton() {
+        return this.colorButton(this.colorStk, 'line', () => {
+            this._lineBtn = !this._lineBtn;
+            this._fillBtn = false;
+            return true;
+        });
     }
 
-    @computed get fillButton() { return this.colorButton(this.colorFil, "fill", () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); }
-    @computed get lineButton() { return this.colorButton(this.colorStk, "line", () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); }
-
-    @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); }
-    @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); }
+    @computed get fillPicker() {
+        return this.colorPicker((color: string) => (this.colorFil = color), 'fil');
+    }
+    @computed get linePicker() {
+        return this.colorPicker((color: string) => (this.colorStk = color), 'stk');
+    }
 
     @computed get strokeAndFill() {
-        return <div>
-            <div key="fill" className="strokeAndFill">
-                <div className="fill">
-                    <div className="fill-title">Fill:</div>
-                    <div className="fill-button">{this.fillButton}</div>
-                </div>
-                <div className="stroke">
-                    <div className="stroke-title"> Stroke: </div>
-                    <div className="stroke-button">{this.lineButton}</div>
+        return (
+            <div>
+                <div key="fill" className="strokeAndFill">
+                    <div className="fill">
+                        <div className="fill-title">Fill:</div>
+                        <div className="fill-button">{this.fillButton}</div>
+                    </div>
+                    <div className="stroke">
+                        <div className="stroke-title"> Stroke: </div>
+                        <div className="stroke-button">{this.lineButton}</div>
+                    </div>
                 </div>
+                {this._fillBtn ? this.fillPicker : ''}
+                {this._lineBtn ? this.linePicker : ''}
             </div>
-            {this._fillBtn ? this.fillPicker : ""}
-            {this._lineBtn ? this.linePicker : ""}
-        </div>;
-    }
-
-    @computed get solidStk() { return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === "0") ? true : false; }
-    @computed get dashdStk() { return this.selectedDoc?.strokeDash || ""; }
-    @computed get unStrokd() { return this.selectedDoc?.color ? true : false; }
-    @computed get widthStk() { return this.getField("strokeWidth") || "1"; }
-    @computed get markScal() { return Number(this.getField("strokeMakerScale") || "1"); }
-    @computed get markHead() { return this.getField("strokeStartMarker") || ""; }
-    @computed get markTail() { return this.getField("strokeEndMarker") || ""; }
-    set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; }
+        );
+    }
+
+    @computed get solidStk() {
+        return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === '0') ? true : false;
+    }
+    @computed get dashdStk() {
+        return this.selectedDoc?.strokeDash || '';
+    }
+    @computed get unStrokd() {
+        return this.selectedDoc?.color ? true : false;
+    }
+    @computed get widthStk() {
+        return this.getField('strokeWidth') || '1';
+    }
+    @computed get markScal() {
+        return Number(this.getField('strokeMakerScale') || '1');
+    }
+    @computed get markHead() {
+        return this.getField('strokeStartMarker') || '';
+    }
+    @computed get markTail() {
+        return this.getField('strokeEndMarker') || '';
+    }
+    set solidStk(value) {
+        this.dashdStk = '';
+        this.unStrokd = !value;
+    }
     set dashdStk(value) {
         value && (this._lastDash = value) && (this.unStrokd = false);
         this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined);
     }
-    set markScal(value) { this.selectedDoc && (this.selectedDoc.strokeMarkerScale = Number(value)); }
-    set widthStk(value) { this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value)); }
-    set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; }
-    set markHead(value) { this.selectedDoc && (this.selectedDoc.strokeStartMarker = value); }
-    set markTail(value) { this.selectedDoc && (this.selectedDoc.strokeEndMarker = value); }
-
-
-    @computed get stkInput() { return this.regInput("stk", this.widthStk, (val: string) => this.widthStk = val); }
-    @computed get markScaleInput() { return this.regInput("scale", this.markScal.toString(), (val: string) => this.markScal = Number(val)); }
+    set markScal(value) {
+        this.selectedDoc && (this.selectedDoc.strokeMarkerScale = Number(value));
+    }
+    set widthStk(value) {
+        this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value));
+    }
+    set unStrokd(value) {
+        this.colorStk = value ? '' : this._lastLine;
+    }
+    set markHead(value) {
+        this.selectedDoc && (this.selectedDoc.strokeStartMarker = value);
+    }
+    set markTail(value) {
+        this.selectedDoc && (this.selectedDoc.strokeEndMarker = value);
+    }
 
+    @computed get stkInput() {
+        return this.regInput('stk', this.widthStk, (val: string) => (this.widthStk = val));
+    }
+    @computed get markScaleInput() {
+        return this.regInput('scale', this.markScal.toString(), (val: string) => (this.markScal = Number(val)));
+    }
 
     regInput = (key: string, value: any, setter: (val: string) => {}) => {
-        return <div className="inputBox">
-            <input className="inputBox-input"
-                type="text" value={value}
-                onChange={e => setter(e.target.value)} />
-            <div className="inputBox-button">
-                <div className="inputBox-button-up" key="up2"
-                    onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} >
-                    <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
-                </div>
-                <div className="inputbox-Button-down" key="down2"
-                    onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} >
-                    <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+        return (
+            <div className="inputBox">
+                <input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} />
+                <div className="inputBox-button">
+                    <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
+                        <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
+                    </div>
+                    <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
+                        <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+                    </div>
                 </div>
             </div>
-        </div>;
-    }
+        );
+    };
 
     @computed get widthAndDash() {
-        return <div className="widthAndDash">
-            <div className="width">
-                <div className="width-top">
-                    <div className="width-title">Width:</div>
-                    <div className="width-input">{this.stkInput}</div>
-                </div>
-                <input className="width-range" type="range"
-                    defaultValue={Number(this.widthStk)} min={1} max={100}
-                    onChange={(action((e) => this.widthStk = e.target.value))}
-                    onMouseDown={(e) => { this._widthUndo = UndoManager.StartBatch("width undo"); }}
-                    onMouseUp={(e) => { this._widthUndo?.end(); this._widthUndo = undefined; }}
-                />
-            </div>
-
-            <div className="arrows">
-                <div className="arrows-head">
+        return (
+            <div className="widthAndDash">
+                <div className="width">
                     <div className="width-top">
-                        <div className="width-title">Arrow Scale:</div>
-                        {/* <div className="width-input">{this.markScalInput}</div> */}
+                        <div className="width-title">Width:</div>
+                        <div className="width-input">{this.stkInput}</div>
                     </div>
-                    <input className="width-range" type="range"
-                        defaultValue={this.markScal} min={0} max={10}
-                        onChange={(action(e => this.markScal = +e.target.value))}
-                        onMouseDown={(e) => { this._widthUndo = UndoManager.StartBatch("scale undo"); }}
-                        onMouseUp={(e) => { this._widthUndo?.end(); this._widthUndo = undefined; }}
+                    <input
+                        className="width-range"
+                        type="range"
+                        defaultValue={Number(this.widthStk)}
+                        min={1}
+                        max={100}
+                        onChange={action(e => (this.widthStk = e.target.value))}
+                        onMouseDown={e => {
+                            this._widthUndo = UndoManager.StartBatch('width undo');
+                        }}
+                        onMouseUp={e => {
+                            this._widthUndo?.end();
+                            this._widthUndo = undefined;
+                        }}
                     />
                 </div>
-                <div className="arrows-head">
-                    <div className="arrows-head-title" >Arrow Head: </div>
-                    <input key="markHead" className="arrows-head-input" type="checkbox"
-                        checked={this.markHead !== ""}
-                        onChange={undoBatch(action(() => this.markHead = this.markHead ? "" : "arrow"))} />
+
+                <div className="arrows">
+                    <div className="arrows-head">
+                        <div className="width-top">
+                            <div className="width-title">Arrow Scale:</div>
+                            {/* <div className="width-input">{this.markScalInput}</div> */}
+                        </div>
+                        <input
+                            className="width-range"
+                            type="range"
+                            defaultValue={this.markScal}
+                            min={0}
+                            max={10}
+                            onChange={action(e => (this.markScal = +e.target.value))}
+                            onMouseDown={e => {
+                                this._widthUndo = UndoManager.StartBatch('scale undo');
+                            }}
+                            onMouseUp={e => {
+                                this._widthUndo?.end();
+                                this._widthUndo = undefined;
+                            }}
+                        />
+                    </div>
+                    <div className="arrows-head">
+                        <div className="arrows-head-title">Arrow Head: </div>
+                        <input key="markHead" className="arrows-head-input" type="checkbox" checked={this.markHead !== ''} onChange={undoBatch(action(() => (this.markHead = this.markHead ? '' : 'arrow')))} />
+                    </div>
+                    <div className="arrows-tail">
+                        <div className="arrows-tail-title">Arrow End: </div>
+                        <input key="markTail" className="arrows-tail-input" type="checkbox" checked={this.markTail !== ''} onChange={undoBatch(action(() => (this.markTail = this.markTail ? '' : 'arrow')))} />
+                    </div>
                 </div>
-                <div className="arrows-tail">
-                    <div className="arrows-tail-title" >Arrow End: </div>
-                    <input key="markTail" className="arrows-tail-input" type="checkbox"
-                        checked={this.markTail !== ""}
-                        onChange={undoBatch(action(() => this.markTail = this.markTail ? "" : "arrow"))} />
+                <div className="dashed">
+                    <div className="dashed-title">Dashed Line:</div>
+                    <input key="markHead" className="dashed-input" type="checkbox" checked={this.dashdStk === '2'} onChange={this.changeDash} />
                 </div>
             </div>
-            <div className="dashed">
-                <div className="dashed-title">Dashed Line:</div>
-                <input key="markHead" className="dashed-input"
-                    type="checkbox" checked={this.dashdStk === "2"}
-                    onChange={this.changeDash} />
-            </div>
-        </div>;
+        );
     }
 
-    @undoBatch @action
+    @undoBatch
+    @action
     changeDash = () => {
-        this.dashdStk = this.dashdStk === "2" ? "0" : "2";
-    }
+        this.dashdStk = this.dashdStk === '2' ? '0' : '2';
+    };
 
     @computed get appearanceEditor() {
-        return <div className="appearance-editor">
-            {this.widthAndDash}
-            {this.strokeAndFill}
-        </div>;
+        return (
+            <div className="appearance-editor">
+                {this.widthAndDash}
+                {this.strokeAndFill}
+            </div>
+        );
     }
 
     @computed get transformEditor() {
-        return <div className="transform-editor">
-            {this.controlPointsButton}
-            {this.hgtInput}
-            {this.XpsInput}
-            {this.rotInput}
-        </div>;
+        return (
+            <div className="transform-editor">
+                {this.controlPointsButton}
+                {this.hgtInput}
+                {this.XpsInput}
+                {this.rotInput}
+            </div>
+        );
     }
 
     @computed get optionsSubMenu() {
-        return <div className="propertiesView-settings" onPointerEnter={action(() => this.inOptions = true)}
-            onPointerLeave={action(() => this.inOptions = false)}>
-            <div className="propertiesView-settings-title"
-                onPointerDown={action(() => this.openOptions = !this.openOptions)}
-                style={{ backgroundColor: this.openOptions ? "black" : "" }}>
-                Options
-                <div className="propertiesView-settings-title-icon">
-                    <FontAwesomeIcon icon={this.openOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-settings" onPointerEnter={action(() => (this.inOptions = true))} onPointerLeave={action(() => (this.inOptions = false))}>
+                <div className="propertiesView-settings-title" onPointerDown={action(() => (this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}>
+                    Options
+                    <div className="propertiesView-settings-title-icon">
+                        <FontAwesomeIcon icon={this.openOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {!this.openOptions ? null : (
+                    <div className="propertiesView-settings-content">
+                        <PropertiesButtons />
+                    </div>
+                )}
             </div>
-            {!this.openOptions ? (null) :
-                <div className="propertiesView-settings-content">
-                    <PropertiesButtons />
-                </div>}
-        </div>;
+        );
     }
 
     @computed get sharingSubMenu() {
-        return <div className="propertiesView-sharing">
-            <div className="propertiesView-sharing-title"
-                onPointerDown={action(() => this.openSharing = !this.openSharing)}
-                style={{ backgroundColor: this.openSharing ? "black" : "" }}>
-                Sharing {"&"} Permissions
-                <div className="propertiesView-sharing-title-icon">
-                    <FontAwesomeIcon icon={this.openSharing ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-sharing">
+                <div className="propertiesView-sharing-title" onPointerDown={action(() => (this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}>
+                    Sharing {'&'} Permissions
+                    <div className="propertiesView-sharing-title-icon">
+                        <FontAwesomeIcon icon={this.openSharing ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
-            </div>
-            {!this.openSharing ? (null) :
-                <div className="propertiesView-sharing-content">
-                    <div className="propertiesView-buttonContainer">
-                        {!Doc.noviceMode ? (<div className="propertiesView-acls-checkbox">
-                            <Checkbox
-                                color="primary"
-                                onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)}
-                                checked={this.layoutDocAcls}
-                            />
-                            <div className="propertiesView-acls-checkbox-text">Layout</div>
-                        </div>) : (null)}
-                        {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+                {!this.openSharing ? null : (
+                    <div className="propertiesView-sharing-content">
+                        <div className="propertiesView-buttonContainer">
+                            {!Doc.noviceMode ? (
+                                <div className="propertiesView-acls-checkbox">
+                                    <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
+                                    <div className="propertiesView-acls-checkbox-text">Layout</div>
+                                </div>
+                            ) : null}
+                            {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
                                         <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
                                             <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
                                         </button>
                                     </Tooltip> */}
+                        </div>
+                        {this.sharingTable}
                     </div>
-                    {this.sharingTable}
-                </div>}
-        </div>;
+                )}
+            </div>
+        );
     }
 
     /**
@@ -930,11 +1148,11 @@ 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 = FilterBox.createFilterDoc();
     }
 
     /**
-     * Creates a new currentFilter for this.filterDoc, 
+     * Creates a new currentFilter for this.filterDoc,
      */
     createNewFilterDoc = () => {
         if (this.selectedDoc) {
@@ -944,9 +1162,9 @@ 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 = CurrentUserUtils.createFilterDoc();
+            this.selectedDoc.currentFilter = FilterBox.createFilterDoc();
         }
-    }
+    };
 
     /**
      * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter
@@ -970,20 +1188,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             doc._docRangeFiltersList = new List<string>();
             this.selectedDoc._docRangeFilters = savedDocRangeFilters;
         }
-    }
+    };
 
     @computed get filtersSubMenu() {
-        return !(this.selectedDoc?.currentFilter instanceof Doc) ? (null) : <div className="propertiesView-filters">
-            <div className="propertiesView-filters-title"
-                onPointerDown={action(() => this.openFilters = !this.openFilters)}
-                style={{ backgroundColor: this.openFilters ? "black" : "" }}>
-                Filters
-                <div className="propertiesView-filters-title-icon">
-                    <FontAwesomeIcon icon={this.openFilters ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return !(this.selectedDoc?.currentFilter instanceof Doc) ? null : (
+            <div className="propertiesView-filters">
+                <div className="propertiesView-filters-title" onPointerDown={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
+                    Filters
+                    <div className="propertiesView-filters-title-icon">
+                        <FontAwesomeIcon icon={this.openFilters ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
-            </div>
-            {
-                !this.openFilters ? (null) :
+                {!this.openFilters ? null : (
                     <div className="propertiesView-filters-content" style={{ height: this.selectedDoc.currentFilter[HeightSym]() + 15 }}>
                         <DocumentView
                             Document={this.selectedDoc.currentFilter}
@@ -1014,110 +1230,106 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             dontCenter="y"
                         />
                     </div>
-            }
-        </div >;
+                )}
+            </div>
+        );
     }
 
     @computed get inkSubMenu() {
-        return <>
-            {!this.isInk ? (null) :
-                <div className="propertiesView-appearance">
-                    <div className="propertiesView-appearance-title"
-                        onPointerDown={action(() => this.openAppearance = !this.openAppearance)}
-                        style={{ backgroundColor: this.openAppearance ? "black" : "" }}>
-                        Appearance
-                        <div className="propertiesView-appearance-title-icon">
-                            <FontAwesomeIcon icon={this.openAppearance ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <>
+                {!this.isInk ? null : (
+                    <div className="propertiesView-appearance">
+                        <div className="propertiesView-appearance-title" onPointerDown={action(() => (this.openAppearance = !this.openAppearance))} style={{ backgroundColor: this.openAppearance ? 'black' : '' }}>
+                            Appearance
+                            <div className="propertiesView-appearance-title-icon">
+                                <FontAwesomeIcon icon={this.openAppearance ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                            </div>
                         </div>
+                        {!this.openAppearance ? null : <div className="propertiesView-appearance-content">{this.appearanceEditor}</div>}
                     </div>
-                    {!this.openAppearance ? (null) :
-                        <div className="propertiesView-appearance-content">
-                            {this.appearanceEditor}
-                        </div>}
-                </div>}
-
-            {this.isInk ? <div className="propertiesView-transform">
-                <div className="propertiesView-transform-title"
-                    onPointerDown={action(() => this.openTransform = !this.openTransform)}
-                    style={{ backgroundColor: this.openTransform ? "black" : "" }}>
-                    Transform
-                    <div className="propertiesView-transform-title-icon">
-                        <FontAwesomeIcon icon={this.openTransform ? "caret-down" : "caret-right"} size="lg" color="white" />
+                )}
+
+                {this.isInk ? (
+                    <div className="propertiesView-transform">
+                        <div className="propertiesView-transform-title" onPointerDown={action(() => (this.openTransform = !this.openTransform))} style={{ backgroundColor: this.openTransform ? 'black' : '' }}>
+                            Transform
+                            <div className="propertiesView-transform-title-icon">
+                                <FontAwesomeIcon icon={this.openTransform ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                            </div>
+                        </div>
+                        {this.openTransform ? <div className="propertiesView-transform-content">{this.transformEditor}</div> : null}
                     </div>
-                </div>
-                {this.openTransform ? <div className="propertiesView-transform-content">
-                    {this.transformEditor}
-                </div> : null}
-            </div> : null}
-        </>;
+                ) : null}
+            </>
+        );
     }
 
     @computed get fieldsSubMenu() {
-        return <div className="propertiesView-fields">
-            <div className="propertiesView-fields-title"
-                onPointerDown={action(() => this.openFields = !this.openFields)}
-                style={{ backgroundColor: this.openFields ? "black" : "" }}>
-                Fields {"&"} Tags
-                <div className="propertiesView-fields-title-icon">
-                    <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-fields">
+                <div className="propertiesView-fields-title" onPointerDown={action(() => (this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}>
+                    Fields {'&'} Tags
+                    <div className="propertiesView-fields-title-icon">
+                        <FontAwesomeIcon icon={this.openFields ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {!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.noviceMode ? this.noviceFields : this.expandedField}</div>}
             </div>
-            {!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.noviceMode ? this.noviceFields : this.expandedField}
-                </div>}
-        </div>;
+        );
     }
 
     @computed get contextsSubMenu() {
-        return <div className="propertiesView-contexts">
-            <div className="propertiesView-contexts-title"
-                onPointerDown={action(() => this.openContexts = !this.openContexts)}
-                style={{ backgroundColor: this.openContexts ? "black" : "" }}>
-                Other Contexts
-                <div className="propertiesView-contexts-title-icon">
-                    <FontAwesomeIcon icon={this.openContexts ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-contexts">
+                <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}>
+                    Other Contexts
+                    <div className="propertiesView-contexts-title-icon">
+                        <FontAwesomeIcon icon={this.openContexts ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {this.openContexts ? <div className="propertiesView-contexts-content">{this.contexts}</div> : null}
             </div>
-            {this.openContexts ? <div className="propertiesView-contexts-content"  >{this.contexts}</div> : null}
-        </div>;
+        );
     }
 
     @computed get linksSubMenu() {
-        return <div className="propertiesView-contexts">
-            <div className="propertiesView-contexts-title"
-                onPointerDown={action(() => this.openLinks = !this.openLinks)}
-                style={{ backgroundColor: this.openLinks ? "black" : "" }}>
-                Linked To
-                <div className="propertiesView-contexts-title-icon">
-                    <FontAwesomeIcon icon={this.openLinks ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-contexts">
+                <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
+                    Linked To
+                    <div className="propertiesView-contexts-title-icon">
+                        <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {this.openLinks ? <div className="propertiesView-contexts-content">{this.links}</div> : null}
             </div>
-            {this.openLinks ? <div className="propertiesView-contexts-content"  >{this.links}</div> : null}
-        </div>;
+        );
     }
 
     @computed get layoutSubMenu() {
-        return <div className="propertiesView-layout">
-            <div className="propertiesView-layout-title"
-                onPointerDown={action(() => this.openLayout = !this.openLayout)}
-                style={{ backgroundColor: this.openLayout ? "black" : "" }}>
-                Layout
-                <div className="propertiesView-layout-title-icon">
-                    <FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-layout">
+                <div className="propertiesView-layout-title" onPointerDown={action(() => (this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}>
+                    Layout
+                    <div className="propertiesView-layout-title-icon">
+                        <FontAwesomeIcon icon={this.openLayout ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {this.openLayout ? <div className="propertiesView-layout-content">{this.layoutPreview}</div> : null}
             </div>
-            {this.openLayout ? <div className="propertiesView-layout-content"  >{this.layoutPreview}</div> : null}
-        </div>;
+        );
     }
 
     @observable description = Field.toString(LinkManager.currentLink?.description as any as Field);
     @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship);
-    @observable private relationshipButtonColor: string = "";
+    @observable private relationshipButtonColor: string = '';
 
     // @action
     // handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; }
@@ -1159,11 +1371,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes);
             const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
 
-            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color 
+            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
             if (!linkRelationshipList?.includes(value)) {
                 linkRelationshipList.push(value);
                 linkRelationshipSizes.push(1);
-                const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")";
+                const randColor = 'rgb(' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')';
                 linkColorList.push(randColor);
                 // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes
             } else if (linkRelationshipList && value !== prevRelationship) {
@@ -1171,20 +1383,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 //increment size of new relationship size
                 if (index !== -1 && index < linkRelationshipSizes.length) {
                     const pvalue = linkRelationshipSizes[index];
-                    linkRelationshipSizes[index] = (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1);
+                    linkRelationshipSizes[index] = pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1;
                 }
                 //decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation)
                 if (linkRelationshipList.includes(prevRelationship)) {
                     const pindex = linkRelationshipList.indexOf(prevRelationship);
                     if (pindex !== -1 && pindex < linkRelationshipSizes.length) {
                         const pvalue = linkRelationshipSizes[pindex];
-                        linkRelationshipSizes[pindex] = Math.max(0, (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1));
+                        linkRelationshipSizes[pindex] = Math.max(0, pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1);
                     }
                 }
-
             }
-            this.relationshipButtonColor = "rgb(62, 133, 55)";
-            setTimeout(action(() => this.relationshipButtonColor = ""), 750);
+            this.relationshipButtonColor = 'rgb(62, 133, 55)';
+            setTimeout(
+                action(() => (this.relationshipButtonColor = '')),
+                750
+            );
             return true;
         }
     });
@@ -1200,68 +1414,72 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     onSelectOutDesc = () => {
         this.setDescripValue(this.description);
         document.getElementById('link_description_input')?.blur();
-    }
+    };
 
     onDescriptionKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             this.setDescripValue(this.description);
             document.getElementById('link_description_input')?.blur();
         }
-    }
+    };
 
     onSelectOutRelationship = () => {
         this.setLinkRelationshipValue(this.relationship);
         document.getElementById('link_relationship_input')?.blur();
-    }
+    };
 
     onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             this.setLinkRelationshipValue(this.relationship);
             document.getElementById('link_relationship_input')?.blur();
         }
-    }
+    };
 
     toggleAnchor = (e: React.PointerEvent) => {
         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 && (this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow))));
-    }
+    };
 
     toggleZoomToTarget1 = (e: React.PointerEvent) => {
         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(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor2).followLinkZoom = !DocCast(this.selectedDoc.anchor2).followLinkZoom))));
-    }
+    };
 
     @computed
     get editRelationship() {
-        return <input
-            autoComplete={"off"}
-            id="link_relationship_input"
-            value={StrCast(this.selectedDoc?.linkRelationship)}
-            onKeyDown={this.onRelationshipKey}
-            onBlur={this.onSelectOutRelationship}
-            onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
-            className="text"
-            type="text"
-        />;
+        return (
+            <input
+                autoComplete={'off'}
+                id="link_relationship_input"
+                value={StrCast(this.selectedDoc?.linkRelationship)}
+                onKeyDown={this.onRelationshipKey}
+                onBlur={this.onSelectOutRelationship}
+                onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
+                className="text"
+                type="text"
+            />
+        );
     }
 
     @computed
     get editDescription() {
-        return <input
-            autoComplete={"off"}
-            id="link_description_input"
-            value={StrCast(this.selectedDoc?.description)}
-            onKeyDown={this.onDescriptionKey}
-            onBlur={this.onSelectOutDesc}
-            onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
-            className="text"
-            type="text"
-        />;
+        return (
+            <input
+                autoComplete={'off'}
+                id="link_description_input"
+                value={StrCast(this.selectedDoc?.description)}
+                onKeyDown={this.onDescriptionKey}
+                onBlur={this.onSelectOutDesc}
+                onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
+                className="text"
+                type="text"
+            />
+        );
     }
 
     /**
@@ -1278,123 +1496,120 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     render() {
         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 }}>
-                    No Document Selected
+            return (
+                <div className="propertiesView" style={{ width: this.props.width }}>
+                    <div className="propertiesView-title" style={{ width: this.props.width }}>
+                        No Document Selected
+                    </div>
                 </div>
-            </div>;
-
+            );
         } else {
             if (this.selectedDoc && this.isLink) {
-                return <div className="propertiesView">
-                    <div className="propertiesView-title">
-                        Linking
-                    </div>
-                    <div className="propertiesView-section">
-                        <p className="propertiesView-label">Information</p>
-                        <div className="propertiesView-input first">
-                            <p>Link Relationship</p>
-                            {this.editRelationship}
-                        </div>
-                        <div className="propertiesView-input">
-                            <p>Description</p>
-                            {this.editDescription}
-                        </div>
-                    </div>
-                    <div className="propertiesView-section">
-                        <p className="propertiesView-label">Behavior</p>
-                        <div className="propertiesView-input inline first">
-                            <p>Follow</p>
-                            <select
-                                name="selectList"
-                                id="selectList"
-                                onChange={e => this.changeFollowBehavior(e.currentTarget.value)}
-                                value={StrCast(this.selectedDoc.followLinkLocation, "default")}>
-                                <option value="default">Default</option>
-                                <option value="add:left">Open in new left pane</option>
-                                <option value="add:right">Open in new right pane</option>
-                                <option value="replace:left">Replace left tab</option>
-                                <option value="replace:right">Replace right tab</option>
-                                <option value="fullScreen">Open full screen</option>
-                                <option value="add">Open in new tab</option>
-                                <option value="replace">Replace current tab</option>
-                                {this.selectedDoc.linksToAnnotation
-                                    ? <option value="openExternal">Open in external page</option>
-                                    : null}
-                            </select>
-                        </div>
-                        <div className="propertiesView-input inline">
-                            <p>Auto-move anchor</p>
-                            <button
-                                style={{ background: this.selectedDoc.hidden ? "gray" : !this.selectedDoc.linkAutoMove ? "" : "#4476f7", borderRadius: 3 }}
-                                onPointerDown={this.toggleAnchor} onClick={e => e.stopPropagation()}
-                                className="propertiesButton"
-                            >
-                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                            </button>
-                        </div>
-                        <div className="propertiesView-input inline">
-                            <p>Display arrow</p>
-                            <button
-                                style={{ background: this.selectedDoc.hidden ? "gray" : !this.selectedDoc.displayArrow ? "" : "#4476f7", borderRadius: 3 }}
-                                onPointerDown={this.toggleArrow} onClick={e => e.stopPropagation()}
-                                className="propertiesButton"
-                            >
-                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                            </button>
-                        </div>
-                        <div className="propertiesView-input inline">
-                            <p>Zoom to target</p>
-                            <button
-                                style={{ background: this.selectedDoc.hidden ? "gray" : !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom ? "" : "#4476f7", borderRadius: 3 }}
-                                onPointerDown={this.toggleZoomToTarget1} onClick={e => e.stopPropagation()}
-                                className="propertiesButton"
-                            >
-                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                            </button>
+                return (
+                    <div className="propertiesView">
+                        <div className="propertiesView-title">Linking</div>
+                        <div className="propertiesView-section">
+                            <p className="propertiesView-label">Information</p>
+                            <div className="propertiesView-input first">
+                                <p>Link Relationship</p>
+                                {this.editRelationship}
+                            </div>
+                            <div className="propertiesView-input">
+                                <p>Description</p>
+                                {this.editDescription}
+                            </div>
                         </div>
-                        <div className="propertiesView-input inline">
-                            <p>Zoom to source</p>
-                            <button
-                                style={{ background: this.selectedDoc.hidden ? "gray" : !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom ? "" : "#4476f7", borderRadius: 3 }}
-                                onPointerDown={this.toggleZoomToTarget2} onClick={e => e.stopPropagation()}
-                                className="propertiesButton"
-                            >
-                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                            </button>
+                        <div className="propertiesView-section">
+                            <p className="propertiesView-label">Behavior</p>
+                            <div className="propertiesView-input inline first">
+                                <p>Follow</p>
+                                <select name="selectList" id="selectList" onChange={e => this.changeFollowBehavior(e.currentTarget.value)} value={StrCast(this.selectedDoc.followLinkLocation, 'default')}>
+                                    <option value="default">Default</option>
+                                    <option value="add:left">Open in new left pane</option>
+                                    <option value="add:right">Open in new right pane</option>
+                                    <option value="replace:left">Replace left tab</option>
+                                    <option value="replace:right">Replace right tab</option>
+                                    <option value="fullScreen">Open full screen</option>
+                                    <option value="add">Open in new tab</option>
+                                    <option value="replace">Replace current tab</option>
+                                    {this.selectedDoc.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
+                                </select>
+                            </div>
+                            <div className="propertiesView-input inline">
+                                <p>Auto-move anchor</p>
+                                <button
+                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !this.selectedDoc.linkAutoMove ? '' : '#4476f7', borderRadius: 3 }}
+                                    onPointerDown={this.toggleAnchor}
+                                    onClick={e => e.stopPropagation()}
+                                    className="propertiesButton">
+                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                </button>
+                            </div>
+                            <div className="propertiesView-input inline">
+                                <p>Display arrow</p>
+                                <button
+                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !this.selectedDoc.displayArrow ? '' : '#4476f7', borderRadius: 3 }}
+                                    onPointerDown={this.toggleArrow}
+                                    onClick={e => e.stopPropagation()}
+                                    className="propertiesButton">
+                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                </button>
+                            </div>
+                            <div className="propertiesView-input inline">
+                                <p>Zoom to target</p>
+                                <button
+                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+                                    onPointerDown={this.toggleZoomToTarget1}
+                                    onClick={e => e.stopPropagation()}
+                                    className="propertiesButton">
+                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                </button>
+                            </div>
+                            <div className="propertiesView-input inline">
+                                <p>Zoom to source</p>
+                                <button
+                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+                                    onPointerDown={this.toggleZoomToTarget2}
+                                    onClick={e => e.stopPropagation()}
+                                    className="propertiesButton">
+                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                </button>
+                            </div>
                         </div>
                     </div>
-                </div >;
+                );
             }
             if (this.selectedDoc && !this.isPres) {
-                return <div className="propertiesView" style={{
-                    width: this.props.width,
-                    minWidth: this.props.width,
-                    //overflowY: this.scrolling ? "scroll" : "visible"
-                }} >
-                    <div className="propertiesView-title" style={{ width: this.props.width }}>
-                        Properties
-                    </div>
-                    <div className="propertiesView-name">
-                        {this.editableTitle}
-                    </div>
+                return (
+                    <div
+                        className="propertiesView"
+                        style={{
+                            width: this.props.width,
+                            minWidth: this.props.width,
+                            //overflowY: this.scrolling ? "scroll" : "visible"
+                        }}>
+                        <div className="propertiesView-title" style={{ width: this.props.width }}>
+                            Properties
+                        </div>
+                        <div className="propertiesView-name">{this.editableTitle}</div>
 
-                    {this.contextsSubMenu}
+                        {this.contextsSubMenu}
 
-                    {this.linksSubMenu}
+                        {this.linksSubMenu}
 
-                    {this.inkSubMenu}
+                        {this.inkSubMenu}
 
-                    {this.optionsSubMenu}
+                        {this.optionsSubMenu}
 
-                    {this.fieldsSubMenu}
+                        {this.fieldsSubMenu}
 
-                    {isNovice ? null : this.sharingSubMenu}
+                        {isNovice ? null : this.sharingSubMenu}
 
-                    {isNovice ? null : this.filtersSubMenu}
+                        {isNovice ? null : this.filtersSubMenu}
 
-                    {isNovice ? null : this.layoutSubMenu}
-                </div>;
+                        {isNovice ? null : this.layoutSubMenu}
+                    </div>
+                );
             }
             if (this.isPres) {
                 const selectedItem: boolean = PresBox.Instance?._selectedArray.size > 0;
@@ -1402,35 +1617,35 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 const viewType = PresBox.Instance.activeItem?._viewType;
                 const pannable: boolean = (type === DocumentType.COL && viewType === CollectionViewType.Freeform) || type === DocumentType.IMG;
                 const scrollable: boolean = type === DocumentType.PDF || type === DocumentType.WEB || type === DocumentType.RTF || viewType === CollectionViewType.Stacking;
-                return <div className="propertiesView" style={{ width: this.props.width }}>
-                    <div className="propertiesView-title" style={{ width: this.props.width }}>
-                        Presentation
-                    </div>
-                    <div className="propertiesView-name" style={{ borderBottom: 0 }}>
-                        {this.editableTitle}
-                        <div className="propertiesView-presSelected">
-                            <div className="propertiesView-selectedCount">
-                                {PresBox.Instance?._selectedArray.size} selected
-                            </div>
-                            <div className="propertiesView-selectedList">
-                                {PresBox.Instance?.listOfSelected}
-                            </div>
+                return (
+                    <div className="propertiesView" style={{ width: this.props.width }}>
+                        <div className="propertiesView-title" style={{ width: this.props.width }}>
+                            Presentation
                         </div>
-                    </div>
-                    {!selectedItem ? (null) : <div className="propertiesView-presTrails">
-                        <div className="propertiesView-presTrails-title"
-                            onPointerDown={action(() => { this.openPresTransitions = !this.openPresTransitions; })}
-                            style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}>
-                            &nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={"rocket"} /> &nbsp; Transitions
-                            <div className="propertiesView-presTrails-title-icon">
-                                <FontAwesomeIcon icon={this.openPresTransitions ? "caret-down" : "caret-right"} size="lg" color="white" />
+                        <div className="propertiesView-name" style={{ borderBottom: 0 }}>
+                            {this.editableTitle}
+                            <div className="propertiesView-presSelected">
+                                <div className="propertiesView-selectedCount">{PresBox.Instance?._selectedArray.size} selected</div>
+                                <div className="propertiesView-selectedList">{PresBox.Instance?.listOfSelected}</div>
                             </div>
                         </div>
-                        {this.openPresTransitions ? <div className="propertiesView-presTrails-content">
-                            {PresBox.Instance.transitionDropdown}
-                        </div> : null}
-                    </div>}
-                    {/* {!selectedItem || type === DocumentType.VID || type === DocumentType.AUDIO ? (null) : <div className="propertiesView-presTrails">
+                        {!selectedItem ? null : (
+                            <div className="propertiesView-presTrails">
+                                <div
+                                    className="propertiesView-presTrails-title"
+                                    onPointerDown={action(() => {
+                                        this.openPresTransitions = !this.openPresTransitions;
+                                    })}
+                                    style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
+                                    &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Transitions
+                                    <div className="propertiesView-presTrails-title-icon">
+                                        <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                                    </div>
+                                </div>
+                                {this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
+                            </div>
+                        )}
+                        {/* {!selectedItem || type === DocumentType.VID || type === DocumentType.AUDIO ? (null) : <div className="propertiesView-presTrails">
                         <div className="propertiesView-presTrails-title"
                             onPointerDown={action(() => this.openPresProgressivize = !this.openPresProgressivize)}
                             style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>
@@ -1443,20 +1658,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             {PresBox.Instance.progressivizeDropdown}
                         </div> : null}
                     </div>} */}
-                    {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? (null) : <div className="propertiesView-presTrails">
-                        <div className="propertiesView-presTrails-title"
-                            onPointerDown={action(() => { this.openSlideOptions = !this.openSlideOptions; })}
-                            style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}>
-                            &nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={type === DocumentType.AUDIO ? "file-audio" : "file-video"} /> &nbsp; {type === DocumentType.AUDIO ? "Audio Options" : "Video Options"}
-                            <div className="propertiesView-presTrails-title-icon">
-                                <FontAwesomeIcon icon={this.openSlideOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
+                        {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? null : (
+                            <div className="propertiesView-presTrails">
+                                <div
+                                    className="propertiesView-presTrails-title"
+                                    onPointerDown={action(() => {
+                                        this.openSlideOptions = !this.openSlideOptions;
+                                    })}
+                                    style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
+                                    &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
+                                    <div className="propertiesView-presTrails-title-icon">
+                                        <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                                    </div>
+                                </div>
+                                {this.openSlideOptions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
                             </div>
-                        </div>
-                        {this.openSlideOptions ? <div className="propertiesView-presTrails-content">
-                            {PresBox.Instance.mediaOptionsDropdown}
-                        </div> : null}
-                    </div>}
-                    {/* <div className="propertiesView-presTrails">
+                        )}
+                        {/* <div className="propertiesView-presTrails">
                         <div className="propertiesView-presTrails-title"
                             onPointerDown={action(() => { this.openAddSlide = !this.openAddSlide; })}
                             style={{ backgroundColor: this.openAddSlide ? "black" : "" }}>
@@ -1469,8 +1687,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             {PresBox.Instance.newDocumentDropdown}
                         </div> : null}
                     </div> */}
-                </div>;
+                    </div>
+                );
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 8f73ac2c3..e81a9c40f 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -1,22 +1,20 @@
 import { computed } from 'mobx';
-import { observer } from "mobx-react";
-import { Doc, DocListCast, StrListCast, Opt } from "../../fields/Doc";
+import { observer } from 'mobx-react';
+import { Doc, DocListCast, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast, StrCast } from '../../fields/Types';
 import { emptyFunction, OmitKeys, returnAll, returnOne, returnTrue, returnZero } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { Transform } from '../util/Transform';
 import { CollectionStackingView } from './collections/CollectionStackingView';
-import { CollectionViewType } from './collections/CollectionView';
 import { FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { SearchBox } from './search/SearchBox';
-import "./SidebarAnnos.scss";
+import './SidebarAnnos.scss';
 import { StyleProp } from './StyleProvider';
-import React = require("react");
-import { DocumentViewProps } from './nodes/DocumentView';
-import { DocumentType } from '../documents/DocumentTypes';
+import React = require('react');
 
 interface ExtraProps {
     fieldKey: string;
@@ -28,8 +26,8 @@ interface ExtraProps {
     nativeWidth: number;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     ScreenToLocalTransform: () => Transform;
-    sidebarAddDocument: (doc: (Doc | Doc[]), suffix: string) => boolean;
-    removeDocument: (doc: (Doc | Doc[]), suffix: string) => boolean;
+    sidebarAddDocument: (doc: Doc | Doc[], suffix: string) => boolean;
+    removeDocument: (doc: Doc | Doc[], suffix: string) => boolean;
     moveDocument: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean, annotationKey?: string) => boolean;
 }
 @observer
@@ -42,29 +40,41 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     @computed get allMetadata() {
         const keys = new Set<string>();
         DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key)));
-        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] !== "_" && (key[0] === key[0].toUpperCase())).sort();
+        return Array.from(keys.keys())
+            .filter(key => key[0])
+            .filter(key => key[0] !== '_' && key[0] === key[0].toUpperCase())
+            .sort();
     }
     @computed get allUsers() {
         const keys = new Set<string>();
         DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
         return Array.from(keys.keys()).sort();
     }
-    get filtersKey() { return "_" + this.sidebarKey + "-docFilters"; }
+    get filtersKey() {
+        return '_' + this.sidebarKey + '-docFilters';
+    }
 
     anchorMenuClick = (anchor: Doc) => {
-        const startup = StrListCast(this.props.rootDoc.docFilters).map(filter => filter.split(":")[0]).join(" ");
+        const startup = StrListCast(this.props.rootDoc.docFilters)
+            .map(filter => filter.split(':')[0])
+            .join(' ');
         const target = Docs.Create.TextDocument(startup, {
-            title: "-note-",
-            annotationOn: this.props.rootDoc, _width: 200, _height: 50, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
-            _fontFamily: StrCast(Doc.UserDoc().fontFamily)
+            title: '-note-',
+            annotationOn: this.props.rootDoc,
+            _width: 200,
+            _height: 50,
+            _fitWidth: true,
+            _autoHeight: true,
+            _fontSize: StrCast(Doc.UserDoc().fontSize),
+            _fontFamily: StrCast(Doc.UserDoc().fontFamily),
         });
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
-        this.allMetadata.map(tag => target[tag] = tag);
-        DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline comment:comment on");
+        this.allMetadata.map(tag => (target[tag] = tag));
+        DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on');
         this.addDocument(target);
         this._stackRef.current?.focusDocument(target);
-    }
+    };
     makeDocUnfiltered = (doc: Doc) => {
         if (DocListCast(this.props.rootDoc[this.sidebarKey]).includes(doc)) {
             if (this.props.layoutDoc[this.filtersKey]) {
@@ -73,65 +83,84 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             return true;
         }
         return false;
-    }
+    };
 
-    get sidebarKey() { return this.props.fieldKey + "-sidebar"; }
+    get sidebarKey() {
+        return this.props.fieldKey + '-sidebar';
+    }
     filtersHeight = () => 38;
-    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(Doc.NativeWidth(this.props.dataDoc), 0).scale(this.props.scaling?.() || 1);
+    screenToLocalTransform = () =>
+        this.props
+            .ScreenToLocalTransform()
+            .translate(Doc.NativeWidth(this.props.dataDoc), 0)
+            .scale(this.props.scaling?.() || 1);
     // panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 :
     //     this.props.usePanelWidth ? this.props.PanelWidth() :
     //         (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth);
-    panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF || this.props.layoutDoc.type === DocumentType.MAP ? this.props.PanelWidth() : (NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.nativeWidth);
+    panelWidth = () =>
+        !this.props.showSidebar
+            ? 0
+            : this.props.layoutDoc.type === DocumentType.RTF || this.props.layoutDoc.type === DocumentType.MAP
+            ? this.props.PanelWidth()
+            : ((NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth()) / NumCast(this.props.nativeWidth);
     panelHeight = () => this.props.PanelHeight() - this.filtersHeight();
     addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey);
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
-    showTitle = () => "title";
+    showTitle = () => 'title';
     setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
     render() {
         const renderTag = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`);
-            return <div key={tag} className={`sidebarAnnos-filterTag${active ? "-active" : ""}`}
-                onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, "check", true, this.sidebarKey, e.shiftKey)}>
-                {tag}
-            </div>;
+            return (
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, 'check', true, this.sidebarKey, e.shiftKey)}>
+                    {tag}
+                </div>
+            );
         };
         const renderMeta = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:exists`);
-            return <div key={tag} className={`sidebarAnnos-filterTag${active ? "-active" : ""}`}
-                onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, "exists", true, this.sidebarKey, e.shiftKey)}>
-                {tag}
-            </div>;
+            return (
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, 'exists', true, this.sidebarKey, e.shiftKey)}>
+                    {tag}
+                </div>
+            );
         };
         const renderUsers = (user: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`author:${user}:check`);
-            return <div key={user} className={`sidebarAnnos-filterUser${active ? "-active" : ""}`}
-                onClick={e => Doc.setDocFilter(this.props.rootDoc, "author", user, "check", true, this.sidebarKey, e.shiftKey)}>
-                {user}
-            </div>;
+            return (
+                <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, 'author', user, 'check', true, this.sidebarKey, e.shiftKey)}>
+                    {user}
+                </div>
+            );
         };
         // TODO: Calculation of the topbar is hardcoded and different for text nodes - it should all be the same and all be part of SidebarAnnos
-        return !this.props.showSidebar ? (null) :
-            <div style={{
-                position: "absolute", pointerEvents: this.props.isContentActive() ? "all" : undefined, top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._showTitle) === "title" ? 15 : 0, right: 0,
-                background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor),
-                width: `${this.panelWidth()}px`,
-                height: "100%"
-            }}>
-                <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight(), width: this.panelWidth() }}
-                    onWheel={e => e.stopPropagation()}>
+        return !this.props.showSidebar ? null : (
+            <div
+                style={{
+                    position: 'absolute',
+                    pointerEvents: this.props.isContentActive() ? 'all' : undefined,
+                    top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._showTitle) === 'title' ? 15 : 0,
+                    right: 0,
+                    background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor),
+                    width: `${this.panelWidth()}px`,
+                    height: '100%',
+                }}>
+                <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight(), width: this.panelWidth() }} onWheel={e => e.stopPropagation()}>
                     {this.allUsers.map(renderUsers)}
                     {this.allMetadata.map(renderMeta)}
                 </div>
-                <div style={{ width: "100%", height: this.panelHeight(), position: "relative" }}>
-                    <CollectionStackingView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} ref={this._stackRef}
+                <div style={{ width: '100%', height: this.panelHeight(), position: 'relative' }}>
+                    <CollectionStackingView
+                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                        ref={this._stackRef}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
                         PanelHeight={this.panelHeight}
                         PanelWidth={this.panelWidth}
                         docFilters={this.docFilters}
-                        scaleField={this.sidebarKey + "-scale"}
+                        scaleField={this.sidebarKey + '-scale'}
                         setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
                         select={emptyFunction}
@@ -151,6 +180,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         pointerEvents={returnAll}
                     />
                 </div>
-            </div>;
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index a9770d253..340a5df45 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -2,18 +2,13 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, runInAction } from 'mobx';
 import { extname } from 'path';
-import { Doc, Opt, StrListCast } from '../../fields/Doc';
-import { List } from '../../fields/List';
-import { listSpec } from '../../fields/Schema';
+import { Doc, Opt } from '../../fields/Doc';
 import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types';
 import { DashColor, lightOrDark } from '../../Utils';
-import { DocumentType } from '../documents/DocumentTypes';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocFocusOrOpen } from '../util/DocumentManager';
 import { ColorScheme } from '../util/SettingsManager';
-import { SnappingManager } from '../util/SnappingManager';
 import { undoBatch, UndoManager } from '../util/UndoManager';
-import { CollectionViewType } from './collections/CollectionView';
 import { TreeSort } from './collections/TreeView';
 import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
@@ -49,7 +44,7 @@ export enum StyleProp {
 }
 
 function darkScheme() {
-    return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark;
+    return Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark;
 }
 
 function toggleLockedPosition(doc: Doc) {
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 689ee4fc1..156513f47 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,43 +1,42 @@
-import { action, computed, observable, ObservableSet, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { ScriptField } from "../../fields/ScriptField";
-import { Cast, StrCast } from "../../fields/Types";
-import { TraceMobx } from "../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
-import { Docs, DocUtils } from "../documents/Documents";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
-import { Transform } from "../util/Transform";
-import { undoBatch } from "../util/UndoManager";
-import { CollectionTreeView } from "./collections/CollectionTreeView";
-import { DocumentView } from "./nodes/DocumentView";
-import { DefaultStyleProvider } from "./StyleProvider";
+import { action, computed, observable, ObservableSet, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, StrCast } from '../../fields/Types';
+import { TraceMobx } from '../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils';
+import { Docs, DocUtils } from '../documents/Documents';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { Transform } from '../util/Transform';
+import { undoBatch } from '../util/UndoManager';
+import { CollectionTreeView } from './collections/CollectionTreeView';
+import { DocumentView } from './nodes/DocumentView';
+import { DefaultStyleProvider } from './StyleProvider';
 import './TemplateMenu.scss';
-import React = require("react");
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import React = require('react');
 
 @observer
-class TemplateToggle extends React.Component<{ template: string, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
+class TemplateToggle extends React.Component<{ template: string; checked: boolean; toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
     render() {
         if (this.props.template) {
             return (
                 <li className="templateToggle">
-                    <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
+                    <input type="checkbox" checked={this.props.checked} onChange={event => this.props.toggle(event, this.props.template)} />
                     {this.props.template}
                 </li>
             );
         } else {
-            return (null);
+            return null;
         }
     }
 }
 @observer
-class OtherToggle extends React.Component<{ checked: boolean, name: string, toggle: (event: React.ChangeEvent<HTMLInputElement>) => void }> {
+class OtherToggle extends React.Component<{ checked: boolean; name: string; toggle: (event: React.ChangeEvent<HTMLInputElement>) => void }> {
     render() {
         return (
             <li className="chromeToggle">
-                <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event)} />
+                <input type="checkbox" checked={this.props.checked} onChange={event => this.props.toggle(event)} />
                 {this.props.name}
             </li>
         );
@@ -49,7 +48,6 @@ export interface TemplateMenuProps {
     templates?: Map<string, boolean>;
 }
 
-
 @observer
 export class TemplateMenu extends React.Component<TemplateMenuProps> {
     _addedKeys = new ObservableSet();
@@ -58,109 +56,118 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
     toggleLayout = (e: React.ChangeEvent<HTMLInputElement>, layout: string): void => {
         this.props.docViews.map(dv => dv.switchViews(e.target.checked, layout));
-    }
+    };
     toggleDefault = (e: React.ChangeEvent<HTMLInputElement>): void => {
-        this.props.docViews.map(dv => dv.switchViews(false, "layout"));
-    }
+        this.props.docViews.map(dv => dv.switchViews(false, 'layout'));
+    };
 
     toggleAudio = (e: React.ChangeEvent<HTMLInputElement>): void => {
-        this.props.docViews.map(dv => dv.props.Document._showAudio = e.target.checked);
-    }
+        this.props.docViews.map(dv => (dv.props.Document._showAudio = e.target.checked));
+    };
 
     @undoBatch
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: string): void => {
-        this.props.docViews.forEach(d => Doc.Layout(d.layoutDoc)["_show" + template] = event.target.checked ? template.toLowerCase() : "");
-    }
+        this.props.docViews.forEach(d => (Doc.Layout(d.layoutDoc)['_show' + template] = event.target.checked ? template.toLowerCase() : ''));
+    };
 
     @action
     toggleTemplateActivity = (): void => {
         this._hidden = !this._hidden;
-    }
+    };
 
     @undoBatch
     @action
     toggleChrome = (): void => {
-        this.props.docViews.map(dv => Doc.Layout(dv.layoutDoc)).forEach(layout => layout._chromeHidden = !layout._chromeHidden);
-    }
+        this.props.docViews.map(dv => Doc.Layout(dv.layoutDoc)).forEach(layout => (layout._chromeHidden = !layout._chromeHidden));
+    };
 
     // todo: add brushes to brushMap to save with a style name
     onCustomKeypress = (e: React.KeyboardEvent) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             runInAction(() => this._addedKeys.add(this._customRef.current!.value));
         }
-    }
+    };
     componentDidMount() {
         !this._addedKeys && (this._addedKeys = new ObservableSet());
-        Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].props.Document))).
-            filter(key => key.startsWith("layout_")).
-            map(key => runInAction(() => this._addedKeys.add(key.replace("layout_", ""))));
+        Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].props.Document)))
+            .filter(key => key.startsWith('layout_'))
+            .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', ''))));
     }
 
     return100 = () => 100;
     @computed get scriptField() {
-        const script = ScriptField.MakeScript("docs.map(d => switchView(d, this))", { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name, firstDoc: Doc.name },
-            { docs: new List<Doc>(this.props.docViews.map(dv => dv.props.Document)) });
+        const script = ScriptField.MakeScript(
+            'docs.map(d => switchView(d, this))',
+            { this: Doc.name, heading: 'string', checked: 'string', containingTreeView: Doc.name, firstDoc: Doc.name },
+            { docs: new List<Doc>(this.props.docViews.map(dv => dv.props.Document)) }
+        );
         return script ? () => script : undefined;
     }
     templateIsUsed = (selDoc: Doc, templateDoc: Doc) => {
         const template = StrCast(templateDoc.dragFactory ? Cast(templateDoc.dragFactory, Doc, null)?.title : templateDoc.title);
-        return StrCast(selDoc.layoutKey) === "layout_" + template ? 'check' : 'unchecked';
-    }
+        return StrCast(selDoc.layoutKey) === 'layout_' + template ? 'check' : 'unchecked';
+    };
     render() {
         TraceMobx();
         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.noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
+        const templateName = StrCast(firstDoc.layoutKey, 'layout').replace('layout_', '');
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], 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.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.noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
-            {templateMenu}
-            {Doc.noviceMode ? (null) : <CollectionTreeView
-                Document={CurrentUserUtils.MyTemplates}
-                CollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                ContainingCollectionView={undefined}
-                styleProvider={DefaultStyleProvider}
-                setHeight={returnFalse}
-                docViewPath={returnEmptyDoclist}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                rootSelected={returnFalse}
-                onCheckedClick={this.scriptField}
-                onChildClick={this.scriptField}
-                dropAction={undefined}
-                isAnyChildContentActive={returnFalse}
-                isContentActive={returnTrue}
-                bringToFront={emptyFunction}
-                focus={emptyFunction}
-                whenChildContentsActiveChanged={emptyFunction}
-                ScreenToLocalTransform={Transform.Identity}
-                isSelected={returnFalse}
-                pinToPres={emptyFunction}
-                select={emptyFunction}
-                renderDepth={1}
-                addDocTab={returnFalse}
-                PanelWidth={this.return100}
-                PanelHeight={this.return100}
-                treeViewHideHeaderFields={true}
-                dontRegisterView={true}
-                fieldKey={"data"}
-                moveDocument={returnFalse}
-                removeDocument={returnFalse}
-                addDocument={returnFalse} />}
-        </ul>;
+        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.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.noviceMode ? null : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
+                {templateMenu}
+                {Doc.noviceMode ? null : (
+                    <CollectionTreeView
+                        Document={Doc.MyTemplates}
+                        CollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                        ContainingCollectionView={undefined}
+                        styleProvider={DefaultStyleProvider}
+                        setHeight={returnFalse}
+                        docViewPath={returnEmptyDoclist}
+                        docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
+                        rootSelected={returnFalse}
+                        onCheckedClick={this.scriptField}
+                        onChildClick={this.scriptField}
+                        dropAction={undefined}
+                        isAnyChildContentActive={returnFalse}
+                        isContentActive={returnTrue}
+                        bringToFront={emptyFunction}
+                        focus={emptyFunction}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        ScreenToLocalTransform={Transform.Identity}
+                        isSelected={returnFalse}
+                        pinToPres={emptyFunction}
+                        select={emptyFunction}
+                        renderDepth={1}
+                        addDocTab={returnFalse}
+                        PanelWidth={this.return100}
+                        PanelHeight={this.return100}
+                        treeViewHideHeaderFields={true}
+                        dontRegisterView={true}
+                        fieldKey={'data'}
+                        moveDocument={returnFalse}
+                        removeDocument={returnFalse}
+                        addDocument={returnFalse}
+                    />
+                )}
+            </ul>
+        );
     }
 }
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 27478e59b..d47dfbea0 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -12,18 +12,17 @@ import { inheritParentAcls } from '../../../fields/util';
 import { emptyFunction, incrementTitleCopy } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { DashboardView } from '../DashboardView';
 import { LightboxView } from '../LightboxView';
 import './CollectionDockingView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { CollectionViewType } from './CollectionView';
 import { TabDocView } from './TabDocView';
 import React = require('react');
 const _global = (window /* browser */ || global) /* node */ as any;
@@ -121,7 +120,7 @@ export class CollectionDockingView extends CollectionSubView() {
         SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') {
-            return CurrentUserUtils.openDashboard(doc);
+            return DashboardView.openDashboard(doc);
         }
         const newItemStackConfig = {
             type: 'stack',
@@ -170,7 +169,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(document);
+        if (document._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document);
 
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
         if (tab) {
@@ -378,7 +377,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.ActiveTool)) {
+        if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             e.stopPropagation();
         }
     };
@@ -404,7 +403,7 @@ export class CollectionDockingView extends CollectionSubView() {
             const cloned = await Doc.MakeClone(doc);
             Array.from(cloned.map.entries()).map(entry => (json = json.replace(entry[0], entry[1][Id])));
             Doc.GetProto(cloned.clone).dockingConfig = json;
-            return CurrentUserUtils.openDashboard(cloned.clone);
+            return DashboardView.openDashboard(cloned.clone);
         }
         const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
         const origtabids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) || [];
@@ -424,7 +423,7 @@ export class CollectionDockingView extends CollectionSubView() {
             return newtab;
         });
         const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
-        return CurrentUserUtils.openDashboard(await copy);
+        return DashboardView.openDashboard(await copy);
     }
 
     @action
@@ -451,8 +450,8 @@ export class CollectionDockingView extends CollectionSubView() {
 
     tabDestroyed = (tab: any) => {
         if (tab.DashDoc?.type !== DocumentType.KVP) {
-            Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, 'data', tab.DashDoc);
-            Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
+            Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
+            Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
         }
         const dview = CollectionDockingView.Instance.props.Document;
         const fieldKey = CollectionDockingView.Instance.props.fieldKey;
@@ -469,7 +468,7 @@ export class CollectionDockingView extends CollectionSubView() {
 
     stackCreated = (stack: any) => {
         stack.header?.element.on('mousedown', (e: any) => {
-            const dashboard = CurrentUserUtils.ActiveDashboard;
+            const dashboard = Doc.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([], {
@@ -507,7 +506,7 @@ 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;
+                    const dashboard = Doc.ActiveDashboard;
                     if (dashboard) {
                         dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
                         const docToAdd = Docs.Create.FreeformDocument([], {
@@ -545,7 +544,7 @@ ScriptingGlobals.add(
 );
 ScriptingGlobals.add(
     function openInOverlay(doc: any) {
-        return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
+        return Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
     },
     'opens up document in screen overlay layer',
     '(doc: any)'
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 668d82387..2c0e44bc3 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,46 +1,46 @@
-import React = require("react");
+import React = require('react');
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx";
-import { observer } from "mobx-react";
-import { ColorState } from "react-color";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { Document } from "../../../fields/documentSchemas";
-import { Id } from "../../../fields/FieldSymbols";
-import { InkTool } from "../../../fields/InkField";
-import { List } from "../../../fields/List";
-import { ObjectField } from "../../../fields/ObjectField";
-import { RichTextField } from "../../../fields/RichTextField";
-import { listSpec } from "../../../fields/Schema";
-import { ScriptField } from "../../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DragManager } from "../../util/DragManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
-import { EditableView } from "../EditableView";
-import { GestureOverlay } from "../GestureOverlay";
-import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
-import { LightboxView } from "../LightboxView";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocumentView } from "../nodes/DocumentView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
-import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
-import { PresBox } from "../nodes/trails/PresBox";
-import { DefaultStyleProvider } from "../StyleProvider";
-import { CollectionDockingView } from "./CollectionDockingView";
-import { CollectionLinearView } from "./collectionLinear";
-import "./CollectionMenu.scss";
-import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
-import { TabDocView } from "./TabDocView";
-import { Colors } from "../global/globalEnums";
+import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { ColorState } from 'react-color';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Document } from '../../../fields/documentSchemas';
+import { Id } from '../../../fields/FieldSymbols';
+import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DragManager } from '../../util/DragManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch } from '../../util/UndoManager';
+import { AntimodeMenu } from '../AntimodeMenu';
+import { EditableView } from '../EditableView';
+import { GestureOverlay } from '../GestureOverlay';
+import { Colors } from '../global/globalEnums';
+import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
+import { LightboxView } from '../LightboxView';
+import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { PresBox } from '../nodes/trails/PresBox';
+import { DefaultStyleProvider } from '../StyleProvider';
+import { CollectionDockingView } from './CollectionDockingView';
+import { CollectionLinearView } from './collectionLinear';
+import './CollectionMenu.scss';
+import { COLLECTION_BORDER_WIDTH } from './CollectionView';
+import { TabDocView } from './TabDocView';
 
 interface CollectionMenuProps {
     panelHeight: () => number;
@@ -48,7 +48,7 @@ interface CollectionMenuProps {
 }
 
 @observer
-export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
+export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
     @observable static Instance: CollectionMenu;
 
     @observable SelectedCollection: DocumentView | undefined;
@@ -58,16 +58,18 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
 
     constructor(props: any) {
         super(props);
-        this.FieldKey = "";
-        runInAction(() => CollectionMenu.Instance = this);
+        this.FieldKey = '';
+        runInAction(() => (CollectionMenu.Instance = this));
         this._canFade = false; // don't let the inking menu fade away
-        runInAction(() => this.Pinned = Cast(Doc.UserDoc()["menuCollections-pinned"], "boolean", true));
+        runInAction(() => (this.Pinned = Cast(Doc.UserDoc()['menuCollections-pinned'], 'boolean', true)));
         this.jumpTo(300, 300);
     }
 
     componentDidMount() {
-        reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0],
-            view => view && this.SetSelection(view));
+        reaction(
+            () => SelectionManager.Views().length && SelectionManager.Views()[0],
+            view => view && this.SetSelection(view)
+        );
     }
 
     @action
@@ -77,84 +79,87 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
 
     @action
     toggleMenuPin = (e: React.MouseEvent) => {
-        Doc.UserDoc()["menuCollections-pinned"] = this.Pinned = !this.Pinned;
+        Doc.UserDoc()['menuCollections-pinned'] = this.Pinned = !this.Pinned;
         if (!this.Pinned && this._left < 0) {
             this.jumpTo(300, 300);
         }
-    }
+    };
 
     @action
     toggleTopBar = () => {
-        if (CurrentUserUtils.headerBarHeight > 0) {
-            CurrentUserUtils.headerBarHeight = 0;
+        if (SettingsManager.headerBarHeight > 0) {
+            SettingsManager.headerBarHeight = 0;
         } else {
-            CurrentUserUtils.headerBarHeight = 60;
+            SettingsManager.headerBarHeight = 60;
         }
-    }
+    };
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
         return new Transform(-translateX, -translateY, 1 / scale);
-    }
+    };
 
     @computed get contMenuButtons() {
-        const selDoc = CurrentUserUtils.MyContextMenuBtns;
-        return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
-            <CollectionLinearView
-                Document={selDoc}
-                DataDoc={undefined}
-                fieldKey={"data"}
-                dropAction={"alias"}
-                setHeight={returnFalse}
-                styleProvider={DefaultStyleProvider}
-                rootSelected={returnTrue}
-                bringToFront={emptyFunction}
-                select={emptyFunction}
-                isContentActive={returnTrue}
-                isAnyChildContentActive={returnFalse}
-                isSelected={returnFalse}
-                docViewPath={returnEmptyDoclist}
-                moveDocument={returnFalse}
-                CollectionView={undefined}
-                addDocument={returnFalse}
-                addDocTab={returnFalse}
-                pinToPres={emptyFunction}
-                removeDocument={returnFalse}
-                ScreenToLocalTransform={this.buttonBarXf}
-                PanelWidth={this.props.panelWidth}
-                PanelHeight={this.props.panelHeight}
-                renderDepth={0}
-                focus={emptyFunction}
-                whenChildContentsActiveChanged={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined} />
-        </div>;
+        const selDoc = Doc.MyContextMenuBtns;
+        return !(selDoc instanceof Doc) ? null : (
+            <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }}>
+                <CollectionLinearView
+                    Document={selDoc}
+                    DataDoc={undefined}
+                    fieldKey={'data'}
+                    dropAction={'alias'}
+                    setHeight={returnFalse}
+                    styleProvider={DefaultStyleProvider}
+                    rootSelected={returnTrue}
+                    bringToFront={emptyFunction}
+                    select={emptyFunction}
+                    isContentActive={returnTrue}
+                    isAnyChildContentActive={returnFalse}
+                    isSelected={returnFalse}
+                    docViewPath={returnEmptyDoclist}
+                    moveDocument={returnFalse}
+                    CollectionView={undefined}
+                    addDocument={returnFalse}
+                    addDocTab={returnFalse}
+                    pinToPres={emptyFunction}
+                    removeDocument={returnFalse}
+                    ScreenToLocalTransform={this.buttonBarXf}
+                    PanelWidth={this.props.panelWidth}
+                    PanelHeight={this.props.panelHeight}
+                    renderDepth={0}
+                    focus={emptyFunction}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                />
+            </div>
+        );
     }
 
     render() {
+        const propIcon = SettingsManager.headerBarHeight > 0 ? 'angle-double-up' : 'angle-double-down';
+        const propTitle = SettingsManager.headerBarHeight > 0 ? 'Close Header Bar' : 'Open Header Bar';
 
-        const propIcon = CurrentUserUtils.headerBarHeight > 0 ? "angle-double-up" : "angle-double-down";
-        const propTitle = CurrentUserUtils.headerBarHeight > 0 ? "Close Header Bar" : "Open Header Bar";
-
-        const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="topar" placement="bottom">
-            <div className="collectionMenu-hardCodedButton"
-                style={{ backgroundColor: CurrentUserUtils.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }}
-                onPointerDown={this.toggleTopBar}>
-                <FontAwesomeIcon icon={propIcon} size="lg" />
-            </div>
-        </Tooltip>;
+        const prop = (
+            <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="topar" placement="bottom">
+                <div className="collectionMenu-hardCodedButton" style={{ backgroundColor: SettingsManager.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }} onPointerDown={this.toggleTopBar}>
+                    <FontAwesomeIcon icon={propIcon} size="lg" />
+                </div>
+            </Tooltip>
+        );
 
         // NEW BUTTONS
         //dash col linear view buttons
-        const contMenuButtons =
+        const contMenuButtons = (
             <div className="collectionMenu-container">
                 {this.contMenuButtons}
                 {prop}
-            </div>;
+            </div>
+        );
 
         return contMenuButtons;
 
@@ -187,49 +192,62 @@ const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
 export class CollectionViewBaseChrome extends React.Component<CollectionViewMenuProps> {
     //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
 
-    get document() { return this.props.docView?.props.Document; }
-    get target() { return this.document; }
+    get document() {
+        return this.props.docView?.props.Document;
+    }
+    get target() {
+        return this.document;
+    }
     _templateCommand = {
-        params: ["target", "source"], title: "item view",
-        script: "self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])",
+        params: ['target', 'source'],
+        title: 'item view',
+        script: 'self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])',
         immediate: undoBatch((source: Doc[]) => {
             let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
-            try { formatStr && JSON.parse(formatStr); } catch (e) { formatStr = ""; }
+            try {
+                formatStr && JSON.parse(formatStr);
+            } catch (e) {
+                formatStr = '';
+            }
             if (source.length === 1 && formatStr) {
-                Doc.SetInPlace(this.target, "childLayoutString", formatStr, false);
+                Doc.SetInPlace(this.target, 'childLayoutString', formatStr, false);
             } else if (source.length) {
                 this.target.childLayoutTemplate = Doc.getDocTemplate(source?.[0]);
             } else {
-                Doc.SetInPlace(this.target, "childLayoutString", undefined, true);
-                Doc.SetInPlace(this.target, "childLayoutTemplate", undefined, true);
+                Doc.SetInPlace(this.target, 'childLayoutString', undefined, true);
+                Doc.SetInPlace(this.target, 'childLayoutTemplate', undefined, true);
             }
         }),
         initialize: emptyFunction,
     };
     _narrativeCommand = {
-        params: ["target", "source"], title: "child click view",
-        script: "self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])",
+        params: ['target', 'source'],
+        title: 'child click view',
+        script: 'self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])',
         immediate: undoBatch((source: Doc[]) => source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]))),
         initialize: emptyFunction,
     };
     _contentCommand = {
-        params: ["target", "source"], title: "set content",
-        script: "getProto(self.target).data = copyField(self.source);",
-        immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)),
+        params: ['target', 'source'],
+        title: 'set content',
+        script: 'getProto(self.target).data = copyField(self.source);',
+        immediate: undoBatch((source: Doc[]) => (Doc.GetProto(this.target).data = new List<Doc>(source))),
         initialize: emptyFunction,
     };
     _onClickCommand = {
-        params: ["target", "proxy"], title: "copy onClick",
+        params: ['target', 'proxy'],
+        title: 'copy onClick',
         script: `{ if (self.proxy?.[0]) {
              getProto(self.proxy[0]).onClick = copyField(self.target.onClick); 
              getProto(self.proxy[0]).target = self.target.target;
              getProto(self.proxy[0]).source = copyField(self.target.source); 
             }}`,
-        immediate: undoBatch((source: Doc[]) => { }),
+        immediate: undoBatch((source: Doc[]) => {}),
         initialize: emptyFunction,
     };
     _openLinkInCommand = {
-        params: ["target", "container"], title: "link follow target",
+        params: ['target', 'container'],
+        title: 'link follow target',
         script: `{ if (self.container?.length) {
             getProto(self.target).linkContainer = self.container[0];
             getProto(self.target).isLinkButton = true;
@@ -239,126 +257,180 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
             if (container.length) {
                 Doc.GetProto(this.target).linkContainer = container[0];
                 Doc.GetProto(this.target).isLinkButton = true;
-                Doc.GetProto(this.target).onClick = ScriptField.MakeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])");
+                Doc.GetProto(this.target).onClick = ScriptField.MakeScript('getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])');
             }
         }),
         initialize: emptyFunction,
     };
     _viewCommand = {
-        params: ["target"], title: "bookmark view",
+        params: ['target'],
+        title: 'bookmark view',
         script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);",
-        immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target._currentFrame = (this.target._currentFrame === undefined ? undefined : 0); }),
-        initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; button['target-currentFrame'] = this.target._currentFrame; },
+        immediate: undoBatch((source: Doc[]) => {
+            this.target._panX = 0;
+            this.target._panY = 0;
+            this.target._viewScale = 1;
+            this.target._currentFrame = this.target._currentFrame === undefined ? undefined : 0;
+        }),
+        initialize: (button: Doc) => {
+            button['target-panX'] = this.target._panX;
+            button['target-panY'] = this.target._panY;
+            button['target-viewScale'] = this.target._viewScale;
+            button['target-currentFrame'] = this.target._currentFrame;
+        },
     };
     _clusterCommand = {
-        params: ["target"], title: "fit content",
-        script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;",
-        immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox),
-        initialize: emptyFunction
+        params: ['target'],
+        title: 'fit content',
+        script: 'self.target._fitContentsToBox = !self.target._fitContentsToBox;',
+        immediate: undoBatch((source: Doc[]) => (this.target._fitContentsToBox = !this.target._fitContentsToBox)),
+        initialize: emptyFunction,
     };
     _fitContentCommand = {
-        params: ["target"], title: "toggle clusters",
-        script: "self.target._useClusters = !self.target._useClusters;",
-        immediate: undoBatch((source: Doc[]) => this.target._useClusters = !this.target._useClusters),
-        initialize: emptyFunction
+        params: ['target'],
+        title: 'toggle clusters',
+        script: 'self.target._useClusters = !self.target._useClusters;',
+        immediate: undoBatch((source: Doc[]) => (this.target._useClusters = !this.target._useClusters)),
+        initialize: emptyFunction,
     };
     _saveFilterCommand = {
-        params: ["target"], title: "save filter",
+        params: ['target'],
+        title: 'save filter',
         script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']); 
                  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; }),
+        immediate: undoBatch((source: Doc[]) => {
+            this.target._docFilters = undefined;
+            this.target._searchFilterDocs = undefined;
+        }),
         initialize: (button: Doc) => {
-            const activeDash = CurrentUserUtils.ActiveDashboard;
+            const activeDash = Doc.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-docFilters'] = (Doc.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.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.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; }
+    @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) {
-            default: return this._doc_commands;
-            case CollectionViewType.Freeform: return this._freeform_commands;
-            case CollectionViewType.Tree: return this._tree_commands;
-            case CollectionViewType.Schema: return this._schema_commands;
-            case CollectionViewType.Stacking: return this._stacking_commands;
-            case CollectionViewType.Masonry: return this._stacking_commands;
-            case CollectionViewType.Time: return this._freeform_commands;
-            case CollectionViewType.Carousel: return this._freeform_commands;
-            case CollectionViewType.Carousel3D: return this._freeform_commands;
+            default:
+                return this._doc_commands;
+            case CollectionViewType.Freeform:
+                return this._freeform_commands;
+            case CollectionViewType.Tree:
+                return this._tree_commands;
+            case CollectionViewType.Schema:
+                return this._schema_commands;
+            case CollectionViewType.Stacking:
+                return this._stacking_commands;
+            case CollectionViewType.Masonry:
+                return this._stacking_commands;
+            case CollectionViewType.Time:
+                return this._freeform_commands;
+            case CollectionViewType.Carousel:
+                return this._freeform_commands;
+            case CollectionViewType.Carousel3D:
+                return this._freeform_commands;
         }
     }
     private _commandRef = React.createRef<HTMLInputElement>();
     private _viewRef = React.createRef<HTMLInputElement>();
-    @observable private _currentKey: string = "";
+    @observable private _currentKey: string = '';
 
     componentDidMount = action(() => {
-        this._currentKey = this._currentKey || (this._buttonizableCommands?.length ? this._buttonizableCommands[0]?.title : "");
+        this._currentKey = this._currentKey || (this._buttonizableCommands?.length ? this._buttonizableCommands[0]?.title : '');
     });
 
     @undoBatch
     viewChanged = (e: React.ChangeEvent) => {
-        const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc;
+        const target = this.document !== Doc.MyLeftSidebarPanel ? this.document : (this.document.proto as Doc);
         //@ts-ignore
         target._viewType = e.target.selectedOptions[0].value;
-    }
+    };
 
     commandChanged = (e: React.ChangeEvent) => {
         //@ts-ignore
-        runInAction(() => this._currentKey = e.target.selectedOptions[0].value);
-    }
-
+        runInAction(() => (this._currentKey = e.target.selectedOptions[0].value));
+    };
 
     @action closeViewSpecs = () => {
         this.document._facetWidth = 0;
-    }
+    };
 
     @computed get subChrome() {
-        switch (this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type) { // bcz: ARgh!  hack to get menu for tree view outline items
-            default: return this.otherSubChrome;
+        switch (
+            this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type // bcz: ARgh!  hack to get menu for tree view outline items
+        ) {
+            default:
+                return this.otherSubChrome;
             case CollectionViewType.Invalid:
-            case CollectionViewType.Freeform: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />);
-            case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" {...this.props} />);
+            case CollectionViewType.Freeform:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />;
+            case CollectionViewType.Stacking:
+                return <CollectionStackingViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Schema:
+                return <CollectionSchemaViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Tree:
+                return <CollectionTreeViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Masonry:
+                return <CollectionStackingViewChrome key="collchrome" {...this.props} />;
             case CollectionViewType.Carousel:
-            case CollectionViewType.Carousel3D: return (<Collection3DCarouselViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Docking: return (<CollectionDockingChrome key="collchrome" {...this.props} />);
+            case CollectionViewType.Carousel3D:
+                return <Collection3DCarouselViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Grid:
+                return <CollectionGridViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Docking:
+                return <CollectionDockingChrome key="collchrome" {...this.props} />;
         }
     }
 
     @computed get otherSubChrome() {
         const docType = this.props.docView.Document.type;
         switch (docType) {
-            default: return (null);
-            case DocumentType.IMG: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.PDF: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.INK: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.WEB: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.VID: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.RTF: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} isDoc={true} />);
-            case DocumentType.MAP: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
+            default:
+                return null;
+            case DocumentType.IMG:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.PDF:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.INK:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.WEB:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.VID:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.RTF:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} isDoc={true} />;
+            case DocumentType.MAP:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
         }
     }
 
-
     private dropDisposer?: DragManager.DragDropDisposer;
     protected createDropTarget = (ele: HTMLDivElement) => {
         this.dropDisposer?.();
         if (ele) {
             this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.document);
         }
-    }
+    };
 
     @undoBatch
     @action
@@ -372,120 +444,139 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     }
 
     dragViewDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            const vtype = this.props.type;
-            const c = {
-                params: ["target"], title: vtype,
-                script: `this.target._viewType = '${StrCast(this.props.type)}'`,
-                immediate: (source: Doc[]) => this.document._viewType = Doc.getDocTemplate(source?.[0]),
-                initialize: emptyFunction,
-            };
-            DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title),
-                { target: this.document }, c.params, c.initialize, e.clientX, e.clientY);
-            return true;
-        }, emptyFunction, emptyFunction);
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                const vtype = this.props.type;
+                const c = {
+                    params: ['target'],
+                    title: vtype,
+                    script: `this.target._viewType = '${StrCast(this.props.type)}'`,
+                    immediate: (source: Doc[]) => (this.document._viewType = Doc.getDocTemplate(source?.[0])),
+                    initialize: emptyFunction,
+                };
+                DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title), { target: this.document }, c.params, c.initialize, e.clientX, e.clientY);
+                return true;
+            },
+            emptyFunction,
+            emptyFunction
+        );
+    };
     dragCommandDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c =>
-                DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
-                    { target: this.document }, c.params, c.initialize, e.clientX, e.clientY));
-            return true;
-        }, emptyFunction, () => {
-            this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate([]));
-        });
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title, { target: this.document }, c.params, c.initialize, e.clientX, e.clientY));
+                return true;
+            },
+            emptyFunction,
+            () => {
+                this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate([]));
+            }
+        );
+    };
 
     @computed get templateChrome() {
-        return <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} >
-            <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
-                <div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
-                    <button className={"antimodeMenu-button"} >
-                        <FontAwesomeIcon icon="bullseye" size="lg" />
-                    </button>
-                    <select
-                        className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
-                        <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={"empty"} value={""} />
-                        {this._buttonizableCommands?.map(cmd =>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>{cmd.title}</option>
-                        )}
-                    </select>
-                </div>
-            </Tooltip>
-        </div>;
+        return (
+            <div className="collectionViewBaseChrome-template" ref={this.createDropTarget}>
+                <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
+                    <div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
+                        <button className={'antimodeMenu-button'}>
+                            <FontAwesomeIcon icon="bullseye" size="lg" />
+                        </button>
+                        <select className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={'empty'} value={''} />
+                            {this._buttonizableCommands?.map(cmd => (
+                                <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>
+                                    {cmd.title}
+                                </option>
+                            ))}
+                        </select>
+                    </div>
+                </Tooltip>
+            </div>
+        );
     }
 
     @computed get viewModes() {
         const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear];
-        const isPres: boolean = (this.document && this.document.type === DocumentType.PRES);
-        return isPres ? (null) : (<div className="collectionViewBaseChrome-viewModes" >
-            <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
-                <div className="commandEntry-outerDiv" ref={this._viewRef} onPointerDown={this.dragViewDown}>
-                    <button className={"antimodeMenu-button"}>
-                        <FontAwesomeIcon icon="bullseye" size="lg" />
-                    </button>
-                    <select
-                        className="collectionViewBaseChrome-viewPicker"
-                        onPointerDown={stopPropagation}
-                        onChange={this.viewChanged}
-                        value={StrCast(this.props.type)}>
-                        {Object.values(CollectionViewType).filter(type => !excludedViewTypes.includes(type)).map(type => (
-                            <option
-                                key={Utils.GenerateGuid()}
-                                className="collectionViewBaseChrome-viewOption"
-                                onPointerDown={stopPropagation}
-                                value={type}>
-                                {type[0].toUpperCase() + type.substring(1)}
-                            </option>
-                        ))}
-                    </select>
-                </div>
-            </Tooltip>
-        </div>);
+        const isPres: boolean = this.document && this.document.type === DocumentType.PRES;
+        return isPres ? null : (
+            <div className="collectionViewBaseChrome-viewModes">
+                <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
+                    <div className="commandEntry-outerDiv" ref={this._viewRef} onPointerDown={this.dragViewDown}>
+                        <button className={'antimodeMenu-button'}>
+                            <FontAwesomeIcon icon="bullseye" size="lg" />
+                        </button>
+                        <select className="collectionViewBaseChrome-viewPicker" onPointerDown={stopPropagation} onChange={this.viewChanged} value={StrCast(this.props.type)}>
+                            {Object.values(CollectionViewType)
+                                .filter(type => !excludedViewTypes.includes(type))
+                                .map(type => (
+                                    <option key={Utils.GenerateGuid()} className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value={type}>
+                                        {type[0].toUpperCase() + type.substring(1)}
+                                    </option>
+                                ))}
+                        </select>
+                    </div>
+                </Tooltip>
+            </div>
+        );
     }
 
-    @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
-    @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
+    @computed get selectedDocumentView() {
+        return SelectionManager.Views().lastElement();
+    }
+    @computed get selectedDoc() {
+        return SelectionManager.Docs().lastElement();
+    }
     @computed get notACollection() {
         if (this.selectedDoc) {
             const layoutField = Doc.LayoutField(this.selectedDoc);
-            return this.props.type === CollectionViewType.Docking ||
-                typeof (layoutField) === "string" && !layoutField?.includes("CollectionView");
-        }
-        else return false;
+            return this.props.type === CollectionViewType.Docking || (typeof layoutField === 'string' && !layoutField?.includes('CollectionView'));
+        } else return false;
     }
     @computed
     get pinButton() {
         const targetDoc = this.selectedDoc;
         const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
-        return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
-            <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderLeft: "1px solid gray" }}
-                onClick={e => TabDocView.PinDoc(targetDoc, { /* unpin: isPinned*/ })}>
-                <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
-            </button>
-        </Tooltip>;
+        return !targetDoc ? null : (
+            <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? 'Unpin from presentation' : 'Pin to presentation'}</div>} placement="top">
+                <button
+                    className="antimodeMenu-button"
+                    style={{ backgroundColor: isPinned ? '121212' : undefined, borderLeft: '1px solid gray' }}
+                    onClick={e =>
+                        TabDocView.PinDoc(targetDoc, {
+                            /* unpin: isPinned*/
+                        })
+                    }>
+                    <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
+                </button>
+            </Tooltip>
+        );
     }
 
     @undoBatch
     @action
     startRecording = () => {
-        const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
-        //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
-        CollectionDockingView.AddSplit(doc, "right");
-    }
+        const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' });
+        //Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
+        CollectionDockingView.AddSplit(doc, 'right');
+    };
 
     @computed
     get recordButton() {
         const targetDoc = this.selectedDoc;
-        return <Tooltip key="record" title={<div className="dash-tooltip">{"Capture screen"}</div>} placement="top">
-            <button className="antimodeMenu-button"
-                onClick={e => this.startRecording()}>
-                <div className="recordButtonOutline" style={{}}>
-                    <div className="recordButtonInner" style={{}}>
+        return (
+            <Tooltip key="record" title={<div className="dash-tooltip">{'Capture screen'}</div>} placement="top">
+                <button className="antimodeMenu-button" onClick={e => this.startRecording()}>
+                    <div className="recordButtonOutline" style={{}}>
+                        <div className="recordButtonInner" style={{}}></div>
                     </div>
-                </div>
-            </button>
-        </Tooltip>;
+                </button>
+            </Tooltip>
+        );
     }
 
     @undoBatch
@@ -517,21 +608,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                 activeDoc.presPinView = true;
             }
         }
-    }
+    };
 
     @computed
     get pinWithViewButton() {
-        const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: "auto", width: 19 }} />;
-        return !this.selectedDoc ? (null) :
-            <Tooltip title={<div className="dash-tooltip">{"Pin with current view"}</div>} placement="top">
-                <button className="antimodeMenu-button" style={{ justifyContent: 'center' }}
-                    onClick={() => this.pinWithView(this.selectedDoc)}>
+        const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: 'auto', width: 19 }} />;
+        return !this.selectedDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">{'Pin with current view'}</div>} placement="top">
+                <button className="antimodeMenu-button" style={{ justifyContent: 'center' }} onClick={() => this.pinWithView(this.selectedDoc)}>
                     {presPinWithViewIcon}
                 </button>
-            </Tooltip>;
+            </Tooltip>
+        );
     }
 
-
     @undoBatch
     onAlias = () => {
         if (this.selectedDoc && this.selectedDocumentView) {
@@ -544,10 +634,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
             alias.y = NumCast(this.selectedDoc.y) + 30;
             this.selectedDocumentView.props.addDocument?.(alias);
         }
-    }
+    };
     onAliasButtonDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
-    }
+    };
 
     @undoBatch
     onAliasButtonMoved = (e: PointerEvent) => {
@@ -555,62 +645,72 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         if (contentDiv && this.selectedDoc) {
             const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
             const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
-            dragData.defaultDropAction = "alias";
+            dragData.defaultDropAction = 'alias';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, {
                 offsetX: offset[0],
                 offsetY: offset[1],
-                hideSource: false
+                hideSource: false,
             });
             return true;
         }
         return false;
-    }
+    };
 
     @computed
     get aliasButton() {
         const targetDoc = this.selectedDoc;
-        return !targetDoc || targetDoc.type === DocumentType.PRES ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top">
-            <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: "drag" }}>
-                <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
-            </button>
-        </Tooltip>;
+        return !targetDoc || targetDoc.type === DocumentType.PRES ? null : (
+            <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an alias'}</div>} placement="top">
+                <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: 'drag' }}>
+                    <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
+                </button>
+            </Tooltip>
+        );
     }
 
     @computed get lightboxButton() {
         const targetDoc = this.selectedDoc;
-        return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"View in Lightbox"}</div>} placement="top">
-            <button className="antimodeMenu-button" onPointerDown={() => {
-                const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
-                LightboxView.SetLightboxDoc(targetDoc, undefined, docs);
-            }}>
-                <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
-            </button>
-        </Tooltip>;
+        return !targetDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">{'View in Lightbox'}</div>} placement="top">
+                <button
+                    className="antimodeMenu-button"
+                    onPointerDown={() => {
+                        const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
+                        LightboxView.SetLightboxDoc(targetDoc, undefined, docs);
+                    }}>
+                    <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
+                </button>
+            </Tooltip>
+        );
     }
 
     @computed get toggleOverlayButton() {
-        return <>
-            <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
-                <button className={"antimodeMenu-button"} key="float"
-                    style={{
-                        backgroundColor: this.props.docView.layoutDoc.z ? "121212" : undefined,
-                        pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "none" : undefined,
-                        color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "dimgrey" : undefined
-                    }}
-                    onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
-                    <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} />
-                </button>
-            </Tooltip>
-        </>;
+        return (
+            <>
+                <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
+                    <button
+                        className={'antimodeMenu-button'}
+                        key="float"
+                        style={{
+                            backgroundColor: this.props.docView.layoutDoc.z ? '121212' : undefined,
+                            pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? 'none' : undefined,
+                            color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? 'dimgrey' : undefined,
+                        }}
+                        onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
+                        <FontAwesomeIcon icon={['fab', 'buffer']} size={'lg'} />
+                    </button>
+                </Tooltip>
+            </>
+        );
     }
 
     render() {
         return (
-            <div className="collectionMenu-cont" >
+            <div className="collectionMenu-cont">
                 <div className="collectionMenu">
                     <div className="collectionViewBaseChrome">
-                        {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
+                        {this.notACollection || this.props.type === CollectionViewType.Invalid ? null : this.viewModes}
                         <div className="collectionMenu-divider" key="divider1"></div>
                         {this.aliasButton}
                         {/* {this.pinButton} */}
@@ -621,7 +721,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                         <div className="collectionMenu-divider" key="divider3"></div>
                         {this.lightboxButton}
                         {this.recordButton}
-                        {!this._buttonizableCommands ? (null) : this.templateChrome}
+                        {!this._buttonizableCommands ? null : this.templateChrome}
                     </div>
                 </div>
             </div>
@@ -632,30 +732,40 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
 @observer
 export class CollectionDockingChrome extends React.Component<CollectionViewMenuProps> {
     render() {
-        return (null);
+        return null;
     }
 }
 
 @observer
-export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean, isDoc?: boolean }> {
+export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean; isDoc?: boolean }> {
     public static Instance: CollectionFreeFormViewChrome;
     constructor(props: any) {
         super(props);
         CollectionFreeFormViewChrome.Instance = this;
     }
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
     @computed get dataField() {
-        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
+        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? '-annotations' : '')];
+    }
+    @computed get childDocs() {
+        return DocListCast(this.dataField);
+    }
+    @computed get selectedDocumentView() {
+        return SelectionManager.Views().lastElement();
+    }
+    @computed get selectedDoc() {
+        return SelectionManager.Docs().lastElement();
+    }
+    @computed get isText() {
+        return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false;
     }
-    @computed get childDocs() { return DocListCast(this.dataField); }
-    @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
-    @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
-    @computed get isText() { return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false; }
 
     @undoBatch
     @action
     nextKeyframe = (): void => {
-        const currentFrame = Cast(this.document._currentFrame, "number", null);
+        const currentFrame = Cast(this.document._currentFrame, 'number', null);
         if (currentFrame === undefined) {
             this.document._currentFrame = 0;
             CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
@@ -663,72 +773,88 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
         CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0);
         this.document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
         this.document.lastFrame = Math.max(NumCast(this.document._currentFrame), NumCast(this.document.lastFrame));
-    }
+    };
     @undoBatch
     @action
     prevKeyframe = (): void => {
-        const currentFrame = Cast(this.document._currentFrame, "number", null);
+        const currentFrame = Cast(this.document._currentFrame, 'number', null);
         if (currentFrame === undefined) {
             this.document._currentFrame = 0;
             CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
         }
         CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
         this.document._currentFrame = Math.max(0, (currentFrame || 0) - 1);
-    }
+    };
 
-    private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""];
-    private _width = ["1", "5", "10", "100"];
+    private _palette = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', ''];
+    private _width = ['1', '5', '10', '100'];
     private _dotsize = [10, 20, 30, 40];
-    private _draw = ["∿", "=", "⎯", "→", "↔︎", "ロ", "O"];
-    private _head = ["", "", "", "", "arrow", "", ""];
-    private _end = ["", "", "", "arrow", "arrow", "", ""];
-    private _shapePrims = ["", "", "line", "line", "line", "rectangle", "circle"];
-    private _title = ["pen", "highlighter", "line", "line with arrow", "line with double arrows", "square", "circle"];
-    private _faName = ["pen-fancy", "highlighter", "minus", "long-arrow-alt-right", "arrows-alt-h", "square", "circle"];
+    private _draw = ['∿', '=', '⎯', '→', '↔︎', 'ロ', 'O'];
+    private _head = ['', '', '', '', 'arrow', '', ''];
+    private _end = ['', '', '', 'arrow', 'arrow', '', ''];
+    private _shapePrims = ['', '', 'line', 'line', 'line', 'rectangle', 'circle'];
+    private _title = ['pen', 'highlighter', 'line', 'line with arrow', 'line with double arrows', 'square', 'circle'];
+    private _faName = ['pen-fancy', 'highlighter', 'minus', 'long-arrow-alt-right', 'arrows-alt-h', 'square', 'circle'];
     @observable _selectedPrimitive = this._shapePrims.length;
     @observable _keepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
     @observable _colorBtn = false;
     @observable _widthBtn = false;
     @observable _fillBtn = false;
 
-    @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; }
+    @action clearKeepPrimitiveMode() {
+        this._selectedPrimitive = this._shapePrims.length;
+    }
     @action primCreated() {
-        if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
-            if (CurrentUserUtils.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
-            CurrentUserUtils.ActiveTool = InkTool.None;
+        if (!this._keepPrimitiveMode) {
+            //get out of ink mode after each stroke=
+            if (Doc.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
+            Doc.ActiveTool = InkTool.None;
             this._selectedPrimitive = this._shapePrims.length;
-            SetActiveArrowStart("none");
-            SetActiveArrowEnd("none");
+            SetActiveArrowStart('none');
+            SetActiveArrowEnd('none');
         }
     }
 
     @action
     changeColor = (color: string, type: string) => {
         const col: ColorState = {
-            hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" },
-            rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "",
+            hex: color,
+            hsl: { a: 0, h: 0, s: 0, l: 0, source: '' },
+            hsv: { a: 0, h: 0, s: 0, v: 0, source: '' },
+            rgb: { a: 0, r: 0, b: 0, g: 0, source: '' },
+            oldHue: 0,
+            source: '',
         };
-        if (type === "color") {
+        if (type === 'color') {
             SetActiveInkColor(Utils.colorString(col));
-        } else if (type === "fill") {
+        } else if (type === 'fill') {
             SetActiveFillColor(Utils.colorString(col));
         }
-    }
+    };
 
     @action
     editProperties = (value: any, field: string) => {
-        SelectionManager.Views().forEach(action((element: DocumentView) => {
-            const doc = Document(element.rootDoc);
-            if (doc.type === DocumentType.INK) {
-                switch (field) {
-                    case "width": doc.strokeWidth = Number(value); break;
-                    case "color": doc.color = String(value); break;
-                    case "fill": doc.fillColor = String(value); break;
-                    case "dash": doc.strokeDash = value;
+        SelectionManager.Views().forEach(
+            action((element: DocumentView) => {
+                const doc = Document(element.rootDoc);
+                if (doc.type === DocumentType.INK) {
+                    switch (field) {
+                        case 'width':
+                            doc.strokeWidth = Number(value);
+                            break;
+                        case 'color':
+                            doc.color = String(value);
+                            break;
+                        case 'fill':
+                            doc.fillColor = String(value);
+                            break;
+                        case 'dash':
+                            doc.strokeDash = value;
+                    }
                 }
-            }
-        }));
-    }
+            })
+        );
+    };
 
     @computed get drawButtons() {
         const func = action((e: React.MouseEvent | React.PointerEvent, i: number, keep: boolean) => {
@@ -736,147 +862,184 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
             // these are for shapes
             if (this._selectedPrimitive !== i) {
                 this._selectedPrimitive = i;
-                if (this._title[i] === "highlighter") {
-                    CurrentUserUtils.ActiveTool = InkTool.Highlighter;
+                if (this._title[i] === 'highlighter') {
+                    Doc.ActiveTool = InkTool.Highlighter;
                     GestureOverlay.Instance.SavedColor = ActiveInkColor();
-                    SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+                    SetActiveInkColor('rgba(245, 230, 95, 0.75)');
                 } else {
-                    CurrentUserUtils.ActiveTool = InkTool.Pen;
+                    Doc.ActiveTool = InkTool.Pen;
                 }
                 SetActiveArrowStart(this._head[i]);
                 SetActiveArrowEnd(this._end[i]);
-                SetActiveBezierApprox("300");
+                SetActiveBezierApprox('300');
 
                 GestureOverlay.Instance.InkShape = this._shapePrims[i];
             } else {
                 this._selectedPrimitive = this._shapePrims.length;
-                CurrentUserUtils.ActiveTool = InkTool.None;
-                SetActiveArrowStart("");
-                SetActiveArrowEnd("");
-                GestureOverlay.Instance.InkShape = "";
-                SetActiveBezierApprox("0");
+                Doc.ActiveTool = InkTool.None;
+                SetActiveArrowStart('');
+                SetActiveArrowEnd('');
+                GestureOverlay.Instance.InkShape = '';
+                SetActiveBezierApprox('0');
             }
             e.stopPropagation();
         });
-        return <div className="btn-draw" key="draw">
-            {this._draw.map((icon, i) =>
-                <Tooltip key={icon} title={<div className="dash-tooltip">{this._title[i]}</div>} placement="bottom">
-                    <button className="antimodeMenu-button"
-                        onPointerDown={e => func(e, i, false)}
-                        onDoubleClick={e => func(e, i, true)}
-                        style={{ backgroundColor: i === this._selectedPrimitive ? "525252" : "", fontSize: "20" }}>
-                        <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
-                    </button>
-                </Tooltip>)}
-        </div>;
+        return (
+            <div className="btn-draw" key="draw">
+                {this._draw.map((icon, i) => (
+                    <Tooltip key={icon} title={<div className="dash-tooltip">{this._title[i]}</div>} placement="bottom">
+                        <button className="antimodeMenu-button" onPointerDown={e => func(e, i, false)} onDoubleClick={e => func(e, i, true)} style={{ backgroundColor: i === this._selectedPrimitive ? '525252' : '', fontSize: '20' }}>
+                            <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
+                        </button>
+                    </Tooltip>
+                ))}
+            </div>
+        );
     }
 
-    toggleButton = (key: string, value: boolean, setter: () => {}, icon: FontAwesomeIconProps["icon"], ele: JSX.Element | null) => {
-        return <Tooltip title={<div className="dash-tooltip">{key}</div>} placement="bottom">
-            <button className="antimodeMenu-button" key={key}
-                onPointerDown={action(e => setter())}
-                style={{ backgroundColor: value ? "121212" : "" }}>
-                <FontAwesomeIcon icon={icon} size="lg" />
-                {ele}
-            </button>
-        </Tooltip>;
-    }
+    toggleButton = (key: string, value: boolean, setter: () => {}, icon: FontAwesomeIconProps['icon'], ele: JSX.Element | null) => {
+        return (
+            <Tooltip title={<div className="dash-tooltip">{key}</div>} placement="bottom">
+                <button className="antimodeMenu-button" key={key} onPointerDown={action(e => setter())} style={{ backgroundColor: value ? '121212' : '' }}>
+                    <FontAwesomeIcon icon={icon} size="lg" />
+                    {ele}
+                </button>
+            </Tooltip>
+        );
+    };
 
     @computed get widthPicker() {
-        const widthPicker = this.toggleButton("stroke width", this._widthBtn, () => this._widthBtn = !this._widthBtn, "bars", null);
-        return !this._widthBtn ? widthPicker :
+        const widthPicker = this.toggleButton('stroke width', this._widthBtn, () => (this._widthBtn = !this._widthBtn), 'bars', null);
+        return !this._widthBtn ? (
+            widthPicker
+        ) : (
             <div className="btn2-group" key="width">
                 {widthPicker}
-                {this._width.map((wid, i) =>
+                {this._width.map((wid, i) => (
                     <Tooltip title={<div className="dash-tooltip">change width</div>} placement="bottom">
-                        <button className="antimodeMenu-button" key={wid}
-                            onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
-                            style={{ backgroundColor: this._widthBtn ? "121212" : "", zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: "center" }}>
+                        <button
+                            className="antimodeMenu-button"
+                            key={wid}
+                            onPointerDown={action(() => {
+                                SetActiveInkWidth(wid);
+                                this._widthBtn = false;
+                                this.editProperties(wid, 'width');
+                            })}
+                            style={{ backgroundColor: this._widthBtn ? '121212' : '', zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: 'center' }}>
                             •
                         </button>
-                    </Tooltip>)}
-            </div>;
+                    </Tooltip>
+                ))}
+            </div>
+        );
     }
 
     @computed get colorPicker() {
-        const colorPicker = this.toggleButton("stroke color", this._colorBtn, () => this._colorBtn = !this._colorBtn, "pen-nib",
-            <div className="color-previewI" style={{ backgroundColor: ActiveInkColor() ?? "121212" }} />);
-        return !this._colorBtn ? colorPicker :
+        const colorPicker = this.toggleButton('stroke color', this._colorBtn, () => (this._colorBtn = !this._colorBtn), 'pen-nib', <div className="color-previewI" style={{ backgroundColor: ActiveInkColor() ?? '121212' }} />);
+        return !this._colorBtn ? (
+            colorPicker
+        ) : (
             <div className="btn-group" key="color">
                 {colorPicker}
-                {this._palette.map(color =>
-                    <button className="antimodeMenu-button" key={color}
-                        onPointerDown={action(() => { this.changeColor(color, "color"); this._colorBtn = false; this.editProperties(color, "color"); })}
-                        style={{ backgroundColor: this._colorBtn ? "121212" : "", zIndex: 1001 }}>
+                {this._palette.map(color => (
+                    <button
+                        className="antimodeMenu-button"
+                        key={color}
+                        onPointerDown={action(() => {
+                            this.changeColor(color, 'color');
+                            this._colorBtn = false;
+                            this.editProperties(color, 'color');
+                        })}
+                        style={{ backgroundColor: this._colorBtn ? '121212' : '', zIndex: 1001 }}>
                         {/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */}
                         <div className="color-previewII" style={{ backgroundColor: color }}>
-                            {color === "" ? <p style={{ fontSize: 40, color: "red", marginTop: -10, marginLeft: -5, position: "fixed" }}>☒</p> : ""}
+                            {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
                         </div>
-                    </button >)}
-            </div >;
+                    </button>
+                ))}
+            </div>
+        );
     }
     @computed get fillPicker() {
-        const fillPicker = this.toggleButton("shape fill color", this._fillBtn, () => this._fillBtn = !this._fillBtn, "fill-drip",
-            <div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? "121212" }} />);
-        return !this._fillBtn ? fillPicker :
-            <div className="btn-group" key="fill" >
+        const fillPicker = this.toggleButton('shape fill color', this._fillBtn, () => (this._fillBtn = !this._fillBtn), 'fill-drip', <div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? '121212' }} />);
+        return !this._fillBtn ? (
+            fillPicker
+        ) : (
+            <div className="btn-group" key="fill">
                 {fillPicker}
-                {this._palette.map(color =>
-                    <button className="antimodeMenu-button" key={color}
-                        onPointerDown={action(() => { this.changeColor(color, "fill"); this._fillBtn = false; this.editProperties(color, "fill"); })}
-                        style={{ backgroundColor: this._fillBtn ? "121212" : "", zIndex: 1001 }}>
+                {this._palette.map(color => (
+                    <button
+                        className="antimodeMenu-button"
+                        key={color}
+                        onPointerDown={action(() => {
+                            this.changeColor(color, 'fill');
+                            this._fillBtn = false;
+                            this.editProperties(color, 'fill');
+                        })}
+                        style={{ backgroundColor: this._fillBtn ? '121212' : '', zIndex: 1001 }}>
                         <div className="color-previewII" style={{ backgroundColor: color }}>
-                            {color === "" ? <p style={{ fontSize: 40, color: "red", marginTop: -10, marginLeft: -5, position: "fixed" }}>☒</p> : ""}
+                            {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
                         </div>
-                    </button>)}
-
-            </div>;
+                    </button>
+                ))}
+            </div>
+        );
     }
 
     render() {
-        return !this.props.docView.layoutDoc ? (null) :
+        return !this.props.docView.layoutDoc ? null : (
             <div className="collectionFreeFormMenu-cont">
                 <RichTextMenu key="rich" />
-                {!this.isText ?
+                {!this.isText ? (
                     <>
                         {this.drawButtons}
                         {this.widthPicker}
                         {this.colorPicker}
                         {this.fillPicker}
-                        {Doc.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}>
-                                        <FontAwesomeIcon icon={"caret-left"} size={"lg"} />
+                                        <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
                                     </div>
                                 </Tooltip>
                                 <Tooltip key="num" title={<div className="dash-tooltip">Toggle View All</div>} placement="bottom">
-                                    <div className="numKeyframe" style={{ color: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? "white" : "black", backgroundColor: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? "#5B9FDD" : "#AEDDF8" }}
-                                        onClick={action(() => this.props.docView.ComponentView?.setKeyFrameEditing?.(!this.props.docView.ComponentView?.getKeyFrameEditing?.()))} >
+                                    <div
+                                        className="numKeyframe"
+                                        style={{ color: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? 'white' : 'black', backgroundColor: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? '#5B9FDD' : '#AEDDF8' }}
+                                        onClick={action(() => this.props.docView.ComponentView?.setKeyFrameEditing?.(!this.props.docView.ComponentView?.getKeyFrameEditing?.()))}>
                                         {NumCast(this.document._currentFrame)}
                                     </div>
                                 </Tooltip>
                                 <Tooltip key="fwd" title={<div className="dash-tooltip">Forward Frame</div>} placement="bottom">
                                     <div className="fwdKeyframe" onClick={this.nextKeyframe}>
-                                        <FontAwesomeIcon icon={"caret-right"} size={"lg"} />
+                                        <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
                                     </div>
                                 </Tooltip>
-                            </>}
-                    </> : (null)
-                }
-                {!this.selectedDocumentView?.ComponentView?.menuControls ? (null) : this.selectedDocumentView?.ComponentView?.menuControls?.()}
-            </div>;
+                            </>
+                        )}
+                    </>
+                ) : null}
+                {!this.selectedDocumentView?.ComponentView?.menuControls ? null : this.selectedDocumentView?.ComponentView?.menuControls?.()}
+            </div>
+        );
     }
 }
 @observer
 export class CollectionStackingViewChrome extends React.Component<CollectionViewMenuProps> {
-    @observable private _currentKey: string = "";
+    @observable private _currentKey: string = '';
     @observable private suggestions: string[] = [];
 
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
 
-    @computed private get descending() { return StrCast(this.document._columnsSort) === "descending"; }
-    @computed get pivotField() { return StrCast(this.document._pivotField); }
+    @computed private get descending() {
+        return StrCast(this.document._columnsSort) === 'descending';
+    }
+    @computed get pivotField() {
+        return StrCast(this.document._pivotField);
+    }
 
     getKeySuggestions = async (value: string): Promise<string[]> => {
         const val = value.toLowerCase();
@@ -884,16 +1047,14 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
 
         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 ||
-                    (key[0].toUpperCase() === key[0] && key[0] !== "_"));
+                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
                 return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
             } else {
                 const keys = new Set<string>();
                 docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-                const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
-                    key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
-                    (key[0]?.toUpperCase() === key[0] && key[0] !== "_"));
+                const noviceKeys = Array.from(keys).filter(
+                    key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
+                );
                 return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
             }
         }
@@ -905,81 +1066,77 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
             docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
             return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
         }
-    }
+    };
 
     @action
     onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
         this._currentKey = newValue;
-    }
+    };
 
     getSuggestionValue = (suggestion: string) => suggestion;
 
     renderSuggestion = (suggestion: string) => {
         return <p>{suggestion}</p>;
-    }
+    };
 
     onSuggestionFetch = async ({ value }: { value: string }) => {
         const sugg = await this.getKeySuggestions(value);
         runInAction(() => {
             this.suggestions = sugg;
         });
-    }
+    };
 
     @action
     onSuggestionClear = () => {
         this.suggestions = [];
-    }
+    };
 
     @action
     setValue = (value: string) => {
         this.document._pivotField = value;
         return true;
-    }
+    };
 
     @action toggleSort = () => {
-        this.document._columnsSort =
-            this.document._columnsSort === "descending" ? "ascending" :
-                this.document._columnsSort === "ascending" ? undefined : "descending";
-    }
-    @action resetValue = () => { this._currentKey = this.pivotField; };
+        this.document._columnsSort = this.document._columnsSort === 'descending' ? 'ascending' : this.document._columnsSort === 'ascending' ? undefined : 'descending';
+    };
+    @action resetValue = () => {
+        this._currentKey = this.pivotField;
+    };
 
     render() {
         const doctype = this.props.docView.Document.type;
-        const isPres: boolean = (doctype === DocumentType.PRES);
-        return (
-            isPres ? (null) : <div className="collectionStackingViewChrome-cont">
+        const isPres: boolean = doctype === DocumentType.PRES;
+        return isPres ? null : (
+            <div className="collectionStackingViewChrome-cont">
                 <div className="collectionStackingViewChrome-pivotField-cont">
-                    <div className="collectionStackingViewChrome-pivotField-label">
-                        GROUP BY:
-                    </div>
-                    <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? "180" : "0"}deg)` }}>
+                    <div className="collectionStackingViewChrome-pivotField-label">GROUP BY:</div>
+                    <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? '180' : '0'}deg)` }}>
                         <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
                     </div>
                     <div className="collectionStackingViewChrome-pivotField">
                         <EditableView
                             GetValue={() => this.pivotField}
-                            autosuggestProps={
-                                {
-                                    resetValue: this.resetValue,
-                                    value: this._currentKey,
-                                    onChange: this.onKeyChange,
-                                    autosuggestProps: {
-                                        inputProps:
-                                        {
-                                            value: this._currentKey,
-                                            onChange: this.onKeyChange
-                                        },
-                                        getSuggestionValue: this.getSuggestionValue,
-                                        suggestions: this.suggestions,
-                                        alwaysRenderSuggestions: true,
-                                        renderSuggestion: this.renderSuggestion,
-                                        onSuggestionsFetchRequested: this.onSuggestionFetch,
-                                        onSuggestionsClearRequested: this.onSuggestionClear
-                                    }
-                                }}
+                            autosuggestProps={{
+                                resetValue: this.resetValue,
+                                value: this._currentKey,
+                                onChange: this.onKeyChange,
+                                autosuggestProps: {
+                                    inputProps: {
+                                        value: this._currentKey,
+                                        onChange: this.onKeyChange,
+                                    },
+                                    getSuggestionValue: this.getSuggestionValue,
+                                    suggestions: this.suggestions,
+                                    alwaysRenderSuggestions: true,
+                                    renderSuggestion: this.renderSuggestion,
+                                    onSuggestionsFetchRequested: this.onSuggestionFetch,
+                                    onSuggestionsClearRequested: this.onSuggestionClear,
+                                },
+                            }}
                             oneLine
                             SetValue={this.setValue}
-                            contents={this.pivotField ? this.pivotField : "N/A"}
+                            contents={this.pivotField ? this.pivotField : 'N/A'}
                         />
                     </div>
                 </div>
@@ -988,11 +1145,12 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
     }
 }
 
-
 @observer
 export class CollectionSchemaViewChrome extends React.Component<CollectionViewMenuProps> {
     // private _textwrapAllRows: boolean = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
 
     @undoBatch
     togglePreview = () => {
@@ -1002,12 +1160,12 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
         const previewWidth = NumCast(this.document.schemaPreviewWidth);
         const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
         this.document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
-    }
+    };
 
     @undoBatch
     @action
     toggleTextwrap = async () => {
-        const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []);
+        const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []);
         if (textwrappedRows.length) {
             this.document.textwrappedSchemaRows = new List<string>([]);
         } else {
@@ -1015,56 +1173,52 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
             const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
             this.document.textwrappedSchemaRows = new List<string>(allRows);
         }
-    }
-
+    };
 
     render() {
         const previewWidth = NumCast(this.document.schemaPreviewWidth);
-        const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
+        const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []).length > 0;
 
         return (
             <div className="collectionSchemaViewChrome-cont">
                 <div className="collectionSchemaViewChrome-toggle">
                     <div className="collectionSchemaViewChrome-label">Show Preview: </div>
                     <div className="collectionSchemaViewChrome-toggler" onClick={this.togglePreview}>
-                        <div className={"collectionSchemaViewChrome-togglerButton" + (previewWidth !== 0 ? " on" : " off")}>
-                            {previewWidth !== 0 ? "on" : "off"}
-                        </div>
+                        <div className={'collectionSchemaViewChrome-togglerButton' + (previewWidth !== 0 ? ' on' : ' off')}>{previewWidth !== 0 ? 'on' : 'off'}</div>
                     </div>
                 </div>
-            </div >
+            </div>
         );
     }
 }
 
 @observer
 export class CollectionTreeViewChrome extends React.Component<CollectionViewMenuProps> {
-
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
     get sortAscending() {
-        return this.document[this.props.fieldKey + "-sortAscending"];
+        return this.document[this.props.fieldKey + '-sortAscending'];
     }
     set sortAscending(value) {
-        this.document[this.props.fieldKey + "-sortAscending"] = value;
+        this.document[this.props.fieldKey + '-sortAscending'] = value;
     }
     @computed private get ascending() {
-        return Cast(this.sortAscending, "boolean", null);
+        return Cast(this.sortAscending, 'boolean', null);
     }
 
     @action toggleSort = () => {
         if (this.sortAscending) this.sortAscending = undefined;
         else if (this.sortAscending === undefined) this.sortAscending = false;
         else this.sortAscending = true;
-    }
+    };
 
     render() {
         return (
             <div className="collectionTreeViewChrome-cont">
                 <button className="collectionTreeViewChrome-sort" onClick={this.toggleSort}>
-                    <div className="collectionTreeViewChrome-sortLabel">
-                        Sort
-                    </div>
-                    <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? "90" : this.ascending ? "180" : "0"}deg)` }}>
+                    <div className="collectionTreeViewChrome-sortLabel">Sort</div>
+                    <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? '90' : this.ascending ? '180' : '0'}deg)` }}>
                         <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
                     </div>
                 </button>
@@ -1073,10 +1227,12 @@ export class CollectionTreeViewChrome extends React.Component<CollectionViewMenu
     }
 }
 
-// Enter scroll speed for 3D Carousel 
+// Enter scroll speed for 3D Carousel
 @observer
 export class Collection3DCarouselViewChrome extends React.Component<CollectionViewMenuProps> {
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
     @computed get scrollSpeed() {
         return this.document._autoScrollSpeed;
     }
@@ -1089,22 +1245,16 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionVi
             return true;
         }
         return false;
-    }
+    };
 
     render() {
         return (
             <div className="collection3DCarouselViewChrome-cont">
                 <div className="collection3DCarouselViewChrome-scrollSpeed-cont">
-                    {FormattedTextBox.Focused ? <RichTextMenu /> : (null)}
-                    <div className="collectionStackingViewChrome-scrollSpeed-label">
-                        AUTOSCROLL SPEED:
-                    </div>
+                    {FormattedTextBox.Focused ? <RichTextMenu /> : null}
+                    <div className="collectionStackingViewChrome-scrollSpeed-label">AUTOSCROLL SPEED:</div>
                     <div className="collection3DCarouselViewChrome-scrollSpeed">
-                        <EditableView
-                            GetValue={() => StrCast(this.scrollSpeed)}
-                            oneLine
-                            SetValue={this.setValue}
-                            contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
+                        <EditableView GetValue={() => StrCast(this.scrollSpeed)} oneLine SetValue={this.setValue} contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
                     </div>
                 </div>
             </div>
@@ -1117,21 +1267,21 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionVi
  */
 @observer
 export class CollectionGridViewChrome extends React.Component<CollectionViewMenuProps> {
-
     private clicked: boolean = false;
     private entered: boolean = false;
     private decrementLimitReached: boolean = false;
     @observable private resize = false;
     private resizeListenerDisposer: Opt<Lambda>;
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
 
     componentDidMount() {
-
-        runInAction(() => this.resize = this.props.docView.props.PanelWidth() < 700);
+        runInAction(() => (this.resize = this.props.docView.props.PanelWidth() < 700));
 
         // listener to reduce text on chrome resize (panel resize)
         this.resizeListenerDisposer = computed(() => this.props.docView.props.PanelWidth()).observe(({ newValue }) => {
-            runInAction(() => this.resize = newValue < 700);
+            runInAction(() => (this.resize = newValue < 700));
         });
     }
 
@@ -1139,14 +1289,16 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         this.resizeListenerDisposer?.();
     }
 
-    get numCols() { return NumCast(this.document.gridNumCols, 10); }
+    get numCols() {
+        return NumCast(this.document.gridNumCols, 10);
+    }
 
     /**
-    * Sets the value of `numCols` on the grid's Document to the value entered.
-    */
+     * Sets the value of `numCols` on the grid's Document to the value entered.
+     */
     onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-        if (e.currentTarget.valueAsNumber > 0) undoBatch(() => this.document.gridNumCols = e.currentTarget.valueAsNumber)();
-    }
+        if (e.currentTarget.valueAsNumber > 0) undoBatch(() => (this.document.gridNumCols = e.currentTarget.valueAsNumber))();
+    };
 
     /**
      * Sets the value of `rowHeight` on the grid's Document to the value entered.
@@ -1166,7 +1318,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
     @undoBatch
     toggleFlex = () => {
         this.document.gridFlex = !BoolCast(this.document.gridFlex, true);
-    }
+    };
 
     /**
      * Increments the value of numCols on button click
@@ -1174,9 +1326,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
     onIncrementButtonClick = () => {
         this.clicked = true;
         this.entered && (this.document.gridNumCols as number)--;
-        undoBatch(() => this.document.gridNumCols = this.numCols + 1)();
+        undoBatch(() => (this.document.gridNumCols = this.numCols + 1))();
         this.entered = false;
-    }
+    };
 
     /**
      * Decrements the value of numCols on button click
@@ -1185,11 +1337,11 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         this.clicked = true;
         if (this.numCols > 1 && !this.decrementLimitReached) {
             this.entered && (this.document.gridNumCols as number)++;
-            undoBatch(() => this.document.gridNumCols = this.numCols - 1)();
+            undoBatch(() => (this.document.gridNumCols = this.numCols - 1))();
             if (this.numCols === 1) this.decrementLimitReached = true;
         }
         this.entered = false;
-    }
+    };
 
     /**
      * Increments the value of numCols on button hover
@@ -1201,7 +1353,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         }
         this.decrementLimitReached = false;
         this.clicked = false;
-    }
+    };
 
     /**
      * Decrements the value of numCols on button hover
@@ -1211,21 +1363,20 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         if (!this.clicked) {
             if (this.numCols > 1) {
                 this.document.gridNumCols = this.numCols - 1;
-            }
-            else {
+            } else {
                 this.decrementLimitReached = true;
             }
         }
 
         this.clicked = false;
-    }
+    };
 
     /**
      * Toggles the value of preventCollision
      */
     toggleCollisions = () => {
         this.document.gridPreventCollision = !this.document.gridPreventCollision;
-    }
+    };
 
     /**
      * Changes the value of the compactType
@@ -1233,16 +1384,26 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
     changeCompactType = (e: React.ChangeEvent<HTMLSelectElement>) => {
         // need to change startCompaction so that this operation will be undoable.
         this.document.gridStartCompaction = e.target.selectedOptions[0].value;
-    }
+    };
 
     render() {
         return (
-            <div className="collectionGridViewChrome-cont" >
-                <span className="grid-control" style={{ width: this.resize ? "25%" : "30%" }}>
+            <div className="collectionGridViewChrome-cont">
+                <span className="grid-control" style={{ width: this.resize ? '25%' : '30%' }}>
                     <span className="grid-icon">
                         <FontAwesomeIcon icon="columns" size="1x" />
                     </span>
-                    <input className="collectionGridViewChrome-entryBox" type="number" value={this.numCols} onChange={this.onNumColsChange} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+                    <input
+                        className="collectionGridViewChrome-entryBox"
+                        type="number"
+                        value={this.numCols}
+                        onChange={this.onNumColsChange}
+                        onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
+                            e.stopPropagation();
+                            e.preventDefault();
+                            e.currentTarget.focus();
+                        }}
+                    />
                     <input className="collectionGridViewChrome-columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
                     <input className="collectionGridViewChrome-columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
                 </span>
@@ -1252,36 +1413,30 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
                     </span>
                     <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
                 </span> */}
-                <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
+                <span className="grid-control" style={{ width: this.resize ? '12%' : '20%' }}>
                     <input type="checkbox" style={{ marginRight: 5 }} onChange={this.toggleCollisions} checked={!this.document.gridPreventCollision} />
-                    <label className="flexLabel">{this.resize ? "Coll" : "Collisions"}</label>
+                    <label className="flexLabel">{this.resize ? 'Coll' : 'Collisions'}</label>
                 </span>
 
-                <select className="collectionGridViewChrome-viewPicker"
+                <select
+                    className="collectionGridViewChrome-viewPicker"
                     style={{ marginRight: 5 }}
                     onPointerDown={stopPropagation}
                     onChange={this.changeCompactType}
                     value={StrCast(this.document.gridStartCompaction, StrCast(this.document.gridCompaction))}>
-                    {["vertical", "horizontal", "none"].map(type =>
-                        <option className="collectionGridViewChrome-viewOption"
-                            onPointerDown={stopPropagation}
-                            value={type}>
-                            {this.resize ? type[0].toUpperCase() + type.substring(1) : "Compact: " + type}
+                    {['vertical', 'horizontal', 'none'].map(type => (
+                        <option className="collectionGridViewChrome-viewOption" onPointerDown={stopPropagation} value={type}>
+                            {this.resize ? type[0].toUpperCase() + type.substring(1) : 'Compact: ' + type}
                         </option>
-                    )}
+                    ))}
                 </select>
 
-                <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
-                    <input style={{ marginRight: 5 }} type="checkbox" onChange={this.toggleFlex}
-                        checked={BoolCast(this.document.gridFlex, true)} />
-                    <label className="flexLabel">{this.resize ? "Flex" : "Flexible"}</label>
+                <span className="grid-control" style={{ width: this.resize ? '12%' : '20%' }}>
+                    <input style={{ marginRight: 5 }} type="checkbox" onChange={this.toggleFlex} checked={BoolCast(this.document.gridFlex, true)} />
+                    <label className="flexLabel">{this.resize ? 'Flex' : 'Flexible'}</label>
                 </span>
 
-                <button onClick={() => this.document.gridResetLayout = true}>
-                    {!this.resize ? "Reset" :
-                        <FontAwesomeIcon icon="redo-alt" size="1x" />}
-                </button>
-
+                <button onClick={() => (this.document.gridResetLayout = true)}>{!this.resize ? 'Reset' : <FontAwesomeIcon icon="redo-alt" size="1x" />}</button>
             </div>
         );
     }
@@ -1289,7 +1444,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
 ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
     const dataField = doc[Doc.LayoutFieldKey(doc)];
     const childDocs = DocListCast(dataField);
-    const currentFrame = Cast(doc._currentFrame, "number", null);
+    const currentFrame = Cast(doc._currentFrame, 'number', null);
     if (currentFrame === undefined) {
         doc._currentFrame = 0;
         CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
@@ -1297,4 +1452,3 @@ ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
     CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
     doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
 });
-
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index b00017453..dcf3f7c51 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,50 +1,33 @@
-import React = require("react");
-import {
-    action,
-    computed,
-    IReactionDisposer,
-    observable,
-    reaction
-} from "mobx";
-import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast } from "../../../fields/Types";
-import {
-    emptyFunction,
-    formatTime,
-    OmitKeys,
-    returnFalse,
-    returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent
-} from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { DocumentManager } from "../../util/DocumentManager";
-import { DragManager } from "../../util/DragManager";
-import { LinkManager } from "../../util/LinkManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { AudioWaveform } from "../AudioWaveform";
-import { CollectionSubView } from "../collections/CollectionSubView";
-import { Colors } from "../global/globalEnums";
-import { LightboxView } from "../LightboxView";
-import {
-    DocAfterFocusFunc,
-    DocFocusFunc,
-    DocumentView,
-    DocumentViewProps
-} from "../nodes/DocumentView";
-import { LabelBox } from "../nodes/LabelBox";
-import "./CollectionStackedTimeline.scss";
-import { VideoBox } from "../nodes/VideoBox";
-import { ImageField } from "../../../fields/URLField";
+import React = require('react');
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { Cast, NumCast } from '../../../fields/Types';
+import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { AudioWaveform } from '../AudioWaveform';
+import { CollectionSubView } from '../collections/CollectionSubView';
+import { Colors } from '../global/globalEnums';
+import { LightboxView } from '../LightboxView';
+import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { LabelBox } from '../nodes/LabelBox';
+import './CollectionStackedTimeline.scss';
+import { VideoBox } from '../nodes/VideoBox';
+import { ImageField } from '../../../fields/URLField';
 
 export type CollectionStackedTimelineProps = {
     Play: () => void;
@@ -68,7 +51,6 @@ export enum TrimScope {
     None = 0,
 }
 
-
 @observer
 export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
     @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
@@ -94,21 +76,38 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     @observable _thumbnail: string | undefined;
 
-    // ensures that clip doesn't get trimmed so small that controls cannot be adjusted anymore 
-    get minTrimLength() { return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5); }
-
-    @computed get trimStart() { return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart; }
-    @computed get trimDuration() { return this.trimEnd - this.trimStart; }
-    @computed get trimEnd() { return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd; }
+    // ensures that clip doesn't get trimmed so small that controls cannot be adjusted anymore
+    get minTrimLength() {
+        return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5);
+    }
 
-    @computed get clipStart() { return this.IsTrimming === TrimScope.All ? 0 : NumCast(this.layoutDoc.clipStart); }
-    @computed get clipDuration() { return this.clipEnd - this.clipStart; }
-    @computed get clipEnd() { return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration); }
+    @computed get trimStart() {
+        return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart;
+    }
+    @computed get trimDuration() {
+        return this.trimEnd - this.trimStart;
+    }
+    @computed get trimEnd() {
+        return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd;
+    }
 
-    @computed get currentTime() { return NumCast(this.layoutDoc._currentTimecode); }
+    @computed get clipStart() {
+        return this.IsTrimming === TrimScope.All ? 0 : NumCast(this.layoutDoc.clipStart);
+    }
+    @computed get clipDuration() {
+        return this.clipEnd - this.clipStart;
+    }
+    @computed get clipEnd() {
+        return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration);
+    }
 
-    @computed get zoomFactor() { return this._zoomFactor; }
+    @computed get currentTime() {
+        return NumCast(this.layoutDoc._currentTimecode);
+    }
 
+    @computed get zoomFactor() {
+        return this._zoomFactor;
+    }
 
     constructor(props: any) {
         super(props);
@@ -117,32 +116,33 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             CollectionStackedTimeline.RangeScript ||
             ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
                 self: Doc.name,
-                scriptContext: "any",
-                clientX: "number",
+                scriptContext: 'any',
+                clientX: 'number',
             })!;
         CollectionStackedTimeline.RangePlayScript =
             CollectionStackedTimeline.RangePlayScript ||
             ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, {
                 self: Doc.name,
-                scriptContext: "any",
-                clientX: "number",
+                scriptContext: 'any',
+                clientX: 'number',
             })!;
     }
 
     componentDidMount() {
-        document.addEventListener("keydown", this.keyEvents, true);
+        document.addEventListener('keydown', this.keyEvents, true);
     }
 
     @action
     componentWillUnmount() {
-        document.removeEventListener("keydown", this.keyEvents, true);
+        document.removeEventListener('keydown', this.keyEvents, true);
         if (CollectionStackedTimeline.SelectingRegion === this) {
             CollectionStackedTimeline.SelectingRegion = undefined;
         }
     }
 
-
-    public get IsTrimming() { return this._trimming; }
+    public get IsTrimming() {
+        return this._trimming;
+    }
 
     @action
     public StartTrimming(scope: TrimScope) {
@@ -162,81 +162,63 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         this._zoomFactor = zoom;
     }
 
-
     anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag]));
     anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this.props.endTag], val) ?? null);
 
-
     // converts screen pixel offset to time
     toTimeline = (screen_delta: number, width: number) => {
-        return Math.max(
-            this.clipStart,
-            Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
-    }
-
+        return Math.max(this.clipStart, Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
+    };
 
     rangeClickScript = () => CollectionStackedTimeline.RangeScript;
     rangePlayScript = () => CollectionStackedTimeline.RangePlayScript;
 
-
     // handles key events for for creating key anchors, scrubbing, exiting trim
     @action
     keyEvents = (e: KeyboardEvent) => {
         if (
             // need to include range inputs because after dragging video time slider it becomes target element
-            !(e.target instanceof HTMLInputElement && !(e.target.type === "range")) &&
+            !(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) &&
             this.props.isSelected(true)
         ) {
             // if shift pressed scrub 1 second otherwise 1/10th
             const jump = e.shiftKey ? 1 : 0.1;
             switch (e.key) {
-                case " ":
+                case ' ':
                     if (!CollectionStackedTimeline.SelectingRegion) {
                         this._markerStart = this._markerEnd = this.currentTime;
                         CollectionStackedTimeline.SelectingRegion = this;
                     } else {
                         this._markerEnd = this.currentTime;
-                        CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this._markerStart,
-                            this._markerEnd
-                        );
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
                         this._markerEnd = undefined;
                         CollectionStackedTimeline.SelectingRegion = undefined;
                     }
                     e.stopPropagation();
                     break;
-                case "Escape":
+                case 'Escape':
                     // abandons current trim
                     this._trimStart = this.clipStart;
                     this._trimStart = this.clipEnd;
                     this._trimming = TrimScope.None;
                     e.stopPropagation();
                     break;
-                case "ArrowLeft":
+                case 'ArrowLeft':
                     this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime - jump), this.clipEnd));
                     e.stopPropagation();
                     break;
-                case "ArrowRight":
+                case 'ArrowRight':
                     this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime + jump), this.clipEnd));
                     e.stopPropagation();
                     break;
             }
         }
-    }
-
+    };
 
     getLinkData(l: Doc) {
         let la1 = l.anchor1 as Doc;
         let la2 = l.anchor2 as Doc;
-        const linkTime = NumCast(
-            la2[this.props.startTag],
-            NumCast(la1[this.props.startTag])
-        );
+        const linkTime = NumCast(la2[this.props.startTag], NumCast(la1[this.props.startTag]));
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
             la1 = l.anchor2 as Doc;
             la2 = l.anchor1 as Doc;
@@ -244,7 +226,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         return { la1, la2, linkTime };
     }
 
-
     // handles dragging selection to create markers
     @action
     onPointerDownTimeline = (e: React.PointerEvent): void => {
@@ -259,7 +240,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             setupMoveUpEvents(
                 this,
                 e,
-                action((e) => {
+                action(e => {
                     if (!wasSelecting) {
                         this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
                         wasSelecting = true;
@@ -274,24 +255,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         this._markerStart = this._markerEnd;
                         this._markerEnd = tmp;
                     }
-                    if (
-                        !isClick &&
-                        Math.abs(movement[0]) > 15 &&
-                        !this.IsTrimming
-                    ) {
-                        const anchor = CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this._markerStart,
-                            this._markerEnd
-                        );
+                    if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
+                        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
                         setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
                     }
-                    (!isClick || !wasSelecting) &&
-                        (this._markerEnd = undefined);
+                    (!isClick || !wasSelecting) && (this._markerEnd = undefined);
                 }),
                 (e, doubleTap) => {
                     if (e.button !== 2) {
@@ -303,23 +271,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 undefined,
                 () => {
                     if (shiftKey) {
-                        CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this.currentTime
-                        );
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
                     } else {
                         !wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
                     }
                 }
             );
         }
-
-    }
-
+    };
 
     @action
     onHover = (e: React.MouseEvent): void => {
@@ -329,15 +288,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         if (rect) {
             this._hoverTime = this.toTimeline(clientX - rect.x, rect.width);
             if (this.dataDoc.thumbnails) {
-                const nearest = Math.floor(this._hoverTime / this.props.rawDuration * VideoBox.numThumbnails);
-                const thumbnails = Cast(this.dataDoc.thumbnails, listSpec("string"), []);
-                const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField("");
-                const src = imgField && imgField.url.href ? imgField.url.href.replace(".png", "_s.png") : "";
+                const nearest = Math.floor((this._hoverTime / this.props.rawDuration) * VideoBox.numThumbnails);
+                const thumbnails = Cast(this.dataDoc.thumbnails, listSpec('string'), []);
+                const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField('');
+                const src = imgField && imgField.url.href ? imgField.url.href.replace('.png', '_s.png') : '';
                 this._thumbnail = src ? src : undefined;
             }
         }
-    }
-
+    };
 
     // for dragging trim start handle
     @action
@@ -348,13 +306,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             e,
             action((e, [], []) => {
                 if (rect && this.props.isContentActive()) {
-                    this._trimStart = Math.min(
-                        Math.max(
-                            this.trimStart + (e.movementX / rect.width) * this.clipDuration,
-                            this.clipStart
-                        ),
-                        this.trimEnd - this.minTrimLength
-                    );
+                    this._trimStart = Math.min(Math.max(this.trimStart + (e.movementX / rect.width) * this.clipDuration, this.clipStart), this.trimEnd - this.minTrimLength);
                 }
                 return false;
             }),
@@ -363,7 +315,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 doubleTap && (this._trimStart = this.clipStart);
             })
         );
-    }
+    };
 
     // for dragging trim end handle
     @action
@@ -374,13 +326,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             e,
             action((e, [], []) => {
                 if (rect && this.props.isContentActive()) {
-                    this._trimEnd = Math.max(
-                        Math.min(
-                            this.trimEnd + (e.movementX / rect.width) * this.clipDuration,
-                            this.clipEnd
-                        ),
-                        this.trimStart + this.minTrimLength
-                    );
+                    this._trimEnd = Math.max(Math.min(this.trimEnd + (e.movementX / rect.width) * this.clipDuration, this.clipEnd), this.trimStart + this.minTrimLength);
                 }
                 return false;
             }),
@@ -389,15 +335,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 doubleTap && (this._trimEnd = this.clipEnd);
             })
         );
-    }
-
+    };
 
     // for rendering scrolling when timeline zoomed
     @action
     setScroll = (e: React.UIEvent) => {
         e.stopPropagation();
         this._scroll = this._timelineWrapper!.scrollLeft;
-    }
+    };
 
     // smooth scrolls to time like when following links overflowed due to zoom
     @action
@@ -406,14 +351,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             if (time > this.toTimeline(this._scroll + this.props.PanelWidth(), this.timelineContentWidth)) {
                 this._scroll = Math.min(this._scroll + this.props.PanelWidth(), this.timelineContentWidth - this.props.PanelWidth());
                 smoothScrollHorizontal(200, this._timelineWrapper, this._scroll);
-            }
-            else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) {
-                this._scroll = time / this.timelineContentWidth * this.clipDuration;
+            } else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) {
+                this._scroll = (time / this.timelineContentWidth) * this.clipDuration;
                 smoothScrollHorizontal(200, this._timelineWrapper, this._scroll);
             }
         }
-    }
-
+    };
 
     // handles dragging and dropping markers in timeline
     @action
@@ -428,9 +371,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         docDragData.droppedDocuments.forEach(drop => {
             const anchorEnd = this.anchorEnd(drop);
             if (anchorEnd !== undefined) {
-                Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : "timecodeToHide", timelinePt + anchorEnd - this.anchorStart(drop), false);
+                Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false);
             }
-            Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : "timecodeToShow", timelinePt, false);
+            Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false);
         });
 
         return true;
@@ -439,38 +382,28 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, 0);
         return false;
-    }
-
+    };
 
     // creates marker on timeline
     @undoBatch
     @action
-    static createAnchor(
-        rootDoc: Doc,
-        dataDoc: Doc,
-        fieldKey: string,
-        startTag: string,
-        endTag: string,
-        anchorStartTime?: number,
-        anchorEndTime?: number,
-        docAnchor?: Doc
-    ) {
+    static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime?: number, anchorEndTime?: number, docAnchor?: Doc) {
         if (anchorStartTime === undefined) return rootDoc;
-        const anchor = docAnchor ?? Docs.Create.LabelDocument({
-            title: ComputedField.MakeFunction(
-                `self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`
-            ) as any,
-            _minFontSize: 12,
-            _maxFontSize: 24,
-            _singleLine: false,
-            _stayInCollection: true,
-            useLinkSmallAnchor: true,
-            hideLinkButton: true,
-            _isLinkButton: true,
-            annotationOn: rootDoc,
-            _timelineLabel: true,
-            borderRounding: anchorEndTime === undefined ? "100%" : undefined
-        });
+        const anchor =
+            docAnchor ??
+            Docs.Create.LabelDocument({
+                title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
+                _minFontSize: 12,
+                _maxFontSize: 24,
+                _singleLine: false,
+                _stayInCollection: true,
+                useLinkSmallAnchor: true,
+                hideLinkButton: true,
+                _isLinkButton: true,
+                annotationOn: rootDoc,
+                _timelineLabel: true,
+                borderRounding: anchorEndTime === undefined ? '100%' : undefined,
+            });
         Doc.GetProto(anchor)[startTag] = anchorStartTime;
         Doc.GetProto(anchor)[endTag] = anchorEndTime;
         if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
@@ -481,7 +414,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         return anchor;
     }
 
-
     @action
     playOnClick = (anchorDoc: Doc, clientX: number) => {
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
@@ -493,10 +425,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 this.scrollToTime(seekTimeInSeconds);
             }
         } else {
-            if (
-                seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) &&
-                endTime > NumCast(this.layoutDoc._currentTimecode)
-            ) {
+            if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && endTime > NumCast(this.layoutDoc._currentTimecode)) {
                 if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) {
                     this.props.Pause();
                 } else {
@@ -508,59 +437,43 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             }
         }
         return { select: true };
-    }
+    };
 
     @action
     clickAnchor = (anchorDoc: Doc, clientX: number) => {
         if (anchorDoc.isLinkButton) {
-            LinkManager.FollowLink(undefined, anchorDoc, this.props, false);
+            LinkFollower.FollowLink(undefined, anchorDoc, this.props, false);
         }
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
         const endTime = this.anchorEnd(anchorDoc);
-        if (
-            seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 &&
-            endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4
-        ) {
+        if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) {
             if (this.props.playing()) this.props.Pause();
             else if (this.layoutDoc.autoPlayAnchors) this.props.Play();
             else if (!this.layoutDoc.autoPlayAnchors) {
                 const rect = this._timeline?.getBoundingClientRect();
-                rect &&
-                    this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
+                rect && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
             }
         } else {
             if (this.layoutDoc.autoPlayAnchors) {
                 this.props.playFrom(seekTimeInSeconds, endTime);
-            }
-            else {
+            } else {
                 this.props.setTime(seekTimeInSeconds);
             }
         }
         return { select: true };
-    }
+    };
 
     // makes sure no anchors overlaps each other by setting the correct position and width
-    getLevel = (
-        m: Doc,
-        placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]
-    ) => {
+    getLevel = (m: Doc, placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]) => {
         const timelineContentWidth = this.timelineContentWidth;
         const x1 = this.anchorStart(m);
-        const x2 = this.anchorEnd(
-            m,
-            x1 + (10 / timelineContentWidth) * this.clipDuration
-        );
+        const x2 = this.anchorEnd(m, x1 + (10 / timelineContentWidth) * this.clipDuration);
         let max = 0;
         const overlappedLevels = new Set(
-            placed.map((p) => {
+            placed.map(p => {
                 const y1 = p.anchorStartTime;
                 const y2 = p.anchorEndTime;
-                if (
-                    (x1 >= y1 && x1 <= y2) ||
-                    (x2 >= y1 && x2 <= y2) ||
-                    (y1 >= x1 && y1 <= x2) ||
-                    (y2 >= x1 && y2 <= x2)
-                ) {
+                if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) || (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) {
                     max = Math.max(max, p.level);
                     return p.level;
                 }
@@ -571,14 +484,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
         placed.push({ anchorStartTime: x1, anchorEndTime: x2, level });
         return level;
-    }
-
+    };
 
     dictationHeightPercent = 50;
     dictationHeight = () => (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100;
 
-    @computed get timelineContentHeight() { return this.props.PanelHeight() * this.dictationHeightPercent / 100; }
-    @computed get timelineContentWidth() { return this.props.PanelWidth() * this.zoomFactor; }  // subtract size of container border
+    @computed get timelineContentHeight() {
+        return (this.props.PanelHeight() * this.dictationHeightPercent) / 100;
+    }
+    @computed get timelineContentWidth() {
+        return this.props.PanelWidth() * this.zoomFactor;
+    } // subtract size of container border
 
     dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight);
 
@@ -586,24 +502,18 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     currentTimecode = () => this.currentTime;
 
-
     @computed get renderDictation() {
         const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null);
         return !dictation ? null : (
             <div
                 style={{
-                    position: "absolute",
-                    height: "100%",
+                    position: 'absolute',
+                    height: '100%',
                     top: this.timelineContentHeight,
                     background: Colors.LIGHT_BLUE,
-                }}
-            >
+                }}>
                 <DocumentView
-                    {...OmitKeys(this.props, [
-                        "NativeWidth",
-                        "NativeHeight",
-                        "setContentView",
-                    ]).omit}
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                     Document={dictation}
                     PanelHeight={this.dictationHeight}
                     isAnnotationOverlay={true}
@@ -618,8 +528,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                     moveDocument={returnFalse}
                     addDocument={returnFalse}
                     CollectionView={undefined}
-                    renderDepth={this.props.renderDepth + 1}
-                ></DocumentView>
+                    renderDepth={this.props.renderDepth + 1}></DocumentView>
             </div>
         );
     }
@@ -644,145 +553,131 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             anchorEndTime: number;
             level: number;
         }[] = [];
-        const drawAnchors = this.childDocs.map((anchor) => ({
+        const drawAnchors = this.childDocs.map(anchor => ({
             level: this.getLevel(anchor, overlaps),
             anchor,
         }));
         const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
-        return (<div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined }}>
-            <div className="timeline-container"
-                style={{ width: this.props.PanelWidth() }}
-                onWheel={e => e.stopPropagation()}
-                onScroll={this.setScroll}
-                onMouseMove={(e) => this.isContentActive() && this.onHover(e)}
-                ref={wrapper => this._timelineWrapper = wrapper}>
+        return (
+            <div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}>
                 <div
-                    className="collectionStackedTimeline"
-                    ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
-                    onClick={(e) => this.isContentActive() && StopEvent(e)}
-                    onPointerDown={(e) => this.isContentActive() && this.onPointerDownTimeline(e)}
-                    style={{ width: this.timelineContentWidth }}>
-
-                    {drawAnchors.map((d) => {
-                        const start = this.anchorStart(d.anchor);
-                        const end = this.anchorEnd(
-                            d.anchor,
-                            start + (10 / this.timelineContentWidth) * this.clipDuration
-                        );
-                        if (end < this.clipStart || start > this.clipEnd) return (null);
-                        const left = Math.max((start - this.clipStart) / this.clipDuration * this.timelineContentWidth, 0);
-                        const top = (d.level / maxLevel) * this.props.PanelHeight();
-                        const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart);
-                        const width = (timespan / this.clipDuration) * this.timelineContentWidth;
-                        const height = this.props.PanelHeight() / maxLevel;
-                        return this.props.Document.hideAnchors ? null : (
-                            <div
-                                className={"collectionStackedTimeline-marker-timeline"}
-                                key={d.anchor[Id]}
-                                style={{
-                                    left,
-                                    top,
-                                    width: `${width}px`,
-                                    height: `${height}px`,
-                                }}
-                                onClick={(e) => {
-                                    this.props.playFrom(start, this.anchorEnd(d.anchor));
-                                    e.stopPropagation();
-                                }}
-                            >
-                                <StackedTimelineAnchor
-                                    {...this.props}
-                                    mark={d.anchor}
-                                    rangeClickScript={this.rangeClickScript}
-                                    rangePlayScript={this.rangePlayScript}
-                                    left={left - this._scroll}
-                                    top={top}
-                                    width={width}
-                                    height={height}
-                                    toTimeline={this.toTimeline}
-                                    layoutDoc={this.layoutDoc}
-                                    // isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
-                                    currentTimecode={this.currentTimecode}
-                                    _timeline={this._timeline}
-                                    stackedTimeline={this}
-                                    trimStart={this.trimStart}
-                                    trimEnd={this.trimEnd}
-                                />
-                            </div>
-                        );
-                    })}
-                    {!this.IsTrimming && this.selectionContainer}
-                    <AudioWaveform
-                        rawDuration={this.props.rawDuration}
-                        duration={this.clipDuration}
-                        mediaPath={this.props.mediaPath}
-                        layoutDoc={this.layoutDoc}
-                        clipStart={this.clipStart}
-                        clipEnd={this.clipEnd}
-                        zoomFactor={this.zoomFactor}
-                        PanelHeight={this.timelineContentHeight}
-                        PanelWidth={this.timelineContentWidth}
-                    />
-                    {/* {this.renderDictation} */}
-
+                    className="timeline-container"
+                    style={{ width: this.props.PanelWidth() }}
+                    onWheel={e => e.stopPropagation()}
+                    onScroll={this.setScroll}
+                    onMouseMove={e => this.isContentActive() && this.onHover(e)}
+                    ref={wrapper => (this._timelineWrapper = wrapper)}>
                     <div
-                        className="collectionStackedTimeline-hover"
-                        style={{
-                            left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`,
-                        }}
-                    />
+                        className="collectionStackedTimeline"
+                        ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
+                        onClick={e => this.isContentActive() && StopEvent(e)}
+                        onPointerDown={e => this.isContentActive() && this.onPointerDownTimeline(e)}
+                        style={{ width: this.timelineContentWidth }}>
+                        {drawAnchors.map(d => {
+                            const start = this.anchorStart(d.anchor);
+                            const end = this.anchorEnd(d.anchor, start + (10 / this.timelineContentWidth) * this.clipDuration);
+                            if (end < this.clipStart || start > this.clipEnd) return null;
+                            const left = Math.max(((start - this.clipStart) / this.clipDuration) * this.timelineContentWidth, 0);
+                            const top = (d.level / maxLevel) * this.props.PanelHeight();
+                            const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart);
+                            const width = (timespan / this.clipDuration) * this.timelineContentWidth;
+                            const height = this.props.PanelHeight() / maxLevel;
+                            return this.props.Document.hideAnchors ? null : (
+                                <div
+                                    className={'collectionStackedTimeline-marker-timeline'}
+                                    key={d.anchor[Id]}
+                                    style={{
+                                        left,
+                                        top,
+                                        width: `${width}px`,
+                                        height: `${height}px`,
+                                    }}
+                                    onClick={e => {
+                                        this.props.playFrom(start, this.anchorEnd(d.anchor));
+                                        e.stopPropagation();
+                                    }}>
+                                    <StackedTimelineAnchor
+                                        {...this.props}
+                                        mark={d.anchor}
+                                        rangeClickScript={this.rangeClickScript}
+                                        rangePlayScript={this.rangePlayScript}
+                                        left={left - this._scroll}
+                                        top={top}
+                                        width={width}
+                                        height={height}
+                                        toTimeline={this.toTimeline}
+                                        layoutDoc={this.layoutDoc}
+                                        // isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+                                        currentTimecode={this.currentTimecode}
+                                        _timeline={this._timeline}
+                                        stackedTimeline={this}
+                                        trimStart={this.trimStart}
+                                        trimEnd={this.trimEnd}
+                                    />
+                                </div>
+                            );
+                        })}
+                        {!this.IsTrimming && this.selectionContainer}
+                        <AudioWaveform
+                            rawDuration={this.props.rawDuration}
+                            duration={this.clipDuration}
+                            mediaPath={this.props.mediaPath}
+                            layoutDoc={this.layoutDoc}
+                            clipStart={this.clipStart}
+                            clipEnd={this.clipEnd}
+                            zoomFactor={this.zoomFactor}
+                            PanelHeight={this.timelineContentHeight}
+                            PanelWidth={this.timelineContentWidth}
+                        />
+                        {/* {this.renderDictation} */}
+
+                        <div
+                            className="collectionStackedTimeline-hover"
+                            style={{
+                                left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`,
+                            }}
+                        />
+
+                        <div
+                            className="collectionStackedTimeline-current"
+                            style={{
+                                left: `${((this.currentTime - this.clipStart) / this.clipDuration) * 100}%`,
+                            }}
+                        />
+
+                        {this.IsTrimming !== TrimScope.None && (
+                            <>
+                                <div className="collectionStackedTimeline-trim-shade" style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}></div>
 
-                    <div
-                        className="collectionStackedTimeline-current"
-                        style={{
-                            left: `${((this.currentTime - this.clipStart) / this.clipDuration) * 100}%`,
-                        }}
-                    />
-
-                    {this.IsTrimming !== TrimScope.None && (
-                        <>
-                            <div
-                                className="collectionStackedTimeline-trim-shade"
-                                style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}
-                            ></div>
-
-                            <div
-                                className="collectionStackedTimeline-trim-controls"
-                                style={{
-                                    left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
-                                    width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
-                                }}
-                            >
                                 <div
-                                    className="collectionStackedTimeline-trim-handle"
-                                    onPointerDown={this.trimLeft}
-                                ></div>
+                                    className="collectionStackedTimeline-trim-controls"
+                                    style={{
+                                        left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
+                                        width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
+                                    }}>
+                                    <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimLeft}></div>
+                                    <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimRight}></div>
+                                </div>
+
                                 <div
-                                    className="collectionStackedTimeline-trim-handle"
-                                    onPointerDown={this.trimRight}
-                                ></div>
-                            </div>
-
-                            <div
-                                className="collectionStackedTimeline-trim-shade"
-                                style={{
-                                    left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
-                                    width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
-                                }}
-                            ></div>
-                        </>
-                    )}
+                                    className="collectionStackedTimeline-trim-shade"
+                                    style={{
+                                        left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
+                                        width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
+                                    }}></div>
+                            </>
+                        )}
+                    </div>
+                </div>
+                <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
+                    <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
+                    {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
                 </div>
             </div>
-            <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
-                <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
-                {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
-            </div>
-        </div >);
+        );
     }
 }
 
-
 /**
  * StackedTimelineAnchor
  * creates the anchors to display markers, links, and embedded documents on timeline
@@ -814,7 +709,6 @@ interface StackedTimelineAnchorProps {
     trimEnd: number;
 }
 
-
 @observer
 class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> {
     _lastTimecode: number;
@@ -831,23 +725,14 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         const start = Math.max(NumCast(this.props.mark[this.props.startTag]), this.props.trimStart) - this.props.trimStart;
         const end = Math.min(NumCast(this.props.mark[this.props.endTag]), this.props.trimEnd) - this.props.trimStart;
         return `#${formatTime(start)}-${formatTime(end)}`;
-    }
+    };
 
     componentDidMount() {
         this._disposer = reaction(
             () => this.props.currentTimecode(),
-            (time) => {
-                const dictationDoc = Cast(
-                    this.props.layoutDoc["data-dictation"],
-                    Doc,
-                    null
-                );
-                const isDictation =
-                    dictationDoc &&
-                    DocListCast(this.props.mark.links).some(
-                        (link) =>
-                            Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc
-                    );
+            time => {
+                const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null);
+                const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc);
                 if (
                     !LightboxView.LightboxDoc &&
                     // bcz: when should links be followed?  we don't want to move away from the video to follow a link but we can open it in a sidebar/etc.  But we don't know that upfront.
@@ -859,13 +744,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     time < NumCast(this.props.mark[this.props.endTag]) &&
                     this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5
                 ) {
-                    LinkManager.FollowLink(
-                        undefined,
-                        this.props.mark,
-                        this.props as any as DocumentViewProps,
-                        false,
-                        true
-                    );
+                    LinkFollower.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false, true);
                 }
                 this._lastTimecode = time;
             }
@@ -876,7 +755,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         this._disposer?.();
     }
 
-
     // starting the drag event for anchor resizing
     onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => {
         this.props._timeline?.setPointerCapture(e.pointerId);
@@ -885,19 +763,13 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             return this.props.toTimeline(e.clientX - rect.x, rect.width);
         };
         const changeAnchor = (anchor: Doc, left: boolean, time: number | undefined) => {
-            const timelineOnly = Cast(anchor[this.props.startTag], "number", null) !== undefined;
+            const timelineOnly = Cast(anchor[this.props.startTag], 'number', null) !== undefined;
             if (timelineOnly) {
                 if (!left && time !== undefined && time <= NumCast(anchor[this.props.startTag])) time = undefined;
-                Doc.SetInPlace(
-                    anchor,
-                    left ? this.props.startTag : this.props.endTag,
-                    time,
-                    true
-                );
-                if (!left) Doc.SetInPlace(anchor, "borderRounding", time !== undefined ? undefined : "100%", true);
-            }
-            else {
-                anchor[left ? "_timecodeToShow" : "_timecodeToHide"] = time;
+                Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true);
+                if (!left) Doc.SetInPlace(anchor, 'borderRounding', time !== undefined ? undefined : '100%', true);
+            } else {
+                anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time;
             }
             return false;
         };
@@ -906,46 +778,34 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         setupMoveUpEvents(
             this,
             e,
-            (e) => {
-                if (!undo) undo = UndoManager.StartBatch("drag anchor");
+            e => {
+                if (!undo) undo = UndoManager.StartBatch('drag anchor');
                 this.props.setTime(newTime(e));
                 return changeAnchor(anchor, left, newTime(e));
             },
-            (e) => {
+            e => {
                 this.props.setTime(newTime(e));
                 this.props._timeline?.releasePointerCapture(e.pointerId);
                 undo?.end();
             },
             emptyFunction
         );
-    }
-
+    };
 
     // context menu
     contextMenuItems = () => {
-        const resetTitle = { script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!, icon: "folder-plus", label: "Reset Title" };
+        const resetTitle = {
+            script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!,
+            icon: 'folder-plus',
+            label: 'Reset Title',
+        };
         return [resetTitle];
-    }
-
+    };
 
     // renders anchor LabelBox
-    renderInner = computedFn(function (
-        this: StackedTimelineAnchor,
-        mark: Doc,
-        script: undefined | (() => ScriptField),
-        doublescript: undefined | (() => ScriptField),
-        screenXf: () => Transform,
-        width: () => number,
-        height: () => number
-    ) {
+    renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
         const anchor = observable({ view: undefined as any });
-        const focusFunc = (
-            doc: Doc,
-            willZoom?: boolean,
-            scale?: number,
-            afterFocus?: DocAfterFocusFunc,
-            docTransform?: Transform
-        ) => {
+        const focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => {
             this.props.playLink(mark);
             this.props.focus(doc, { willZoom, scale, afterFocus, docTransform });
         };
@@ -954,13 +814,13 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             view: (
                 <DocumentView
                     key="view"
-                    {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                    ref={action((r: DocumentView | null) => anchor.view = r)}
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+                    ref={action((r: DocumentView | null) => (anchor.view = r))}
                     Document={mark}
                     DataDoc={undefined}
                     renderDepth={this.props.renderDepth + 1}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LabelBox.LayoutStringWithTitle("data", this.computeTitle())}
+                    LayoutTemplateString={LabelBox.LayoutStringWithTitle('data', this.computeTitle())}
                     isDocumentActive={this.props.isDocumentActive}
                     PanelWidth={width}
                     PanelHeight={height}
@@ -985,32 +845,14 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
     height = () => this.props.height;
 
     render() {
-        const inner = this.renderInner(
-            this.props.mark,
-            this.props.rangeClickScript,
-            this.props.rangePlayScript,
-            this.anchorScreenToLocalXf,
-            this.width,
-            this.height
-        );
+        const inner = this.renderInner(this.props.mark, this.props.rangeClickScript, this.props.rangePlayScript, this.anchorScreenToLocalXf, this.width, this.height);
         return (
             <>
                 {inner.view}
-                {!inner.anchor.view ||
-                    !SelectionManager.IsSelected(inner.anchor.view) ? null : (
+                {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : (
                     <>
-                        <div
-                            key="left"
-                            className="collectionStackedTimeline-left-resizer"
-                            onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
-                        />
-                        <div
-                            key="right"
-                            className="collectionStackedTimeline-resizer"
-                            onPointerDown={(e) =>
-                                this.onAnchorDown(e, this.props.mark, false)
-                            }
-                        />
+                        <div key="left" className="collectionStackedTimeline-left-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, true)} />
+                        <div key="right" className="collectionStackedTimeline-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, false)} />
                     </>
                 )}
             </>
@@ -1025,4 +867,4 @@ ScriptingGlobals.add(function min(num1: number, num2: number): number {
 });
 ScriptingGlobals.add(function max(num1: number, num2: number): number {
     return Math.max(num1, num2);
-});
\ No newline at end of file
+});
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 7d40cab8c..6850fb23a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -12,6 +12,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
+import { CollectionViewType } from '../../documents/DocumentTypes';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
@@ -22,14 +23,13 @@ import { EditableView } from '../EditableView';
 import { LightboxView } from '../LightboxView';
 import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
 import './CollectionStackingView.scss';
 import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
 import { CollectionSubView } from './CollectionSubView';
-import { CollectionViewType } from './CollectionView';
-import { FieldViewProps } from '../nodes/FieldView';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionStackingViewProps = {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 03450b798..5479929bd 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,24 +1,23 @@
-import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx";
-import CursorField from "../../../fields/CursorField";
-import { Doc, Opt, Field, DocListCast, AclPrivate, StrListCast } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ScriptField } from "../../../fields/ScriptField";
-import { WebField } from "../../../fields/URLField";
-import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
-import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
-import { DocServer } from "../../DocServer";
-import { ImageUtils } from "../../util/Import & Export/ImageUtils";
-import { InteractionUtils } from "../../util/InteractionUtils";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { DocComponent } from "../DocComponent";
-import React = require("react");
+import { action, computed, observable } from 'mobx';
 import ReactLoading from 'react-loading';
 import * as rp from 'request-promise';
-import { Networking } from "../../Network";
-
+import CursorField from '../../../fields/CursorField';
+import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
+import { WebField } from '../../../fields/URLField';
+import { GestureUtils } from '../../../pen-gestures/GestureUtils';
+import { returnFalse, Utils } from '../../../Utils';
+import { DocServer } from '../../DocServer';
+import { Networking } from '../../Network';
+import { ImageUtils } from '../../util/Import & Export/ImageUtils';
+import { InteractionUtils } from '../../util/InteractionUtils';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { DocComponent } from '../DocComponent';
+import React = require('react');
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
@@ -33,7 +32,8 @@ export function CollectionSubView<X>(moreProps?: X) {
         protected _mainCont?: HTMLDivElement;
         @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
         @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
-        protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+        protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+            //used for stacking and masonry view
             this.dropDisposer?.();
             this.gestureDisposer?.();
             this._multiTouchDisposer?.();
@@ -43,8 +43,9 @@ export function CollectionSubView<X>(moreProps?: X) {
                 this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
                 this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
             }
-        }
-        protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view
+        };
+        protected CreateDropTarget(ele: HTMLDivElement) {
+            //used in schema view
             this.createDashEventsTarget(ele);
         }
 
@@ -54,13 +55,12 @@ export function CollectionSubView<X>(moreProps?: X) {
         }
 
         @computed get dataDoc() {
-            return (this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) :
-                this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document)); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
+            return this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
         }
 
         rootSelected = (outsideReaction?: boolean) => {
             return this.props.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected(outsideReaction));
-        }
+        };
 
         // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
         // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through
@@ -73,10 +73,12 @@ export function CollectionSubView<X>(moreProps?: X) {
             return this.dataDoc[this.props.fieldKey];
         }
 
-        get childLayoutPairs(): { layout: Doc; data: Doc; }[] {
+        get childLayoutPairs(): { layout: Doc; data: Doc }[] {
             const { Document, DataDoc } = this.props;
-            const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc)).
-                filter(pair => {  // filter out any documents that have a proto that we don't have permissions to
+            const validPairs = this.childDocs
+                .map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc))
+                .filter(pair => {
+                    // filter out any documents that have a proto that we don't have permissions to
                     return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));
                 });
             return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
@@ -85,21 +87,24 @@ export function CollectionSubView<X>(moreProps?: X) {
             return Cast(this.dataField, listSpec(Doc));
         }
         collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters);
-        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
+        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec('string'), []);
         childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
         unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
         childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
-        IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
-            this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
+        IsFiltered = () =>
+            this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined;
         searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
             let rawdocs: (Doc | Promise<Doc>)[] = [];
-            if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
+            if (this.dataField instanceof Doc) {
+                // if collection data is just a document, then promote it to a singleton list;
                 rawdocs = [this.dataField];
-            } else if (Cast(this.dataField, listSpec(Doc), null)) { // otherwise, if the collection data is a list, then use it.  
+            } else if (Cast(this.dataField, listSpec(Doc), null)) {
+                // otherwise, if the collection data is a list, then use it.
                 rawdocs = Cast(this.dataField, listSpec(Doc), null);
-            } else {   // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
+            } else {
+                // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
                 // For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection.
                 // Since the data field is actually an image, we set the list of documents to the singleton of root document's proto which will be an image.
                 const rootDoc = Cast(this.props.Document.rootDocument, Doc, null);
@@ -117,19 +122,19 @@ export function CollectionSubView<X>(moreProps?: X) {
                 return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
             }
 
-            // console.log(CurrentUserUtils.ActiveDashboard._docFilters);
+            // console.log(Doc.ActiveDashboard._docFilters);
             // if (!this.props.Document._docFilters && this.props.Document.currentFilter) {
             //     (this.props.Document.currentFilter as Doc).filterBoolean = (this.props.ContainingCollectionDoc?.currentFilter as Doc)?.filterBoolean;
             // }
             const docsforFilter: Doc[] = [];
-            childDocs.forEach((d) => {
+            childDocs.forEach(d => {
                 // if (DocUtils.Excluded(d, docFilters)) return;
-                let notFiltered = d.z || Doc.IsSystem(d) || (DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0);
+                let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0;
                 if (notFiltered) {
-                    notFiltered = ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0));
+                    notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0;
                     const fieldKey = Doc.LayoutFieldKey(d);
-                    const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                    const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+                    const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+                    const data = d[annos ? fieldKey + '-annotations' : fieldKey];
                     if (data !== undefined) {
                         let subDocs = DocListCast(data);
                         if (subDocs.length > 0) {
@@ -137,11 +142,12 @@ export function CollectionSubView<X>(moreProps?: X) {
                             notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, viewSpecScript, d).length);
                             while (subDocs.length > 0 && !notFiltered) {
                                 newarray = [];
-                                subDocs.forEach((t) => {
+                                subDocs.forEach(t => {
                                     const fieldKey = Doc.LayoutFieldKey(t);
-                                    const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
-                                    notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, viewSpecScript, d).length));
-                                    DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+                                    const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
+                                    notFiltered =
+                                        notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, viewSpecScript, d).length));
+                                    DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                                 });
                                 subDocs = newarray;
                             }
@@ -157,7 +163,7 @@ export function CollectionSubView<X>(moreProps?: X) {
         protected async setCursorPosition(position: [number, number]) {
             let ind;
             const doc = this.props.Document;
-            const id = CurrentUserUtils.id;
+            const id = Doc.UserDoc()[Id];
             const email = Doc.CurrentUserEmail;
             const pos = { x: position[0], y: position[1] };
             if (id && email) {
@@ -167,7 +173,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 }
                 // The following conditional detects a recurring bug we've seen on the server
                 if (proto[Id] === Docs.Prototypes.get(DocumentType.COL)[Id]) {
-                    alert("COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...");
+                    alert('COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...');
                     console.log(doc);
                     console.log(proto);
                     throw new Error(`AHA! You were trying to set a cursor on a collection's proto, which is the original collection proto! Look at the two previously printed lines for document values!`);
@@ -186,8 +192,7 @@ export function CollectionSubView<X>(moreProps?: X) {
         }
 
         @undoBatch
-        protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {
-        }
+        protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {}
 
         protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
             if (de.complete.docDragData) {
@@ -210,12 +215,16 @@ export function CollectionSubView<X>(moreProps?: X) {
                 const dropAction = docDragData.dropAction || docDragData.userDropAction;
                 const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
                 const someMoved = !docDragData.userDropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
-                if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop);
-                if ((!dropAction || dropAction === "same" || dropAction === "move" || someMoved) && docDragData.moveDocument) {
+                if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
+                if ((!dropAction || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
-                        const canAdd = this.props.Document._viewType === CollectionViewType.Pile || de.embedKey || (!this.props.isAnnotationOverlay || this.props.Document.allowOverlayDrop) ||
+                        const canAdd =
+                            this.props.Document._viewType === CollectionViewType.Pile ||
+                            de.embedKey ||
+                            !this.props.isAnnotationOverlay ||
+                            this.props.Document.allowOverlayDrop ||
                             Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
                         added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
                     } else {
@@ -228,11 +237,10 @@ export function CollectionSubView<X>(moreProps?: X) {
                     ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
                     added = this.addDocument(docDragData.droppedDocuments);
                 }
-                !added && alert("You cannot perform this move");
+                !added && alert('You cannot perform this move');
                 e.stopPropagation();
                 return added;
-            }
-            else if (de.complete.annoDragData) {
+            } else if (de.complete.annoDragData) {
                 const dropCreator = de.complete.annoDragData.dropDocCreator;
                 de.complete.annoDragData.dropDocCreator = () => {
                     const dropped = dropCreator(this.props.isAnnotationOverlay ? this.rootDoc : undefined);
@@ -253,11 +261,11 @@ export function CollectionSubView<X>(moreProps?: X) {
             }
 
             const { dataTransfer } = e;
-            const html = dataTransfer.getData("text/html");
-            const text = dataTransfer.getData("text/plain");
-            const uriList = dataTransfer.getData("text/uri-list");
+            const html = dataTransfer.getData('text/html');
+            const text = dataTransfer.getData('text/plain');
+            const uriList = dataTransfer.getData('text/uri-list');
 
-            if (text && text.startsWith("<div")) {
+            if (text && text.startsWith('<div')) {
                 return;
             }
 
@@ -271,11 +279,15 @@ export function CollectionSubView<X>(moreProps?: X) {
                     const href = FormattedTextBox.GetHref(html);
                     if (href) {
                         const docid = FormattedTextBox.GetDocFromUrl(href);
-                        if (docid) { // prosemirror text containing link to dash document
+                        if (docid) {
+                            // prosemirror text containing link to dash document
                             DocServer.GetRefField(docid).then(f => {
                                 if (f instanceof Doc) {
-                                    if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number; } // should be in CollectionFreeFormView
-                                    (f instanceof Doc) && addDocument(f);
+                                    if (options.x || options.y) {
+                                        f.x = options.x as number;
+                                        f.y = options.y as number;
+                                    } // should be in CollectionFreeFormView
+                                    f instanceof Doc && addDocument(f);
                                 }
                             });
                         } else {
@@ -286,47 +298,50 @@ export function CollectionSubView<X>(moreProps?: X) {
                     }
                     return;
                 }
-                if (!html.startsWith("<a")) {
-                    const tags = html.split("<");
-                    if (tags[0] === "") tags.splice(0, 1);
-                    let img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
-                    const cors = img.includes("corsProxy") ? img.match(/http.*corsProxy\//)![0] : "";
-                    img = cors ? img.replace(cors, "") : img;
+                if (!html.startsWith('<a')) {
+                    const tags = html.split('<');
+                    if (tags[0] === '') tags.splice(0, 1);
+                    let img = tags[0].startsWith('img') ? tags[0] : tags.length > 1 && tags[1].startsWith('img') ? tags[1] : '';
+                    const cors = img.includes('corsProxy') ? img.match(/http.*corsProxy\//)![0] : '';
+                    img = cors ? img.replace(cors, '') : img;
                     if (img) {
-                        const split = img.split("src=\"")[1].split("\"")[0];
+                        const split = img.split('src="')[1].split('"')[0];
                         let source = split;
-                        if (split.startsWith("data:image") && split.includes("base64")) {
-                            const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
+                        if (split.startsWith('data:image') && split.includes('base64')) {
+                            const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [split] });
                             source = Utils.prepend(accessPaths.agnostic.client);
                         }
-                        if (source.startsWith("http")) {
+                        if (source.startsWith('http')) {
                             const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
                             ImageUtils.ExtractExif(doc);
                             addDocument(doc);
                         }
                         return;
                     } else {
-                        const path = window.location.origin + "/doc/";
+                        const path = window.location.origin + '/doc/';
                         if (text.startsWith(path)) {
-                            const docid = text.replace(Doc.globalServerPath(), "").split("?")[0];
+                            const docid = text.replace(Doc.globalServerPath(), '').split('?')[0];
                             DocServer.GetRefField(docid).then(f => {
                                 if (f instanceof Doc) {
-                                    if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number;  } // should be in CollectionFreeFormView
-                                    (f instanceof Doc) && addDocument(f);
+                                    if (options.x || options.y) {
+                                        f.x = options.x as number;
+                                        f.y = options.y as number;
+                                    } // should be in CollectionFreeFormView
+                                    f instanceof Doc && addDocument(f);
                                 }
                             });
                         } else {
                             const srcWeb = SelectionManager.Views().lastElement();
                             const srcUrl = (srcWeb?.Document.data as WebField)?.url?.href?.match(/https?:\/\/[^/]*/)?.[0];
-                            const reg = new RegExp(Utils.prepend(""), "g");
+                            const reg = new RegExp(Utils.prepend(''), 'g');
                             const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
-                            const backgroundColor = tags.map(tag => tag.match(/.*(background-color: ?[^;]*)/)?.[1]?.replace(/background-color: ?(.*)/, "$1")).filter(t => t)?.[0];
-                            const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: srcUrl ? "from:" + srcUrl : "-web clip-", _width: 300, _height: 300, backgroundColor });
-                            Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text;
+                            const backgroundColor = tags.map(tag => tag.match(/.*(background-color: ?[^;]*)/)?.[1]?.replace(/background-color: ?(.*)/, '$1')).filter(t => t)?.[0];
+                            const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: srcUrl ? 'from:' + srcUrl : '-web clip-', _width: 300, _height: 300, backgroundColor });
+                            Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text;
                             addDocument(htmlDoc);
                             if (srcWeb) {
-                                const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName("iframe")?.[0];
-                                const focusNode = (iframe?.contentDocument?.getSelection()?.focusNode as any);
+                                const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
+                                const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
                                 if (focusNode) {
                                     const anchor = srcWeb?.ComponentView?.getAnchor?.();
                                     anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor });
@@ -339,11 +354,10 @@ export function CollectionSubView<X>(moreProps?: X) {
             }
 
             if (uriList || text) {
-                if ((uriList || text).includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) {
-
-                    const batch = UndoManager.StartBatch("youtube upload");
+                if ((uriList || text).includes('www.youtube.com/watch') || text.includes('www.youtube.com/embed')) {
+                    const batch = UndoManager.StartBatch('youtube upload');
                     const generatedDocuments: Doc[] = [];
-                    this.slowLoadDocuments((uriList || text).split("v=")[1].split("&")[0], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
+                    this.slowLoadDocuments((uriList || text).split('v=')[1].split('&')[0], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
 
                     return;
                 }
@@ -374,15 +388,16 @@ export function CollectionSubView<X>(moreProps?: X) {
                 //     alias._height = 512;
                 //     alias._width = 400;
                 //     addDocument(alias);
-                // } else 
+                // } else
                 {
-                    const newDoc = Docs.Create.WebDocument(uriList.split("#annotations:")[0], {// clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
+                    const newDoc = Docs.Create.WebDocument(uriList.split('#annotations:')[0], {
+                        // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
                         ...options,
-                        title: uriList.split("#annotations:")[0],
+                        title: uriList.split('#annotations:')[0],
                         _width: 400,
                         _height: 512,
                         _nativeWidth: 850,
-                        useCors: true
+                        useCors: true,
                     });
                     addDocument(newDoc);
                 }
@@ -394,82 +409,99 @@ export function CollectionSubView<X>(moreProps?: X) {
             const files: File[] = [];
             const generatedDocuments: Doc[] = [];
             if (!length) {
-                alert("No uploadable content found.");
+                alert('No uploadable content found.');
                 return;
             }
 
-            const batch = UndoManager.StartBatch("collection view drop");
+            const batch = UndoManager.StartBatch('collection view drop');
             for (let i = 0; i < length; i++) {
                 const item = e.dataTransfer.items[i];
-                if (item.kind === "string" && item.type.includes("uri")) {
+                if (item.kind === 'string' && item.type.includes('uri')) {
                     const stringContents = await new Promise<string>(resolve => item.getAsString(resolve));
-                    const type = (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
+                    const type = (await rp.head(Utils.CorsProxy(stringContents)))['content-type'];
                     if (type) {
                         const doc = await DocUtils.DocumentFromType(type, Utils.CorsProxy(stringContents), options);
                         doc && generatedDocuments.push(doc);
                     }
                 }
-                if (item.kind === "file") {
+                if (item.kind === 'file') {
                     const file = item.getAsFile();
                     file?.type && files.push(file);
 
-                    file?.type === "application/json" && Utils.readUploadedFileAsText(file).then(result => {
-                        const json = JSON.parse(result as string);
-                        addDocument(Docs.Create.TreeDocument(
-                            json["rectangular-puzzle"].crossword.clues[0].clue.map((c: any) => {
-                                const label = Docs.Create.LabelDocument({ title: c["#text"], _width: 120, _height: 20 });
-                                const proto = Doc.GetProto(label);
-                                proto._width = 120;
-                                proto._height = 20;
-                                return proto;
-                            }
-                            ), { _width: 150, _height: 600, title: "across", backgroundColor: "white", _singleLine: true }));
-                    });
+                    file?.type === 'application/json' &&
+                        Utils.readUploadedFileAsText(file).then(result => {
+                            const json = JSON.parse(result as string);
+                            addDocument(
+                                Docs.Create.TreeDocument(
+                                    json['rectangular-puzzle'].crossword.clues[0].clue.map((c: any) => {
+                                        const label = Docs.Create.LabelDocument({ title: c['#text'], _width: 120, _height: 20 });
+                                        const proto = Doc.GetProto(label);
+                                        proto._width = 120;
+                                        proto._height = 20;
+                                        return proto;
+                                    }),
+                                    { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _singleLine: true }
+                                )
+                            );
+                        });
                 }
             }
             this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
         }
-        slowLoadDocuments = async (files: (File[] | string), options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: ((doc: Doc[]) => void) | undefined, clientX: number, clientY: number, addDocument: (doc: Doc | Doc[]) => boolean) => {
-            const disposer = OverlayView.Instance.addElement(
-                <ReactLoading type={"spinningBubbles"} color={"green"} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
-            if (typeof files === "string") {
-                generatedDocuments.push(...await DocUtils.uploadYoutubeVideo(files, options));
+        slowLoadDocuments = async (
+            files: File[] | string,
+            options: DocumentOptions,
+            generatedDocuments: Doc[],
+            text: string,
+            completed: ((doc: Doc[]) => void) | undefined,
+            clientX: number,
+            clientY: number,
+            addDocument: (doc: Doc | Doc[]) => boolean
+        ) => {
+            const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
+            if (typeof files === 'string') {
+                generatedDocuments.push(...(await DocUtils.uploadYoutubeVideo(files, options)));
             } else {
-                generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options));
+                generatedDocuments.push(...(await DocUtils.uploadFilesToDocs(files, options)));
             }
             if (generatedDocuments.length) {
                 // Creating a dash document
                 const isFreeformView = this.props.Document._viewType === CollectionViewType.Freeform;
-                const set = !isFreeformView ? generatedDocuments :
-                    generatedDocuments.length > 1 ? generatedDocuments.map(d => { DocUtils.iconify(d); return d; }) : [];
+                const set = !isFreeformView
+                    ? generatedDocuments
+                    : generatedDocuments.length > 1
+                    ? generatedDocuments.map(d => {
+                          DocUtils.iconify(d);
+                          return d;
+                      })
+                    : [];
                 if (completed) completed(set);
                 else {
                     if (isFreeformView && generatedDocuments.length > 1) {
-                        addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!,);
+                        addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!);
                     } else {
                         generatedDocuments.forEach(addDocument);
                     }
                 }
             } else {
-                if (text && !text.includes("https://")) {
+                if (text && !text.includes('https://')) {
                     addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 }));
                 } else {
-                    alert("Document upload failed - possibly an unsupported file type.");
+                    alert('Document upload failed - possibly an unsupported file type.');
                 }
             }
             disposer();
-        }
+        };
     }
 
     return CollectionSubView;
 }
 
-import { DragManager, dropActionType } from "../../util/DragManager";
-import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox";
-import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView";
-import { SelectionManager } from "../../util/SelectionManager";
-import { OverlayView } from "../OverlayView";
-import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
\ No newline at end of file
+import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
+import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DragManager, dropActionType } from '../../util/DragManager';
+import { SelectionManager } from '../../util/SelectionManager';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { OverlayView } from '../OverlayView';
+import { CollectionView, CollectionViewProps } from './CollectionView';
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f5b9162d3..809a73a77 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -9,7 +9,6 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -20,6 +19,7 @@ import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { DocumentView } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionFreeFormView } from './collectionFreeForm';
@@ -27,7 +27,6 @@ import { CollectionSubView } from './CollectionSubView';
 import './CollectionTreeView.scss';
 import { TreeView } from './TreeView';
 import React = require('react');
-import { FieldViewProps } from '../nodes/FieldView';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionTreeViewProps = {
@@ -81,7 +80,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return this.doc.treeViewType === TreeViewType.fileSystem;
     }
     @computed get dashboardMode() {
-        return this.doc === CurrentUserUtils.MyDashboards;
+        return this.doc === Doc.MyDashboards;
     }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
@@ -92,9 +91,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _isAnyChildContentActive = false;
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
     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;
+        Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction) ? true : false;
 
     componentWillUnmount() {
         this._isDisposing = true;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 1576ec40f..f38efe578 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -9,12 +9,13 @@ import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { returnEmptyString } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
+import { CollectionViewType } from '../../documents/DocumentTypes';
 import { BranchCreate, BranchTask } from '../../documents/Gitlike';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
+import { DashboardView } from '../DashboardView';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { CollectionCarousel3DView } from './CollectionCarousel3DView';
@@ -35,26 +36,6 @@ import './CollectionView.scss';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
-export enum CollectionViewType {
-    Invalid = 'invalid',
-    Freeform = 'freeform',
-    Schema = 'schema',
-    Docking = 'docking',
-    Tree = 'tree',
-    Stacking = 'stacking',
-    Masonry = 'masonry',
-    Multicolumn = 'multicolumn',
-    Multirow = 'multirow',
-    Time = 'time',
-    Carousel = 'carousel',
-    Carousel3D = '3D Carousel',
-    Linear = 'linear',
-    //Staff = "staff",
-    Map = 'map',
-    Grid = 'grid',
-    Pile = 'pileup',
-    StackedTimeline = 'stacked timeline',
-}
 interface CollectionViewProps_ extends FieldViewProps {
     isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
@@ -127,39 +108,26 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     };
 
     screenToLocalTransform = () => (this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()));
+    // prettier-ignore
     private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
         TraceMobx();
         if (type === undefined) return null;
         switch (type) {
             default:
-            case CollectionViewType.Freeform:
-                return <CollectionFreeFormView key="collview" {...props} />;
-            case CollectionViewType.Schema:
-                return <CollectionSchemaView key="collview" {...props} />;
-            case CollectionViewType.Docking:
-                return <CollectionDockingView key="collview" {...props} />;
-            case CollectionViewType.Tree:
-                return <CollectionTreeView key="collview" {...props} />;
-            case CollectionViewType.Multicolumn:
-                return <CollectionMulticolumnView key="collview" {...props} />;
-            case CollectionViewType.Multirow:
-                return <CollectionMultirowView key="collview" {...props} />;
-            case CollectionViewType.Linear:
-                return <CollectionLinearView key="collview" {...props} />;
-            case CollectionViewType.Pile:
-                return <CollectionPileView key="collview" {...props} />;
-            case CollectionViewType.Carousel:
-                return <CollectionCarouselView key="collview" {...props} />;
-            case CollectionViewType.Carousel3D:
-                return <CollectionCarousel3DView key="collview" {...props} />;
-            case CollectionViewType.Stacking:
-                return <CollectionStackingView key="collview" {...props} />;
-            case CollectionViewType.Masonry:
-                return <CollectionStackingView key="collview" {...props} />;
-            case CollectionViewType.Time:
-                return <CollectionTimeView key="collview" {...props} />;
-            case CollectionViewType.Grid:
-                return <CollectionGridView key="collview" {...props} />;
+            case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
+            case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
+            case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
+            case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
+            case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
+            case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
+            case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
+            case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
+            case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
+            case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
+            case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
+            case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
             //case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />;
         }
     };
@@ -193,7 +161,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     onContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
         if (e.nativeEvent.cancelBubble) return; // nested calls to React to render can cause the same event to trigger in the outer view even if the inner view has handled it.  This avoid CollectionDockingView menu options from being added when the event has been handled by a sub-document.
-        if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== CurrentUserUtils.MainDocId) {
+        if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== Doc.MainDocId) {
             // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             this.setupViewTypes(
                 'UI Controls...',
@@ -235,7 +203,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 });
             }
             if (this.Document._viewType === CollectionViewType.Docking) {
-                optionItems.push({ description: 'Create Dashboard', event: () => CurrentUserUtils.createNewDashboard(), icon: 'project-diagram' });
+                optionItems.push({ description: 'Create Dashboard', event: () => DashboardView.createNewDashboard(), icon: 'project-diagram' });
             }
 
             !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
@@ -286,9 +254,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         return StrCast(this.rootDoc.childLayoutString);
     }
 
-    isContentActive = (outsideReaction?: boolean) => {
-        return this.props.isContentActive();
-    };
+    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive();
+
     render() {
         TraceMobx();
         const props: SubCollectionViewProps = {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index c0a61c90f..b8aaea622 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -9,18 +9,20 @@ import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from '../../../fields/RefField';
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { DocUtils } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { DashboardView } from '../DashboardView';
 import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { MainView } from '../MainView';
@@ -30,11 +32,9 @@ import { PinProps, PresBox, PresMovement } from '../nodes/trails';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { CollectionView, CollectionViewType } from './CollectionView';
+import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
 import React = require('react');
-import { listSpec } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface TabDocViewProps {
@@ -215,7 +215,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         const batch = UndoManager.StartBatch('pinning doc');
 
         // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
-        const curPres = CurrentUserUtils.ActivePresentation;
+        const curPres = Doc.ActivePresentation;
         curPres &&
             docList.forEach(doc => {
                 // Edge Case 1: Cannot pin document to itself
@@ -306,7 +306,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 .map(d => d.DashDoc)
                 .includes(curPres)
         ) {
-            const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []);
+            const docs = Cast(Doc.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
@@ -361,7 +361,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         const locationParams = locationFields.length > 1 ? locationFields[1] : '';
         switch (locationFields[0]) {
             case 'dashboard':
-                return CurrentUserUtils.openDashboard(doc);
+                return DashboardView.openDashboard(doc);
             case 'close':
                 return CollectionDockingView.CloseSplit(doc, locationParams);
             case 'fullScreen':
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 704b8989a..eb5faf4e1 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,7 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx';
+import { observer } from 'mobx-react';
 import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
@@ -12,26 +12,25 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, simulateMouseClick, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { DocumentManager, DocFocusOrOpen } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { EditableView } from "../EditableView";
+import { EditableView } from '../EditableView';
 import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
 import { DocumentView, DocumentViewInternal, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { KeyValueBox } from '../nodes/KeyValueBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
-import { CollectionView, CollectionViewType } from './CollectionView';
-import "./TreeView.scss";
-import React = require("react");
-import { KeyValueBox } from '../nodes/KeyValueBox';
-import { FieldViewProps } from '../nodes/FieldView';
+import { CollectionView } from './CollectionView';
+import './TreeView.scss';
+import React = require('react');
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -55,7 +54,7 @@ export interface TreeViewProps {
     indentDocument?: (editTitle: boolean) => void;
     outdentDocument?: (editTitle: boolean) => void;
     ScreenToLocalTransform: () => Transform;
-    contextMenuItems: { script: ScriptField, filter: ScriptField, icon: string, label: string }[];
+    contextMenuItems: { script: ScriptField; filter: ScriptField; icon: string; label: string }[];
     dontRegisterView?: boolean;
     styleProvider?: StyleProviderFunc | undefined;
     treeViewHideHeaderFields: () => boolean;
@@ -70,24 +69,26 @@ export interface TreeViewProps {
     hierarchyIndex?: number[];
 }
 
-const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("px", "")); };
+const treeBulletWidth = function () {
+    return Number(TREE_BULLET_WIDTH.replace('px', ''));
+};
 
 export enum TreeSort {
-    Up = "up",
-    Down = "down",
-    Zindex = "z",
-    None = "none"
+    Up = 'up',
+    Down = 'down',
+    Zindex = 'z',
+    None = 'none',
 }
 /**
  * Renders a treeView of a collection of documents
- * 
+ *
  * special fields:
  * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
  * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
  */
 @observer
 export class TreeView extends React.Component<TreeViewProps> {
-    static _editTitleOnLoad: Opt<{ id: string, parent: TreeView | CollectionTreeView | undefined }>;
+    static _editTitleOnLoad: Opt<{ id: string; parent: TreeView | CollectionTreeView | undefined }>;
     static _openTitleScript: Opt<ScriptField | undefined>;
     static _openLevelScript: Opt<ScriptField | undefined>;
     private _header: React.RefObject<HTMLDivElement> = React.createRef();
@@ -98,7 +99,9 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _openScript: (() => ScriptField) | undefined;
     private _treedropDisposer?: DragManager.DragDropDisposer;
 
-    get treeViewOpenIsTransient() { return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc); }
+    get treeViewOpenIsTransient() {
+        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc);
+    }
     set treeViewOpen(c: boolean) {
         if (this.treeViewOpenIsTransient) this._transientOpenState = c;
         else {
@@ -109,26 +112,61 @@ export class TreeView extends React.Component<TreeViewProps> {
     @observable _transientOpenState = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
     @observable _editTitle: boolean = false;
     @observable _dref: DocumentView | undefined | null;
-    get displayName() { return "TreeView(" + this.props.document.title + ")"; }  // this makes mobx trace() statements more descriptive
+    get displayName() {
+        return 'TreeView(' + this.props.document.title + ')';
+    } // this makes mobx trace() statements more descriptive
     get defaultExpandedView() {
-        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.noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
-    }
-
-    @computed get doc() { return this.props.document; }
-    @computed get treeViewOpen() { return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._transientOpenState; }
-    @computed get treeViewExpandedView() { return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView; }
-    @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); }
-    @computed get dataDoc() { return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym]; }
-    @computed get layoutDoc() { return Doc.Layout(this.doc); }
-    @computed get fieldKey() { return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc)); }
-    @computed get childDocs() { return this.childDocList(this.fieldKey); }
-    @computed get childLinks() { return this.childDocList("links"); }
-    @computed get childAliases() { return this.childDocList("aliases"); }
-    @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
-    @computed get selected() { return SelectionManager.IsSelected(this._docRef); }
+        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.noviceMode
+            ? 'layout'
+            : StrCast(this.props.treeView.doc.treeViewExpandedView, 'fields');
+    }
+
+    @computed get doc() {
+        return this.props.document;
+    }
+    @computed get treeViewOpen() {
+        return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, 'treeViewOpen', 'boolean', true)) || this._transientOpenState;
+    }
+    @computed get treeViewExpandedView() {
+        return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView;
+    }
+    @computed get MAX_EMBED_HEIGHT() {
+        return NumCast(this.props.containerCollection.maxEmbedHeight, 200);
+    }
+    @computed get dataDoc() {
+        return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym];
+    }
+    @computed get layoutDoc() {
+        return Doc.Layout(this.doc);
+    }
+    @computed get fieldKey() {
+        return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc));
+    }
+    @computed get childDocs() {
+        return this.childDocList(this.fieldKey);
+    }
+    @computed get childLinks() {
+        return this.childDocList('links');
+    }
+    @computed get childAliases() {
+        return this.childDocList('aliases');
+    }
+    @computed get childAnnos() {
+        return this.childDocList(this.fieldKey + '-annotations');
+    }
+    @computed get selected() {
+        return SelectionManager.IsSelected(this._docRef);
+    }
     //      SelectionManager.Views().lastElement()?.props.Document === this.props.document; }
 
     @observable _presTimer!: NodeJS.Timeout;
@@ -136,64 +174,76 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     @observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>();
     // the selected item's index
-    @computed get itemIndex() { return NumCast(this.doc._itemIndex); }
-    // the item that's active 
-    @computed get activeItem() { return this.childDocs ? Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null) : undefined; }
-    @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
+    @computed get itemIndex() {
+        return NumCast(this.doc._itemIndex);
+    }
+    // the item that's active
+    @computed get activeItem() {
+        return this.childDocs ? Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null) : undefined;
+    }
+    @computed get targetDoc() {
+        return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
+    }
 
     childDocList(field: string) {
         const layout = Cast(Doc.LayoutField(this.doc), Doc, null);
-        return (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
+        return (
+            (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
             (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
-            DocListCastOrNull(this.doc[field]); // otherwise use the document's data field
+            DocListCastOrNull(this.doc[field])
+        ); // otherwise use the document's data field
     }
     @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
-        if (this.doc !== target && addDoc !== returnFalse) { // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
+        if (this.doc !== target && addDoc !== returnFalse) {
+            // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
             if (this.props.removeDoc?.(doc) === true) {
                 return addDoc(doc);
             }
         }
         return false;
-    }
+    };
     @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
         this.props.treeView.props.select(false);
         const ind = this.dataDoc[key].indexOf(doc);
         const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
         res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.CollectionView)?.select(false);
         return res;
-    }
+    };
 
     @action setEditTitle = (docView?: DocumentView) => {
         this._selDisposer?.();
         if (!docView) {
             this._editTitle = false;
-        }
-        else if (docView.isSelected()) {
+        } else if (docView.isSelected()) {
             const doc = docView.Document;
             SelectionManager.SelectSchemaViewDoc(doc);
             this._editTitle = true;
-            this._selDisposer = reaction(() => SelectionManager.SelectedSchemaDoc(), seldoc => seldoc !== doc && this.setEditTitle(undefined));
+            this._selDisposer = reaction(
+                () => SelectionManager.SelectedSchemaDoc(),
+                seldoc => seldoc !== doc && this.setEditTitle(undefined)
+            );
         } else {
             docView.select(false);
         }
-    }
+    };
     @action
     openLevel = (docView: DocumentView) => {
         if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
             this.treeViewOpen = !this.treeViewOpen;
         } else {
             // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
-            const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+            const bestAlias =
+                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
             const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail);
-            this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), "lightbox");
+            this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), 'lightbox');
         }
-    }
+    };
 
     constructor(props: any) {
         super(props);
         if (!TreeView._openLevelScript) {
-            TreeView._openTitleScript = ScriptField.MakeScript("scriptContext.setEditTitle(documentView)", { scriptContext: "any", documentView: "any" });
-            TreeView._openLevelScript = ScriptField.MakeScript(`scriptContext.openLevel(documentView)`, { scriptContext: "any", documentView: "any" });
+            TreeView._openTitleScript = ScriptField.MakeScript('scriptContext.setEditTitle(documentView)', { scriptContext: 'any', documentView: 'any' });
+            TreeView._openLevelScript = ScriptField.MakeScript(`scriptContext.openLevel(documentView)`, { scriptContext: 'any', documentView: 'any' });
         }
         this._openScript = Doc.IsSystem(this.props.document) ? undefined : () => TreeView._openLevelScript!;
         this._editTitleScript = Doc.IsSystem(this.props.document) ? () => TreeView._openLevelScript! : () => TreeView._openTitleScript!;
@@ -202,16 +252,16 @@ export class TreeView extends React.Component<TreeViewProps> {
     _treeEle: any;
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this._treedropDisposer?.();
-        ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
+        ele && ((this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this))), this.doc);
         if (this._treeEle) this.props.unobserveHeight(this._treeEle);
-        this.props.observeHeight(this._treeEle = ele);
-    }
+        this.props.observeHeight((this._treeEle = ele));
+    };
 
     componentWillUnmount() {
         this._selDisposer?.();
         this._treeEle && this.props.unobserveHeight(this._treeEle);
-        document.removeEventListener("pointermove", this.onDragMove, true);
-        document.removeEventListener("pointermove", this.onDragUp, true);
+        document.removeEventListener('pointermove', this.onDragMove, true);
+        document.removeEventListener('pointermove', this.onDragUp, true);
         // TODO: [AL] add these
         this.props.hierarchyIndex !== undefined && this.props.RemFromMap?.(this.doc, this.props.hierarchyIndex);
     }
@@ -225,49 +275,60 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     onDragUp = (e: PointerEvent) => {
-        document.removeEventListener("pointerup", this.onDragUp, true);
-        document.removeEventListener("pointermove", this.onDragMove, true);
-    }
+        document.removeEventListener('pointerup', this.onDragUp, true);
+        document.removeEventListener('pointermove', this.onDragMove, true);
+    };
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc);
         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
-            this._header.current!.className = "treeView-header";
-            document.removeEventListener("pointermove", this.onDragMove, true);
-            document.removeEventListener("pointerup", this.onDragUp, true);
-            document.addEventListener("pointermove", this.onDragMove, true);
-            document.addEventListener("pointerup", this.onDragUp, true);
+            this._header.current!.className = 'treeView-header';
+            document.removeEventListener('pointermove', this.onDragMove, true);
+            document.removeEventListener('pointerup', this.onDragUp, true);
+            document.addEventListener('pointermove', this.onDragMove, true);
+            document.addEventListener('pointerup', this.onDragUp, true);
         }
-    }
+    };
     onPointerLeave = (e: React.PointerEvent): void => {
         Doc.UnBrushDoc(this.dataDoc);
-        if (this._header.current?.className !== "treeView-header-editing") {
-            this._header.current!.className = "treeView-header";
+        if (this._header.current?.className !== 'treeView-header-editing') {
+            this._header.current!.className = 'treeView-header';
         }
-        document.removeEventListener("pointerup", this.onDragUp, true);
-        document.removeEventListener("pointermove", this.onDragMove, true);
-    }
+        document.removeEventListener('pointerup', this.onDragUp, true);
+        document.removeEventListener('pointermove', this.onDragMove, true);
+    };
     onDragMove = (e: PointerEvent): void => {
         Doc.UnBrushDoc(this.dataDoc);
         const pt = [e.clientX, e.clientY];
         const rect = this._header.current!.getBoundingClientRect();
         const before = pt[1] < rect.top + rect.height / 2;
-        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
-        this._header.current!.className = "treeView-header";
+        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
+        this._header.current!.className = 'treeView-header';
         if (!this.props.treeView.outlineMode || DragManager.DocDragData?.treeViewDoc === this.props.treeView.rootDoc) {
-            if (inside) this._header.current!.className += " treeView-header-inside";
-            else if (before) this._header.current!.className += " treeView-header-above";
-            else if (!before) this._header.current!.className += " treeView-header-below";
+            if (inside) this._header.current!.className += ' treeView-header-inside';
+            else if (before) this._header.current!.className += ' treeView-header-above';
+            else if (!before) this._header.current!.className += ' treeView-header-below';
         }
         e.stopPropagation();
-    }
+    };
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", {
-            layout: CollectionView.LayoutString("data"),
-            title: "-title-",
-            treeViewExpandedViewLock: true, treeViewExpandedView: "data",
-            _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: TreeViewType.outline,
-            x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _width: 1000, _height: 10
+        const bullet = Docs.Create.TextDocument('-text-', {
+            layout: CollectionView.LayoutString('data'),
+            title: '-title-',
+            treeViewExpandedViewLock: true,
+            treeViewExpandedView: 'data',
+            _viewType: CollectionViewType.Tree,
+            hideLinkButton: true,
+            _showSidebar: true,
+            treeViewType: TreeViewType.outline,
+            x: 0,
+            y: 0,
+            _xMargin: 0,
+            _yMargin: 0,
+            _autoHeight: true,
+            _singleLine: true,
+            _width: 1000,
+            _height: 10,
         });
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
@@ -279,19 +340,19 @@ export class TreeView extends React.Component<TreeViewProps> {
         const bullet = TreeView.makeTextBullet();
         TreeView._editTitleOnLoad = { id: bullet[Id], parent: this };
         return this.props.addDocument(bullet);
-    }
+    };
 
     makeFolder = () => {
-        const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
+        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
         TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
         return this.props.addDocument(folder);
-    }
+    };
     deleteItem = () => this.props.removeDoc?.(this.doc);
 
     preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
         const dragData = de.complete.docDragData;
-        dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
-    }
+        dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? 'same' : dragData.dropAction);
+    };
 
     @undoBatch
     treeDrop = (e: Event, de: DragManager.DropEvent) => {
@@ -299,11 +360,11 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (!this._header.current) return;
         const rect = this._header.current.getBoundingClientRect();
         const before = pt[1] < rect.top + rect.height / 2;
-        const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
+        const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
         if (de.complete.linkDragData) {
             const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
             const destDoc = this.doc;
-            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", "");
+            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, 'tree link', '');
             e.stopPropagation();
         }
         const docDragData = de.complete.docDragData;
@@ -313,22 +374,16 @@ export class TreeView extends React.Component<TreeViewProps> {
                 e.stopPropagation();
             }
         }
-    }
+    };
 
     dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) {
         const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
-        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes("add")) || forceAdd;
-        const localAdd = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false;
-        const addDoc = !inside ? parentAddDoc :
-            (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
-        const move = (!dropAction || dropAction === "proto" || dropAction === "move" || dropAction === "same") && moveDocument;
+        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes('add')) || forceAdd;
+        const localAdd = (doc: Doc) => (Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false);
+        const addDoc = !inside ? parentAddDoc : (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
+        const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
         if (canAdd) {
-            return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) =>
-                (move ?
-                    move(d, undefined, addDoc) || (dropAction === "proto" ? addDoc(d) : false)
-                    :
-                    addDoc(d)) || added,
-                false));
+            return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false));
         }
         return false;
     }
@@ -339,7 +394,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const outerXf = Utils.GetScreenTransform(this.props.treeView.MainEle());
         const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
         return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
-    }
+    };
     docTransform = () => this.refTransform(this._dref?.ContentRef?.current);
     getTransform = () => this.refTransform(this._tref.current);
     docWidth = () => {
@@ -348,22 +403,25 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]());
         if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth()));
         return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
-    }
+    };
     docHeight = () => {
         const layoutDoc = this.layoutDoc;
-        return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
-            const aspect = Doc.NativeAspect(layoutDoc);
-            if (aspect) return this.docWidth() / (aspect || 1);
-            return layoutDoc._fitWidth ?
-                (!Doc.NativeHeight(this.doc) ?
-                    NumCast(this.props.containerCollection._height)
-                    :
-                    Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height))
-                    ))
-                :
-                (layoutDoc[HeightSym]() || 50);
-        })()));
-    }
+        return Math.max(
+            70,
+            Math.min(
+                this.MAX_EMBED_HEIGHT,
+                (() => {
+                    const aspect = Doc.NativeAspect(layoutDoc);
+                    if (aspect) return this.docWidth() / (aspect || 1);
+                    return layoutDoc._fitWidth
+                        ? !Doc.NativeHeight(this.doc)
+                            ? NumCast(this.props.containerCollection._height)
+                            : Math.min((this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height)))
+                        : layoutDoc[HeightSym]() || 50;
+                })()
+            )
+        );
+    };
 
     @computed get expandedField() {
         const ids: { [key: string]: string } = {};
@@ -372,11 +430,11 @@ export class TreeView extends React.Component<TreeViewProps> {
         doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
 
         for (const key of Object.keys(ids).slice().sort()) {
-            if (this.props.skipFields?.includes(key) || key === "title" || key === "treeViewOpen") continue;
+            if (this.props.skipFields?.includes(key) || key === 'title' || key === 'treeViewOpen') continue;
             const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
 
-            if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
+            if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc)) {
                 const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
                 const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
                     const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
@@ -384,74 +442,109 @@ export class TreeView extends React.Component<TreeViewProps> {
                     return added;
                 };
                 const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
-                contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
-                    this.props.treeView, this, doc, undefined, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move,
-                    this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive,
-                    this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
-                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged,
-                    this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems(),
+                contentElement = TreeView.GetChildElements(
+                    contents instanceof Doc ? [contents] : DocListCast(contents),
+                    this.props.treeView,
+                    this,
+                    doc,
+                    undefined,
+                    this.props.containerCollection,
+                    this.props.prevSibling,
+                    addDoc,
+                    remDoc,
+                    this.move,
+                    this.props.dropAction,
+                    this.props.addDocTab,
+                    this.titleStyleProvider,
+                    this.props.ScreenToLocalTransform,
+                    this.props.isContentActive,
+                    this.props.panelWidth,
+                    this.props.renderDepth,
+                    this.props.treeViewHideHeaderFields,
+                    [...this.props.renderedIds, doc[Id]],
+                    this.props.onCheckedClick,
+                    this.props.onChildClick,
+                    this.props.skipFields,
+                    false,
+                    this.props.whenChildContentsActiveChanged,
+                    this.props.dontRegisterView,
+                    emptyFunction,
+                    emptyFunction,
+                    this.childContextMenuItems(),
                     // TODO: [AL] Add these
                     this.props.AddToMap,
                     this.props.RemFromMap,
                     this.props.hierarchyIndex
                 );
             } else {
-                contentElement = <EditableView key="editableView"
-                    contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
-                    height={13}
-                    fontSize={12}
-                    GetValue={() => Field.toKeyValueString(doc, key)}
-                    SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
+                contentElement = (
+                    <EditableView
+                        key="editableView"
+                        contents={contents !== undefined ? Field.toString(contents as Field) : 'null'}
+                        height={13}
+                        fontSize={12}
+                        GetValue={() => Field.toKeyValueString(doc, key)}
+                        SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
+                    />
+                );
             }
-            rows.push(<div style={{ display: "flex" }} key={key}>
-                <span style={{ fontWeight: "bold" }}>{key + ":"}</span>
-                &nbsp;
-                {contentElement}
-            </div>);
+            rows.push(
+                <div style={{ display: 'flex' }} key={key}>
+                    <span style={{ fontWeight: 'bold' }}>{key + ':'}</span>
+                    &nbsp;
+                    {contentElement}
+                </div>
+            );
         }
-        rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
-            <EditableView
-                key="editableView"
-                contents={"+key:value"}
-                height={13}
-                fontSize={12}
-                GetValue={returnEmptyString}
-                SetValue={value => value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true)} />
-        </div>);
+        rows.push(
+            <div style={{ display: 'flex' }} key={'newKeyValue'}>
+                <EditableView
+                    key="editableView"
+                    contents={'+key:value'}
+                    height={13}
+                    fontSize={12}
+                    GetValue={returnEmptyString}
+                    SetValue={value => value.indexOf(':') !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(':')), value.substring(value.indexOf(':') + 1, value.length), true)}
+                />
+            </div>
+        );
         return rows;
     }
 
     rtfWidth = () => {
-        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
-        return Math.min(layout[WidthSym](), (this.props.panelWidth() - treeBulletWidth())) / (this.props.treeView.props.scaling?.() || 1);
-    }
+        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+        return Math.min(layout[WidthSym](), this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1);
+    };
     rtfHeight = () => {
-        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
         return this.rtfWidth() <= layout[WidthSym]() ? Math.min(layout[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
-    }
+    };
     rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth());
     expandPanelHeight = () => {
         if (this.layoutDoc._fitWidth) return this.docHeight();
         const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
         const docAspect = this.docWidth() / this.docHeight();
-        return (docAspect < aspect) ? this.docWidth() / aspect : this.docHeight();
-    }
+        return docAspect < aspect ? this.docWidth() / aspect : this.docHeight();
+    };
     expandPanelWidth = () => {
         if (this.layoutDoc._fitWidth) return this.docWidth();
         const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
         const docAspect = this.docWidth() / this.docHeight();
-        return (docAspect > aspect) ? this.docHeight() * aspect : this.docWidth();
-    }
+        return docAspect > aspect ? this.docHeight() * aspect : this.docWidth();
+    };
 
     @computed get renderContent() {
         TraceMobx();
         const expandKey = this.treeViewExpandedView;
-        const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string, label: string } };
-        if (["links", "annotations", "aliases", this.fieldKey].includes(expandKey)) {
+        const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } };
+        if (['links', 'annotations', 'aliases', this.fieldKey].includes(expandKey)) {
             const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
             const sortKeys = Object.keys(sortings);
-            const curSortIndex = Math.max(0, sortKeys.findIndex(val => val === sorting));
-            const key = (expandKey === "annotations" ? `${this.fieldKey}-` : "") + expandKey;
+            const curSortIndex = Math.max(
+                0,
+                sortKeys.findIndex(val => val === sorting)
+            );
+            const key = (expandKey === 'annotations' ? `${this.fieldKey}-` : '') + expandKey;
             const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
             const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
                 // if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't),
@@ -461,107 +554,161 @@ export class TreeView extends React.Component<TreeViewProps> {
                     const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering);
                     doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 0.5) : 1000;
                     docs.push(doc);
-                    docs.sort((a, b) => NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1).forEach((d, i) => d.zIndex = i);
+                    docs.sort((a, b) => (NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1)).forEach((d, i) => (d.zIndex = i));
                 }
                 const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
                 added && (doc.context = this.doc.context);
                 return added;
             };
             const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
-            const docs = expandKey === "aliases" ? this.childAliases : expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
-            let downX = 0, downY = 0;
-            return <>
-                {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? (null) : <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }} >
-                    {sortings[sorting]?.label}
-                </div>}
-                <ul key={expandKey + "more"} title="click to change sort order" className={this.doc.treeViewHideTitle ? "no-indent" : ""}
-                    onPointerDown={e => { downX = e.clientX; downY = e.clientY; e.stopPropagation(); }}
-                    onClick={(e) => {
-                        if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
-                            !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+            const docs = expandKey === 'aliases' ? this.childAliases : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs;
+            let downX = 0,
+                downY = 0;
+            return (
+                <>
+                    {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : (
+                        <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }}>
+                            {sortings[sorting]?.label}
+                        </div>
+                    )}
+                    <ul
+                        key={expandKey + 'more'}
+                        title="click to change sort order"
+                        className={this.doc.treeViewHideTitle ? 'no-indent' : ''}
+                        onPointerDown={e => {
+                            downX = e.clientX;
+                            downY = e.clientY;
                             e.stopPropagation();
-                        }
-                    }}>
-                    {!docs ? (null) :
-                        TreeView.GetChildElements(docs, this.props.treeView, this, this.layoutDoc,
-                            this.dataDoc, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move,
-                            StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform,
-                            this.props.isContentActive, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
-                            [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged,
-                            this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems(),
-                            // TODO: [AL] add these
-                            this.props.AddToMap,
-                            this.props.RemFromMap,
-                            this.props.hierarchyIndex)}
-                </ul >
-            </>;
-        } else if (this.treeViewExpandedView === "fields") {
-            return <ul key={this.doc[Id] + this.doc.title}>
-                <div style={{ display: "inline-block" }} >
-                    {this.expandedField}
-                </div>
-            </ul>;
+                        }}
+                        onClick={e => {
+                            if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
+                                !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+                                e.stopPropagation();
+                            }
+                        }}>
+                        {!docs
+                            ? null
+                            : TreeView.GetChildElements(
+                                  docs,
+                                  this.props.treeView,
+                                  this,
+                                  this.layoutDoc,
+                                  this.dataDoc,
+                                  this.props.containerCollection,
+                                  this.props.prevSibling,
+                                  addDoc,
+                                  remDoc,
+                                  this.move,
+                                  StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType,
+                                  this.props.addDocTab,
+                                  this.titleStyleProvider,
+                                  this.props.ScreenToLocalTransform,
+                                  this.props.isContentActive,
+                                  this.props.panelWidth,
+                                  this.props.renderDepth,
+                                  this.props.treeViewHideHeaderFields,
+                                  [...this.props.renderedIds, this.doc[Id]],
+                                  this.props.onCheckedClick,
+                                  this.props.onChildClick,
+                                  this.props.skipFields,
+                                  false,
+                                  this.props.whenChildContentsActiveChanged,
+                                  this.props.dontRegisterView,
+                                  emptyFunction,
+                                  emptyFunction,
+                                  this.childContextMenuItems(),
+                                  // TODO: [AL] add these
+                                  this.props.AddToMap,
+                                  this.props.RemFromMap,
+                                  this.props.hierarchyIndex
+                              )}
+                    </ul>
+                </>
+            );
+        } else if (this.treeViewExpandedView === 'fields') {
+            return (
+                <ul key={this.doc[Id] + this.doc.title}>
+                    <div style={{ display: 'inline-block' }}>{this.expandedField}</div>
+                </ul>
+            );
         }
-        return <ul onPointerDown={e => { e.preventDefault(); e.stopPropagation(); }}>{this.renderEmbeddedDocument(false, this.props.treeView.props.childDocumentsActive ?? returnFalse)}</ul>; // "layout"
+        return (
+            <ul
+                onPointerDown={e => {
+                    e.preventDefault();
+                    e.stopPropagation();
+                }}>
+                {this.renderEmbeddedDocument(false, this.props.treeView.props.childDocumentsActive ?? returnFalse)}
+            </ul>
+        ); // "layout"
     }
 
-    get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); }
+    get onCheckedClick() {
+        return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick);
+    }
 
     @action
     bulletClick = (e: React.MouseEvent) => {
         if (this.onCheckedClick) {
-            this.onCheckedClick?.script.run({
-                this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
-                heading: this.props.containerCollection.title,
-                checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? "remove" : "check",
-                containingTreeView: this.props.treeView.props.Document,
-            }, console.log);
+            this.onCheckedClick?.script.run(
+                {
+                    this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
+                    heading: this.props.containerCollection.title,
+                    checked: this.doc.treeViewChecked === 'check' ? 'x' : this.doc.treeViewChecked === 'x' ? 'remove' : 'check',
+                    containingTreeView: this.props.treeView.props.Document,
+                },
+                console.log
+            );
         } else {
             this.treeViewOpen = !this.treeViewOpen;
         }
         e.stopPropagation();
-    }
+    };
 
     @computed get renderBullet() {
         TraceMobx();
-        const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ":open" : "")) || "question";
-        const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
-        return <div className={`bullet${this.props.treeView.outlineMode ? "-outline" : ""}`} key={"bullet"}
-            title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
-            onClick={this.bulletClick}
-            style={this.props.treeView.outlineMode ?
-                {
-                    opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity)
-                } :
-                {
-                    pointerEvents: this.props.isContentActive() ? "all" : undefined,
-                    opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4,
-                    color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
-                }}>
-            {this.props.treeView.outlineMode ?
-                !(this.doc.text as RichTextField)?.Text ? (null) :
-                    <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"]} /> :
-                <div className="treeView-bulletIcons" >
-                    <div className={`treeView-${this.onCheckedClick ? "checkIcon" : "expandIcon"}`}>
-                        <FontAwesomeIcon size="sm" icon={
-                            checked === "check" ? "check" :
-                                checked === "x" ? "times" :
-                                    checked === "unchecked" ? "square" :
-                                        !this.treeViewOpen ? "caret-right" : "caret-down"} />
+        const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : '')) || 'question';
+        const checked = this.onCheckedClick ? this.doc.treeViewChecked ?? 'unchecked' : undefined;
+        return (
+            <div
+                className={`bullet${this.props.treeView.outlineMode ? '-outline' : ''}`}
+                key={'bullet'}
+                title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : 'view fields'}
+                onClick={this.bulletClick}
+                style={
+                    this.props.treeView.outlineMode
+                        ? {
+                              opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity),
+                          }
+                        : {
+                              pointerEvents: this.props.isContentActive() ? 'all' : undefined,
+                              opacity: checked === 'unchecked' || typeof iconType !== 'string' ? undefined : 0.4,
+                              color: StrCast(this.doc.color, checked === 'unchecked' ? 'white' : 'inherit'),
+                          }
+                }>
+                {this.props.treeView.outlineMode ? (
+                    !(this.doc.text as RichTextField)?.Text ? null : (
+                        <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />
+                    )
+                ) : (
+                    <div className="treeView-bulletIcons">
+                        <div className={`treeView-${this.onCheckedClick ? 'checkIcon' : 'expandIcon'}`}>
+                            <FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />
+                        </div>
+                        {this.onCheckedClick ? null : typeof iconType === 'string' ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
                     </div>
-                    {this.onCheckedClick ? (null) : typeof iconType === "string" ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
-                </div>
-            }
-        </div>;
+                )}
+            </div>
+        );
     }
 
     @computed get validExpandViewTypes() {
-        const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length && !this.props.treeView.dashboardMode ? "annotations" : "";
-        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.noviceMode ? "" : "fields";
-        const layout = (Doc.noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+        const annos = () => (DocListCast(this.doc[this.fieldKey + '-annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : '');
+        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.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
@@ -571,49 +718,68 @@ export class TreeView extends React.Component<TreeViewProps> {
             this.doc.treeViewExpandedView = next(this.validExpandViewTypes);
         }
         this.treeViewOpen = true;
-    }
+    };
 
     @computed get headerElements() {
-        return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? (null)
-            : <>
-                {this.doc.hideContextMenu ? (null) : <FontAwesomeIcon title="context menu" key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />}
-                {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? (null) :
+        return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : (
+            <>
+                {this.doc.hideContextMenu ? null : (
+                    <FontAwesomeIcon
+                        title="context menu"
+                        key="bars"
+                        icon="bars"
+                        size="sm"
+                        onClick={e => {
+                            this.showContextMenu(e);
+                            e.stopPropagation();
+                        }}
+                    />
+                )}
+                {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
                     <span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
                         {this.treeViewExpandedView}
-                    </span>}
-            </>;
+                    </span>
+                )}
+            </>
+        );
     }
 
     showContextMenu = (e: React.MouseEvent) => {
         DocumentViewInternal.SelectAfterContextMenu = false;
         simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
         DocumentViewInternal.SelectAfterContextMenu = true;
-    }
+    };
     contextMenuItems = () => {
-        const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, icon: "folder-plus", label: "New Folder" };
-        const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: "any" })!, icon: "folder-plus", label: "Delete" };
+        const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' };
+        const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' };
         const folderOp = this.childDocs?.length ? [makeFolder] : [];
-        const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: "copy", label: "Open Alias" };
-        const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: "eye", label: "Focus or Open" };
-        return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? folderOp :
-            Doc.IsSystem(this.doc) ? [] :
-                this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
-                    [openAlias, makeFolder] :
-                    this.doc.viewType === CollectionViewType.Docking ? [] :
-                        [deleteItem, openAlias, focusDoc])];
-    }
+        const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: 'copy', label: 'Open Alias' };
+        const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' };
+        return [
+            ...this.props.contextMenuItems.filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
+            ...(this.doc.isFolder
+                ? folderOp
+                : Doc.IsSystem(this.doc)
+                ? []
+                : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
+                ? [openAlias, makeFolder]
+                : this.doc.viewType === CollectionViewType.Docking
+                ? []
+                : [deleteItem, openAlias, focusDoc]),
+        ];
+    };
     childContextMenuItems = () => {
         const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
         const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
-    }
+    };
 
     onChildClick = () => {
         return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
-    }
+    };
 
-    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null));
+    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
 
     refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
     ignoreEvent = (e: any) => {
@@ -621,51 +787,59 @@ export class TreeView extends React.Component<TreeViewProps> {
             e.stopPropagation();
             e.preventDefault();
         }
-    }
-    titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+    };
+    titleStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
 
-        switch (property.split(":")[0]) {
-            case StyleProp.Opacity: return this.props.treeView.outlineMode ? undefined : 1;
-            case StyleProp.BackgroundColor: return this.selected ? "#7089bb" : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
-            case StyleProp.DocContents: return this.props.treeView.outlineMode ? (null) :
-                <div className="treeView-label" style={{    // just render a title for a tree view label (identified by treeViewDoc being set in 'props')
-                    maxWidth: props?.PanelWidth() || undefined,
-                    background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
-                }}>
-                    {StrCast(doc?.title)}
-                </div>;
-            default: return this.props?.treeView?.props.styleProvider?.(doc, props, property);
+        switch (property.split(':')[0]) {
+            case StyleProp.Opacity:
+                return this.props.treeView.outlineMode ? undefined : 1;
+            case StyleProp.BackgroundColor:
+                return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+            case StyleProp.DocContents:
+                return this.props.treeView.outlineMode ? null : (
+                    <div
+                        className="treeView-label"
+                        style={{
+                            // just render a title for a tree view label (identified by treeViewDoc being set in 'props')
+                            maxWidth: props?.PanelWidth() || undefined,
+                            background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
+                        }}>
+                        {StrCast(doc?.title)}
+                    </div>
+                );
+            default:
+                return this.props?.treeView?.props.styleProvider?.(doc, props, property);
         }
-    }
-    embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
-        if (property.startsWith(StyleProp.Decorations)) return (null);
+    };
+    embeddedStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
+        if (property.startsWith(StyleProp.Decorations)) return null;
         return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
-    }
+    };
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         if (this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
             switch (e.key) {
-                case "Tab":
+                case 'Tab':
                     e.stopPropagation?.();
                     e.preventDefault?.();
                     setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
-                    UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true), "tab");
+                    UndoManager.RunInBatch(() => (e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true)), 'tab');
                     return true;
-                case "Backspace":
+                case 'Backspace':
                     if (!(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc)) {
                         e.stopPropagation?.();
                         e.preventDefault?.();
                         return true;
                     }
                     break;
-                case "Enter":
+                case 'Enter':
                     e.stopPropagation?.();
                     e.preventDefault?.();
-                    return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
+                    return UndoManager.RunInBatch(this.makeTextCollection, 'bullet');
             }
         }
         return false;
-    }
+    };
     titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth()));
 
     return18 = () => 18;
@@ -675,28 +849,32 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed
     get renderTitle() {
         TraceMobx();
-        const view = this._editTitle ? <EditableView key="_editTitle"
-            oneLine={true}
-            display={"inline-block"}
-            editing={this._editTitle}
-            background={"#7089bb"}
-            contents={StrCast(this.doc.title)}
-            height={12}
-            sizeToContent={true}
-            fontSize={12}
-            GetValue={() => StrCast(this.doc.title)}
-            OnTab={undoBatch((shift?: boolean) => {
-                if (!shift) this.props.indentDocument?.(true);
-                else this.props.outdentDocument?.(true);
-            })}
-            OnEmpty={undoBatch(() => this.props.treeView.outlineMode && this.props.removeDoc?.(this.doc))}
-            OnFillDown={val => this.props.treeView.fileSysMode && this.makeFolder()}
-            SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
-                Doc.SetInPlace(this.doc, "title", value, false);
-                this.props.treeView.outlineMode && enterKey && this.makeTextCollection();
-            })}
-        />
-            : <DocumentView key="title"
+        const view = this._editTitle ? (
+            <EditableView
+                key="_editTitle"
+                oneLine={true}
+                display={'inline-block'}
+                editing={this._editTitle}
+                background={'#7089bb'}
+                contents={StrCast(this.doc.title)}
+                height={12}
+                sizeToContent={true}
+                fontSize={12}
+                GetValue={() => StrCast(this.doc.title)}
+                OnTab={undoBatch((shift?: boolean) => {
+                    if (!shift) this.props.indentDocument?.(true);
+                    else this.props.outdentDocument?.(true);
+                })}
+                OnEmpty={undoBatch(() => this.props.treeView.outlineMode && this.props.removeDoc?.(this.doc))}
+                OnFillDown={val => this.props.treeView.fileSysMode && this.makeFolder()}
+                SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
+                    Doc.SetInPlace(this.doc, 'title', value, false);
+                    this.props.treeView.outlineMode && enterKey && this.makeTextCollection();
+                })}
+            />
+        ) : (
+            <DocumentView
+                key="title"
                 ref={action((r: any) => {
                     this._docRef = r ? r : undefined;
                     if (this._docRef && TreeView._editTitleOnLoad?.id === this.props.document[Id] && TreeView._editTitleOnLoad.parent === this.props.parentTreeView) {
@@ -743,139 +921,161 @@ export class TreeView extends React.Component<TreeViewProps> {
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.treeView.props.Document}
                 ContentScaling={returnOne}
-            />;
-
-        const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : ""));
-        return <>
-            <div className={`docContainer${Doc.IsSystem(this.props.document) || this.props.document.isFolder ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open"
-                style={{
-                    fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
-                    textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
-                    outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
-                    pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined
-                }} >
-                {view}
-            </div >
-            <div className="treeView-rightButtons">
-                {buttons} {/* hide and lock buttons */}
-                {this.headerElements}
-            </div>
-        </>;
-    }
+            />
+        );
 
-    renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
-        return <>
-            <div className={`treeView-header` + (editing ? "-editing" : "")} key="titleheader"
-                style={{ width: StrCast(this.doc.treeViewHeaderWidth, "max-content") }}
-                ref={this._header}
-                onClick={this.ignoreEvent}
-                onPointerDown={this.ignoreEvent}
-                onPointerEnter={this.onPointerEnter}
-                onPointerLeave={this.onPointerLeave}>
-                {contents}
-            </div>
-            {this.renderBorder}
-        </>;
+        const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ':afterHeader' : ''));
+        return (
+            <>
+                <div
+                    className={`docContainer${Doc.IsSystem(this.props.document) || this.props.document.isFolder ? '-system' : ''}`}
+                    ref={this._tref}
+                    title="click to edit title. Double Click or Drag to Open"
+                    style={{
+                        fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined,
+                        textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined,
+                        outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined,
+                        pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+                    }}>
+                    {view}
+                </div>
+                <div className="treeView-rightButtons">
+                    {buttons} {/* hide and lock buttons */}
+                    {this.headerElements}
+                </div>
+            </>
+        );
     }
 
+    renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
+        return (
+            <>
+                <div
+                    className={`treeView-header` + (editing ? '-editing' : '')}
+                    key="titleheader"
+                    style={{ width: StrCast(this.doc.treeViewHeaderWidth, 'max-content') }}
+                    ref={this._header}
+                    onClick={this.ignoreEvent}
+                    onPointerDown={this.ignoreEvent}
+                    onPointerEnter={this.onPointerEnter}
+                    onPointerLeave={this.onPointerLeave}>
+                    {contents}
+                </div>
+                {this.renderBorder}
+            </>
+        );
+    };
 
     renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
         const isExpandable = this.doc._treeViewGrowsHorizontally;
         const panelWidth = asText || isExpandable ? this.rtfWidth : this.expandPanelWidth;
         const panelHeight = asText ? this.rtfOutlineHeight : isExpandable ? this.rtfHeight : this.expandPanelHeight;
-        return <DocumentView key={this.doc[Id]} ref={action((r: DocumentView | null) => this._dref = r)}
-            Document={this.doc}
-            DataDoc={undefined}
-            PanelWidth={panelWidth}
-            PanelHeight={panelHeight}
-            NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
-            NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
-            LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined}
-            LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
-            isContentActive={isActive}
-            isDocumentActive={isActive}
-            styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
-            hideTitle={asText}
-            fitContentsToBox={returnTrue}
-            hideDecorationTitle={this.props.treeView.outlineMode}
-            hideResizeHandles={this.props.treeView.outlineMode}
-            onClick={this.onChildClick}
-            focus={this.refocus}
-            ContentScaling={returnOne}
-            onKey={this.onKeyDown}
-            hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
-            dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
-            ScreenToLocalTransform={this.docTransform}
-            renderDepth={this.props.renderDepth + 1}
-            treeViewDoc={this.props.treeView?.props.Document}
-            rootSelected={returnTrue}
-            docViewPath={this.props.treeView.props.docViewPath}
-            docFilters={returnEmptyFilter}
-            docRangeFilters={returnEmptyFilter}
-            searchFilterDocs={returnEmptyDoclist}
-            ContainingCollectionDoc={this.props.containerCollection}
-            ContainingCollectionView={undefined}
-            addDocument={this.props.addDocument}
-            moveDocument={this.move}
-            removeDocument={this.props.removeDoc}
-            whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-            addDocTab={this.props.addDocTab}
-            pinToPres={this.props.treeView.props.pinToPres}
-            disableDocBrushing={this.props.treeView.props.disableDocBrushing}
-            bringToFront={returnFalse}
-        />;
-    }
+        return (
+            <DocumentView
+                key={this.doc[Id]}
+                ref={action((r: DocumentView | null) => (this._dref = r))}
+                Document={this.doc}
+                DataDoc={undefined}
+                PanelWidth={panelWidth}
+                PanelHeight={panelHeight}
+                NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
+                NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
+                LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
+                LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
+                isContentActive={isActive}
+                isDocumentActive={isActive}
+                styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
+                hideTitle={asText}
+                fitContentsToBox={returnTrue}
+                hideDecorationTitle={this.props.treeView.outlineMode}
+                hideResizeHandles={this.props.treeView.outlineMode}
+                onClick={this.onChildClick}
+                focus={this.refocus}
+                ContentScaling={returnOne}
+                onKey={this.onKeyDown}
+                hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
+                dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
+                ScreenToLocalTransform={this.docTransform}
+                renderDepth={this.props.renderDepth + 1}
+                treeViewDoc={this.props.treeView?.props.Document}
+                rootSelected={returnTrue}
+                docViewPath={this.props.treeView.props.docViewPath}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionDoc={this.props.containerCollection}
+                ContainingCollectionView={undefined}
+                addDocument={this.props.addDocument}
+                moveDocument={this.move}
+                removeDocument={this.props.removeDoc}
+                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.treeView.props.pinToPres}
+                disableDocBrushing={this.props.treeView.props.disableDocBrushing}
+                bringToFront={returnFalse}
+            />
+        );
+    };
 
     // renders the text version of a document as the header.  This is used in the file system mode and in other vanilla tree views.
     @computed get renderTitleAsHeader() {
-        return <>
-            {this.renderBullet}
-            {this.renderTitle}
-        </>;
+        return (
+            <>
+                {this.renderBullet}
+                {this.renderTitle}
+            </>
+        );
     }
 
     // renders the document in the header field instead of a text proxy.
     renderDocumentAsHeader = (asText: boolean) => {
-        return <>
-            {this.renderBullet}
-            {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
-        </>;
-    }
+        return (
+            <>
+                {this.renderBullet}
+                {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
+            </>
+        );
+    };
 
     @computed get renderBorder() {
         const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
-        const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string, label: string } };
-        return <div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ""}`} style={{ borderColor: sortings[sorting]?.color }}>
-            {!this.treeViewOpen ? (null) : this.renderContent}
-        </div>;
+        const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } };
+        return (
+            <div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ''}`} style={{ borderColor: sortings[sorting]?.color }}>
+                {!this.treeViewOpen ? null : this.renderContent}
+            </div>
+        );
     }
 
     onTreeDrop = (de: React.DragEvent) => {
         const pt = [de.clientX, de.clientY];
         const rect = this._header.current!.getBoundingClientRect();
         const before = pt[1] < rect.top + rect.height / 2;
-        const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
-
-        const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, "copy", undefined, false));
-    }
+        const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
 
+        const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, false));
+    };
 
     render() {
         TraceMobx();
         const hideTitle = this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
-        return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
-            <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`}
+        return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (
+            '<' + this.doc.title + '>' // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
+        ) : (
+            <div
+                className={`treeView-container${this.props.isContentActive() ? '-active' : ''}`}
                 ref={this.createTreeDropTarget}
                 onDrop={this.onTreeDrop}
-            //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
-            // onKeyDown={this.onKeyDown}
+                //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
+                // onKeyDown={this.onKeyDown}
             >
                 <li className="collection-child">
-                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader ? // should test for prop 'treeViewRenderDocWithBulletAsHeader"
-                        this.renderEmbeddedDocument(false, returnFalse) :
-                        this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
+                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader // should test for prop 'treeViewRenderDocWithBulletAsHeader"
+                        ? this.renderEmbeddedDocument(false, returnFalse)
+                        : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
                 </li>
-            </div>;
+            </div>
+        );
     }
 
     public static sortDocs(childDocs: Doc[], criterion: string | undefined) {
@@ -883,9 +1083,10 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (criterion !== TreeSort.None) {
             const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
                 const reN = /[0-9]*$/;
-                const aA = a.replace(reN, "") ? a.replace(reN, "") : +a; // get rid of trailing numbers
-                const bA = b.replace(reN, "") ? b.replace(reN, "") : +b;
-                if (aA === bA) {  // if header string matches, then compare numbers numerically
+                const aA = a.replace(reN, '') ? a.replace(reN, '') : +a; // get rid of trailing numbers
+                const bA = b.replace(reN, '') ? b.replace(reN, '') : +b;
+                if (aA === bA) {
+                    // if header string matches, then compare numbers numerically
                     const aN = parseInt(a.match(reN)![0], 10);
                     const bN = parseInt(b.match(reN)![0], 10);
                     return aN === bN ? 0 : aN > bN ? 1 : -1;
@@ -894,11 +1095,11 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
             };
             docs.sort(function (d1, d2): 0 | 1 | -1 {
-                const a = (criterion === TreeSort.Up ? d2 : d1);
-                const b = (criterion === TreeSort.Up ? d1 : d2);
-                const first = a[criterion === TreeSort.Zindex ? "zIndex" : "title"];
-                const second = b[criterion === TreeSort.Zindex ? "zIndex" : "title"];
-                if (typeof first === 'number' && typeof second === 'number') return (first - second) > 0 ? 1 : -1;
+                const a = criterion === TreeSort.Up ? d2 : d1;
+                const b = criterion === TreeSort.Up ? d1 : d2;
+                const first = a[criterion === TreeSort.Zindex ? 'zIndex' : 'title'];
+                const second = b[criterion === TreeSort.Zindex ? 'zIndex' : 'title'];
+                if (typeof first === 'number' && typeof second === 'number') return first - second > 0 ? 1 : -1;
                 if (typeof first === 'string' && typeof second === 'string') return sortAlphaNum(first, second);
                 return criterion ? 1 : -1;
             });
@@ -934,11 +1135,11 @@ export class TreeView extends React.Component<TreeViewProps> {
         dontRegisterView: boolean | undefined,
         observerHeight: (ref: any) => void,
         unobserveHeight: (ref: any) => void,
-        contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string, icon: string }[]),
+        contextMenuItems: { script: ScriptField; filter: ScriptField; label: string; icon: string }[],
         // TODO: [AL] add these
         AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[],
         RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[],
-        hierarchyIndex?: number[],
+        hierarchyIndex?: number[]
     ) {
         const viewSpecScript = Cast(containerCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -948,68 +1149,77 @@ export class TreeView extends React.Component<TreeViewProps> {
         const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion, TreeSort.None));
         const rowWidth = () => panelWidth() - treeBulletWidth();
         const treeViewRefs = new Map<Doc, TreeView | undefined>();
-        return docs.filter(child => child instanceof Doc).map((child, i) => {
-            const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child);
-            if (!pair.layout || pair.data instanceof Promise) {
-                return (null);
-            }
-
-            const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
-                if (parent instanceof TreeView && parent.props.treeView.fileSysMode && !newParent.isFolder) return;
-                const fieldKey = Doc.LayoutFieldKey(newParent);
-                if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
-                    remove(child);
-                    FormattedTextBox.SelectOnLoad = child[Id];
-                    TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
-                    Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
-                    newParent.treeViewOpen = true;
-                    child.context = treeView.Document;
+        return docs
+            .filter(child => child instanceof Doc)
+            .map((child, i) => {
+                const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child);
+                if (!pair.layout || pair.data instanceof Promise) {
+                    return null;
                 }
-            };
-            const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
-            const outdent = parentCollectionDoc?._viewType !== CollectionViewType.Tree ? undefined : ((editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined));
-            const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
-            const childLayout = Doc.Layout(pair.layout);
-            const rowHeight = () => {
-                const aspect = Doc.NativeAspect(childLayout);
-                return (aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]());
-            };
-            return <TreeView key={child[Id]} ref={r => treeViewRefs.set(child, r ? r : undefined)}
-                document={pair.layout}
-                dataDoc={pair.data}
-                containerCollection={containerCollection}
-                prevSibling={docs[i]}
-                // TODO: [AL] add these
-                hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
-                AddToMap={AddToMap}
-                RemFromMap={RemFromMap}
-                treeView={treeView}
-                indentDocument={indent}
-                outdentDocument={outdent}
-                onCheckedClick={onCheckedClick}
-                onChildClick={onChildClick}
-                renderDepth={renderDepth}
-                removeDoc={StrCast(containerCollection.freezeChildren).includes("remove") ? undefined : remove}
-                addDocument={addDocument}
-                styleProvider={styleProvider}
-                panelWidth={rowWidth}
-                panelHeight={rowHeight}
-                dontRegisterView={dontRegisterView}
-                moveDocument={move}
-                dropAction={dropAction}
-                addDocTab={addDocTab}
-                ScreenToLocalTransform={screenToLocalXf}
-                isContentActive={isContentActive}
-                treeViewHideHeaderFields={treeViewHideHeaderFields}
-                renderedIds={renderedIds}
-                skipFields={skipFields}
-                firstLevel={firstLevel}
-                whenChildContentsActiveChanged={whenChildContentsActiveChanged}
-                parentTreeView={parentTreeView}
-                observeHeight={observerHeight}
-                unobserveHeight={unobserveHeight}
-                contextMenuItems={contextMenuItems}
-            />;
-        });
+
+                const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
+                    if (parent instanceof TreeView && parent.props.treeView.fileSysMode && !newParent.isFolder) return;
+                    const fieldKey = Doc.LayoutFieldKey(newParent);
+                    if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
+                        remove(child);
+                        FormattedTextBox.SelectOnLoad = child[Id];
+                        TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
+                        Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
+                        newParent.treeViewOpen = true;
+                        child.context = treeView.Document;
+                    }
+                };
+                const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
+                const outdent =
+                    parentCollectionDoc?._viewType !== CollectionViewType.Tree
+                        ? undefined
+                        : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined);
+                const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
+                const childLayout = Doc.Layout(pair.layout);
+                const rowHeight = () => {
+                    const aspect = Doc.NativeAspect(childLayout);
+                    return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
+                };
+                return (
+                    <TreeView
+                        key={child[Id]}
+                        ref={r => treeViewRefs.set(child, r ? r : undefined)}
+                        document={pair.layout}
+                        dataDoc={pair.data}
+                        containerCollection={containerCollection}
+                        prevSibling={docs[i]}
+                        // TODO: [AL] add these
+                        hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
+                        AddToMap={AddToMap}
+                        RemFromMap={RemFromMap}
+                        treeView={treeView}
+                        indentDocument={indent}
+                        outdentDocument={outdent}
+                        onCheckedClick={onCheckedClick}
+                        onChildClick={onChildClick}
+                        renderDepth={renderDepth}
+                        removeDoc={StrCast(containerCollection.freezeChildren).includes('remove') ? undefined : remove}
+                        addDocument={addDocument}
+                        styleProvider={styleProvider}
+                        panelWidth={rowWidth}
+                        panelHeight={rowHeight}
+                        dontRegisterView={dontRegisterView}
+                        moveDocument={move}
+                        dropAction={dropAction}
+                        addDocTab={addDocTab}
+                        ScreenToLocalTransform={screenToLocalXf}
+                        isContentActive={isContentActive}
+                        treeViewHideHeaderFields={treeViewHideHeaderFields}
+                        renderedIds={renderedIds}
+                        skipFields={skipFields}
+                        firstLevel={firstLevel}
+                        whenChildContentsActiveChanged={whenChildContentsActiveChanged}
+                        parentTreeView={parentTreeView}
+                        observeHeight={observerHeight}
+                        unobserveHeight={unobserveHeight}
+                        contextMenuItems={contextMenuItems}
+                    />
+                );
+            });
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 9de2cfcf9..a0ebe4cdc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,13 +1,12 @@
-import { Doc, Field, FieldResult, HeightSym, WidthSym } from "../../../../fields/Doc";
-import { Id, ToString } from "../../../../fields/FieldSymbols";
-import { ObjectField } from "../../../../fields/ObjectField";
-import { RefField } from "../../../../fields/RefField";
-import { listSpec } from "../../../../fields/Schema";
-import { Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { aggregateBounds } from "../../../../Utils";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import React = require("react");
-import { ColorScheme } from "../../../util/SettingsManager";
+import { Doc, Field, FieldResult, HeightSym, WidthSym } from '../../../../fields/Doc';
+import { Id, ToString } from '../../../../fields/FieldSymbols';
+import { ObjectField } from '../../../../fields/ObjectField';
+import { RefField } from '../../../../fields/RefField';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { aggregateBounds } from '../../../../Utils';
+import { ColorScheme } from '../../../util/SettingsManager';
+import React = require('react');
 
 export interface ViewDefBounds {
     type: string;
@@ -25,7 +24,7 @@ export interface ViewDefBounds {
     color?: string;
     opacity?: number;
     replica?: string;
-    pair?: { layout: Doc, data?: Doc };
+    pair?: { layout: Doc; data?: Doc };
 }
 
 export interface PoolData {
@@ -40,7 +39,7 @@ export interface PoolData {
     transition?: string;
     highlight?: boolean;
     replica: string;
-    pair: { layout: Doc, data?: Doc };
+    pair: { layout: Doc; data?: Doc };
 }
 
 export interface ViewDefResult {
@@ -48,7 +47,7 @@ export interface ViewDefResult {
     bounds?: ViewDefBounds;
 }
 function toLabel(target: FieldResult<Field>) {
-    if (typeof target === "number" || Number(target)) {
+    if (typeof target === 'number' || Number(target)) {
         const truncated = Number(Number(target).toFixed(0));
         const precise = Number(Number(target).toFixed(2));
         return truncated === precise ? Number(target).toFixed(0) : Number(target).toFixed(2);
@@ -60,16 +59,16 @@ function toLabel(target: FieldResult<Field>) {
 }
 /**
  * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
- * 
+ *
  * @param {String} text The text to be rendered.
  * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
- * 
+ *
  * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
  */
 function getTextWidth(text: string, font: string): number {
     // re-use canvas object for better performance
-    const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement("canvas"));
-    const context = canvas.getContext("2d");
+    const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement('canvas'));
+    const context = canvas.getContext('2d');
     context.font = font;
     const metrics = context.measureText(text);
     return metrics.width;
@@ -81,14 +80,7 @@ interface PivotColumn {
     filters: string[];
 }
 
-export function computerPassLayout(
-    poolData: Map<string, PoolData>,
-    pivotDoc: Doc,
-    childPairs: { layout: Doc, data?: Doc }[],
-    panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
-    engineProps: any
-) {
+export function computerPassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
     const docMap = new Map<string, PoolData>();
     childPairs.forEach(({ layout, data }, i) => {
         docMap.set(layout[Id], {
@@ -97,60 +89,49 @@ export function computerPassLayout(
             width: layout[WidthSym](),
             height: layout[HeightSym](),
             pair: { layout, data },
-            replica: ""
+            replica: '',
         });
     });
     return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
 }
 
-export function computerStarburstLayout(
-    poolData: Map<string, PoolData>,
-    pivotDoc: Doc,
-    childPairs: { layout: Doc, data?: Doc }[],
-    panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
-    engineProps: any
-) {
+export function computerStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
     const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
     const docMap = new Map<string, PoolData>();
-    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume an icon sized at 75
+    const docSize = mustFit ? panelDim[0] * 0.33 : 75; // assume an icon sized at 75
     const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize];
     const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize];
     childPairs.forEach(({ layout, data }, i) => {
-        const docSize = layout.layoutKey === "layout_icon" ? (mustFit ? panelDim[0] * .33 : 75) : 400; // assume a icon sized at 75
-        const deg = i / childPairs.length * Math.PI * 2;
+        const docSize = layout.layoutKey === 'layout_icon' ? (mustFit ? panelDim[0] * 0.33 : 75) : 400; // assume a icon sized at 75
+        const deg = (i / childPairs.length) * Math.PI * 2;
         docMap.set(layout[Id], {
             x: Math.cos(deg) * burstRadius[0] - docSize / 2,
-            y: Math.sin(deg) * burstRadius[1] - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
-            width: docSize,//layout[WidthSym](),
-            height: docSize * layout[HeightSym]() / layout[WidthSym](),
+            y: Math.sin(deg) * burstRadius[1] - (docSize * layout[HeightSym]()) / layout[WidthSym]() / 2,
+            width: docSize, //layout[WidthSym](),
+            height: (docSize * layout[HeightSym]()) / layout[WidthSym](),
             zIndex: NumCast(layout.zIndex),
             pair: { layout, data },
-            replica: ""
+            replica: '',
         });
     });
-    const divider = { type: "div", color: "transparent", x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
+    const divider = { type: 'div', color: 'transparent', x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
     return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
 }
 
-
-export function computePivotLayout(
-    poolData: Map<string, PoolData>,
-    pivotDoc: Doc,
-    childPairs: { layout: Doc, data?: Doc }[],
-    panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
-    engineProps: any
-) {
+export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
     const docMap = new Map<string, PoolData>();
-    const fieldKey = "data";
+    const fieldKey = 'data';
     const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
 
     let nonNumbers = 0;
-    const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || "author";
+    const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || 'author';
     childPairs.map(pair => {
-        const lval = pivotFieldKey === "#" || pivotFieldKey === "tags" ? Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(k => k.startsWith("#")).map(k => k.substring(1)) :
-            Cast(pair.layout[pivotFieldKey], listSpec("string"), null);
+        const lval =
+            pivotFieldKey === '#' || pivotFieldKey === 'tags'
+                ? Array.from(Object.keys(Doc.GetProto(pair.layout)))
+                      .filter(k => k.startsWith('#'))
+                      .map(k => k.substring(1))
+                : Cast(pair.layout[pivotFieldKey], listSpec('string'), null);
 
         const num = toNumber(pair.layout[pivotFieldKey]);
         if (num === undefined || Number.isNaN(num)) {
@@ -166,7 +147,7 @@ export function computePivotLayout(
         } else if (val) {
             !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
             pivotColumnGroups.get(val)!.docs.push(pair.layout);
-            pivotColumnGroups.get(val)!.replicas.push("");
+            pivotColumnGroups.get(val)!.replicas.push('');
         } else {
             docMap.set(pair.layout[Id], {
                 x: 0,
@@ -175,11 +156,11 @@ export function computePivotLayout(
                 width: 0,
                 height: 0,
                 pair,
-                replica: ""
+                replica: '',
             });
         }
     });
-    const pivotNumbers = nonNumbers / childPairs.length < .1;
+    const pivotNumbers = nonNumbers / childPairs.length < 0.1;
     if (pivotColumnGroups.size > 10) {
         const arrayofKeys = Array.from(pivotColumnGroups.keys());
         const sortedKeys = pivotNumbers ? arrayofKeys.sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : arrayofKeys.sort();
@@ -196,9 +177,11 @@ export function computePivotLayout(
             }
         }
     }
-    const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
+    const fontSize = NumCast(pivotDoc[fieldKey + '-timelineFontSize'], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
     const desc = `${fontSize}px ${getComputedStyle(document.body).fontFamily}`;
-    const textlen = Array.from(pivotColumnGroups.keys()).map(c => getTextWidth(toLabel(c), desc)).reduce((p, c) => Math.max(p, c), 0 as number);
+    const textlen = Array.from(pivotColumnGroups.keys())
+        .map(c => getTextWidth(toLabel(c), desc))
+        .reduce((p, c) => Math.max(p, c), 0 as number);
     const max_text = Math.min(Math.ceil(textlen / 120) * 28, panelDim[1] / 2);
     const maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
 
@@ -222,7 +205,7 @@ export function computePivotLayout(
     const groupNames: ViewDefBounds[] = [];
 
     const expander = 1.05;
-    const gap = .15;
+    const gap = 0.15;
     const maxColHeight = pivotAxisWidth * expander * Math.ceil(maxInColumn / numCols);
     let x = 0;
     const sortedPivotKeys = pivotNumbers ? Array.from(pivotColumnGroups.keys()).sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : Array.from(pivotColumnGroups.keys()).sort();
@@ -232,14 +215,14 @@ export function computePivotLayout(
         let xCount = 0;
         const text = toLabel(key);
         groupNames.push({
-            type: "text",
+            type: 'text',
             text,
             x,
             y: pivotAxisWidth,
             width: pivotAxisWidth * expander * numCols,
             height: max_text,
             fontSize,
-            payload: val
+            payload: val,
         });
         val.docs.forEach((doc, i) => {
             const layoutDoc = Doc.Layout(doc);
@@ -249,13 +232,13 @@ export function computePivotLayout(
                 hgt = pivotAxisWidth;
                 wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
             }
-            docMap.set(doc[Id] + (val.replicas || ""), {
-                x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0),
+            docMap.set(doc[Id] + (val.replicas || ''), {
+                x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? ((numCols - val.docs.length) * pivotAxisWidth) / 2 : 0),
                 y: -y + (pivotAxisWidth - hgt) / 2,
                 width: wid,
                 height: hgt,
                 pair: { layout: doc },
-                replica: val.replicas[i]
+                replica: val.replicas[i],
             });
             xCount++;
             if (xCount >= numCols) {
@@ -266,13 +249,14 @@ export function computePivotLayout(
         x += pivotAxisWidth * (numCols * expander + gap);
     });
 
-    const dividers = sortedPivotKeys.map((key, i) =>
-    ({
-        type: "div", color: "lightGray",
-        x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2,
-        y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander,
+    const dividers = sortedPivotKeys.map((key, i) => ({
+        type: 'div',
+        color: 'lightGray',
+        x: i * pivotAxisWidth * (numCols * expander + gap) - (pivotAxisWidth * (expander - 1)) / 2,
+        y: -maxColHeight + pivotAxisWidth,
+        width: pivotAxisWidth * numCols * expander,
         height: maxColHeight,
-        payload: pivotColumnGroups.get(key)!.filters
+        payload: pivotColumnGroups.get(key)!.filters,
     }));
     groupNames.push(...dividers);
     return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
@@ -282,24 +266,17 @@ function toNumber(val: FieldResult<Field>) {
     return val === undefined ? undefined : NumCast(val, Number(StrCast(val)));
 }
 
-export function computeTimelineLayout(
-    poolData: Map<string, PoolData>,
-    pivotDoc: Doc,
-    childPairs: { layout: Doc, data?: Doc }[],
-    panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
-    engineProps?: any
-) {
-    const fieldKey = "data";
+export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps?: any) {
+    const fieldKey = 'data';
     const pivotDateGroups = new Map<number, Doc[]>();
     const docMap = new Map<string, PoolData>();
     const groupNames: ViewDefBounds[] = [];
     const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field);
-    const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]);
-    const curTimeSpan = Cast(pivotDoc[fieldKey + "-timelineSpan"], "number", null);
-    const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMinReq"], "number", null) : curTime && (curTime - curTimeSpan);
-    const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMaxReq"], "number", null) : curTime && (curTime + curTimeSpan);
-    const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
+    const curTime = toNumber(pivotDoc[fieldKey + '-timelineCur']);
+    const curTimeSpan = Cast(pivotDoc[fieldKey + '-timelineSpan'], 'number', null);
+    const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMinReq'], 'number', null) : curTime && curTime - curTimeSpan;
+    const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMaxReq'], 'number', null) : curTime && curTime + curTimeSpan;
+    const fontSize = NumCast(pivotDoc[fieldKey + '-timelineFontSize'], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
     const fontHeight = panelDim[1] > 58 ? 30 : panelDim[1] / 2;
     const findStack = (time: number, stack: number[]) => {
         const index = stack.findIndex(val => val === undefined || val < x);
@@ -325,8 +302,8 @@ export function computeTimelineLayout(
         }
     }
     setTimeout(() => {
-        pivotDoc[fieldKey + "-timelineMin"] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
-        pivotDoc[fieldKey + "-timelineMax"] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
+        pivotDoc[fieldKey + '-timelineMin'] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
+        pivotDoc[fieldKey + '-timelineMax'] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
     }, 0);
 
     if (maxTime === minTime) {
@@ -340,10 +317,10 @@ export function computeTimelineLayout(
     let prevKey = Math.floor(minTime);
 
     if (sortedKeys.length && scaling * (sortedKeys[0] - prevKey) > 25) {
-        groupNames.push({ type: "text", text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: 'text', text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
     if (!sortedKeys.length && curTime !== undefined) {
-        groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: 'text', text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: 'orange', y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
     const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
@@ -351,26 +328,26 @@ export function computeTimelineLayout(
     let zind = 0;
     sortedKeys.forEach(key => {
         if (curTime !== undefined && curTime > prevKey && curTime <= key) {
-            groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key });
+            groupNames.push({ type: 'text', text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: 'orange', height: fontHeight, fontSize, payload: key });
         }
         const keyDocs = pivotDateGroups.get(key)!;
         x += scaling * (key - prevKey);
         const stack = findStack(x, stacking);
         prevKey = key;
         if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) {
-            groupNames.push({ type: "text", text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
+            groupNames.push({ type: 'text', text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
         }
         layoutDocsAtTime(keyDocs, key);
     });
     if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) {
         x = (curTime - minTime) * scaling;
-        groupNames.push({ type: "text", text: toLabel(curTime), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: 'text', text: toLabel(curTime), x: x, y: 0, zIndex: 1000, color: 'orange', height: fontHeight, fontSize, payload: undefined });
     }
     if (Math.ceil(maxTime - minTime) * scaling > x + 25) {
-        groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: 'text', text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
-    const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimgray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+    const divider = { type: 'div', color: Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'black', x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
     return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
@@ -384,13 +361,14 @@ export function computeTimelineLayout(
                 wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
             }
             docMap.set(doc[Id], {
-                x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
-                zIndex: (curTime === key ? 1000 : zind++),
+                x: x,
+                y: (-Math.sqrt(stack) * pivotAxisWidth) / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
+                zIndex: curTime === key ? 1000 : zind++,
                 highlight: curTime === key,
-                width: wid / (Math.max(stack, 1)),
-                height: hgt / (Math.max(stack, 1)),
+                width: wid / Math.max(stack, 1),
+                height: hgt / Math.max(stack, 1),
                 pair: { layout: doc },
-                replica: ""
+                replica: '',
             });
             stacking[stack] = x + pivotAxisWidth;
         });
@@ -407,41 +385,49 @@ function normalizeResults(
     minWidth: number,
     extras: ViewDefBounds[]
 ): ViewDefResult[] {
-    const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
+    const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height } as ViewDefBounds));
     const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
-    const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
+    const aggBounds = aggregateBounds(
+        extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: 'doc', payload: '' })))).filter(e => e.zIndex !== -99),
+        0,
+        0
+    );
     aggBounds.r = aggBounds.x + Math.max(minWidth, aggBounds.r - aggBounds.x);
     const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
-    let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
+    let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? panelDim[1] / (aggBounds.b - aggBounds.y) : wscale;
     if (Number.isNaN(scale)) scale = 1;
 
-    Array.from(docMap.entries()).filter(ele => ele[1].pair).map(ele => {
-        const newPosRaw = ele[1];
-        if (newPosRaw) {
-            const newPos = {
-                x: newPosRaw.x * scale,
-                y: newPosRaw.y * scale,
-                z: newPosRaw.z,
-                replica: newPosRaw.replica,
-                highlight: newPosRaw.highlight,
-                zIndex: newPosRaw.zIndex,
-                width: (newPosRaw.width || 0) * scale,
-                height: newPosRaw.height! * scale,
-                pair: ele[1].pair
-            };
-            poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "all 1s", ...newPos });
-        }
-    });
+    Array.from(docMap.entries())
+        .filter(ele => ele[1].pair)
+        .map(ele => {
+            const newPosRaw = ele[1];
+            if (newPosRaw) {
+                const newPos = {
+                    x: newPosRaw.x * scale,
+                    y: newPosRaw.y * scale,
+                    z: newPosRaw.z,
+                    replica: newPosRaw.replica,
+                    highlight: newPosRaw.highlight,
+                    zIndex: newPosRaw.zIndex,
+                    width: (newPosRaw.width || 0) * scale,
+                    height: newPosRaw.height! * scale,
+                    pair: ele[1].pair,
+                };
+                poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos });
+            }
+        });
 
-    return viewDefsToJSX(extras.concat(groupNames).map(gname => ({
-        type: gname.type,
-        text: gname.text,
-        x: gname.x * scale,
-        y: gname.y * scale,
-        color: gname.color,
-        width: gname.width === undefined ? undefined : gname.width * scale,
-        height: gname.height === -1 ? 1 : gname.type === "text" ? Math.max(fontHeight * scale, (gname.height || 0) * scale) : (gname.height || 0) * scale,
-        fontSize: gname.fontSize,
-        payload: gname.payload
-    })));
+    return viewDefsToJSX(
+        extras.concat(groupNames).map(gname => ({
+            type: gname.type,
+            text: gname.text,
+            x: gname.x * scale,
+            y: gname.y * scale,
+            color: gname.color,
+            width: gname.width === undefined ? undefined : gname.width * scale,
+            height: gname.height === -1 ? 1 : gname.type === 'text' ? Math.max(fontHeight * scale, (gname.height || 0) * scale) : (gname.height || 0) * scale,
+            fontSize: gname.fontSize,
+            payload: gname.payload,
+        }))
+    );
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 5f890c810..d979ef961 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,19 +1,18 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Field } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { List } from "../../../../fields/List";
-import { Cast, NumCast } from "../../../../fields/Types";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Field } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { Cast, NumCast } from '../../../../fields/Types';
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { LinkManager } from "../../../util/LinkManager";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { DocumentView } from "../../nodes/DocumentView";
-import "./CollectionFreeFormLinkView.scss";
-import React = require("react");
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { Colors } from "../../global/globalEnums";
-
+import { LinkManager } from '../../../util/LinkManager';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Colors } from '../../global/globalEnums';
+import { DocumentView } from '../../nodes/DocumentView';
+import './CollectionFreeFormLinkView.scss';
+import React = require('react');
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -27,31 +26,41 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     @observable _start = 0;
     _anchorDisposer: IReactionDisposer | undefined;
     _timeout: NodeJS.Timeout | undefined;
-    componentWillUnmount() { this._anchorDisposer?.(); }
-    @action timeout = action(() => (Date.now() < this._start++ + 1000) && (this._timeout = setTimeout(this.timeout, 25)));
+    componentWillUnmount() {
+        this._anchorDisposer?.();
+    }
+    @action timeout = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25)));
     componentDidMount() {
-        this._anchorDisposer = reaction(() => [
-            this.props.A.props.ScreenToLocalTransform(),
-            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
-            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
-            this.props.B.props.ScreenToLocalTransform(),
-            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
-            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
-        ],
+        this._anchorDisposer = reaction(
+            () => [
+                this.props.A.props.ScreenToLocalTransform(),
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
+                this.props.B.props.ScreenToLocalTransform(),
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
+            ],
             action(() => {
                 this._start = Date.now();
                 this._timeout && clearTimeout(this._timeout);
                 this._timeout = setTimeout(this.timeout, 25);
                 setTimeout(this.placeAnchors);
-            })
-            , { fireImmediately: true });
+            }),
+            { fireImmediately: true }
+        );
     }
     placeAnchors = () => {
         const { A, B, LinkDocs } = this.props;
         const linkDoc = LinkDocs[0];
         if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
-        setTimeout(action(() => this._opacity = 0.75), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
-        setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
+        setTimeout(
+            action(() => (this._opacity = 0.75)),
+            0
+        ); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
+        setTimeout(
+            action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)),
+            750
+        ); // this will unhighlight the link line.
         const a = A.ContentDiv.getBoundingClientRect();
         const b = B.ContentDiv.getBoundingClientRect();
         const { left: aleft, top: atop, width: awidth, height: aheight } = A.ContentDiv.parentElement!.getBoundingClientRect();
@@ -60,7 +69,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const bpt = Utils.closestPtBetweenRectangles(bleft, btop, bwidth, bheight, aleft, atop, awidth, aheight, apt.point.x, apt.point.y);
 
         // really hacky stuff to make the LinkAnchorBox display where we want it to:
-        //   if there's an element in the DOM with a classname containing a link anchor's id, 
+        //   if there's an element in the DOM with a classname containing a link anchor's id,
         //   then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
         //   otherwise, we just use the computed nearest point on the document boundary to the target Document
         const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor1 as Doc)[Id])).lastElement();
@@ -68,8 +77,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
         if (!targetAhyperlink) {
             if (linkDoc.linkAutoMove) {
-                linkDoc.anchor1_x = (apt.point.x - aleft) / awidth * 100;
-                linkDoc.anchor1_y = (apt.point.y - atop) / aheight * 100;
+                linkDoc.anchor1_x = ((apt.point.x - aleft) / awidth) * 100;
+                linkDoc.anchor1_y = ((apt.point.y - atop) / aheight) * 100;
             }
         } else {
             const m = targetAhyperlink.getBoundingClientRect();
@@ -78,13 +87,13 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             const mpy = mp[1] / A.props.PanelHeight();
             if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
             if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
-            if (getComputedStyle(targetAhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+            if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
             else linkDoc.opacity = 1;
         }
         if (!targetBhyperlink) {
             if (linkDoc.linkAutoMove) {
-                linkDoc.anchor2_x = (bpt.point.x - bleft) / bwidth * 100;
-                linkDoc.anchor2_y = (bpt.point.y - btop) / bheight * 100;
+                linkDoc.anchor2_x = ((bpt.point.x - bleft) / bwidth) * 100;
+                linkDoc.anchor2_y = ((bpt.point.y - btop) / bheight) * 100;
             }
         } else {
             const m = targetBhyperlink.getBoundingClientRect();
@@ -93,80 +102,86 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             const mpy = mp[1] / B.props.PanelHeight();
             if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
             if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
-            if (getComputedStyle(targetBhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+            if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
             else linkDoc.opacity = 1;
         }
-    }
-
+    };
 
     pointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
-            this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
-            return false;
-        }, emptyFunction, () => {
-            // OverlayView.Instance.addElement(
-            //     <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
-            //         showLinks={action(() => { })}
-            //     />, { x: 300, y: 300 });
-        });
-
-
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
+                this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
+                return false;
+            },
+            emptyFunction,
+            () => {
+                // OverlayView.Instance.addElement(
+                //     <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
+                //         showLinks={action(() => { })}
+                //     />, { x: 300, y: 300 });
+            }
+        );
+    };
 
     visibleY = (el: any) => {
         let rect = el.getBoundingClientRect();
-        const top = rect.top, height = rect.height;
+        const top = rect.top,
+            height = rect.height;
         var el = el.parentNode;
         while (el && el !== document.body) {
             rect = el.getBoundingClientRect?.();
             if (rect?.width) {
-                if (top <= rect.bottom === false && getComputedStyle(el).overflow === "hidden") return rect.bottom;
+                if (top <= rect.bottom === false && getComputedStyle(el).overflow === 'hidden') return rect.bottom;
                 // Check if the element is out of view due to a container scrolling
-                if ((top + height) <= rect.top && getComputedStyle(el).overflow === "hidden") return rect.top;
+                if (top + height <= rect.top && getComputedStyle(el).overflow === 'hidden') return rect.top;
             }
             el = el.parentNode;
         }
         // Check its within the document viewport
         return top; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
-    }
+    };
     visibleX = (el: any) => {
         let rect = el.getBoundingClientRect();
-        const left = rect.left, width = rect.width;
+        const left = rect.left,
+            width = rect.width;
         var el = el.parentNode;
         while (el && el !== document.body) {
             rect = el?.getBoundingClientRect();
             if (rect?.width) {
-                if (left <= rect.right === false && getComputedStyle(el).overflow === "hidden") return rect.right;
+                if (left <= rect.right === false && getComputedStyle(el).overflow === 'hidden') return rect.right;
                 // Check if the element is out of view due to a container scrolling
-                if ((left + width) <= rect.left && getComputedStyle(el).overflow === "hidden") return rect.left;
+                if (left + width <= rect.left && getComputedStyle(el).overflow === 'hidden') return rect.left;
             }
             el = el.parentNode;
         }
         // Check its within the document viewport
         return left; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
-    }
+    };
 
     @action
     toggleProperties = () => {
-        if (CurrentUserUtils.propertiesWidth > 0) {
-            CurrentUserUtils.propertiesWidth = 0;
+        if (SettingsManager.propertiesWidth > 0) {
+            SettingsManager.propertiesWidth = 0;
         } else {
-            CurrentUserUtils.propertiesWidth = 250;
+            SettingsManager.propertiesWidth = 250;
         }
-    }
+    };
 
     onClickLine = () => {
         SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
         this.toggleProperties();
-    }
+    };
 
     @computed.struct get renderData() {
-        this._start; SnappingManager.GetIsDragging();
+        this._start;
+        SnappingManager.GetIsDragging();
         const { A, B, LinkDocs } = this.props;
         if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
-        const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
-        const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
+        const acont = A.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
+        const bcont = B.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
         const adiv = acont.length ? acont[0] : A.ContentDiv;
         const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
         for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
@@ -185,11 +200,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const pt2 = [bleft + b.width / 2, btop + b.width / 2];
         const pt1vec = [(bDocBounds.left + bDocBounds.right) / 2 - pt1[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt1[1]];
         const pt2vec = [(aDocBounds.left + aDocBounds.right) / 2 - pt2[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt2[1]];
-        const pt1len = Math.sqrt((pt1vec[0] * pt1vec[0]) + (pt1vec[1] * pt1vec[1]));
-        const pt2len = Math.sqrt((pt2vec[0] * pt2vec[0]) + (pt2vec[1] * pt2vec[1]));
+        const pt1len = Math.sqrt(pt1vec[0] * pt1vec[0] + pt1vec[1] * pt1vec[1]);
+        const pt2len = Math.sqrt(pt2vec[0] * pt2vec[0] + pt2vec[1] * pt2vec[1]);
         const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 2;
-        const pt1norm = clipped ? [0, 0] : [pt1vec[0] / pt1len * ptlen, pt1vec[1] / pt1len * ptlen];
-        const pt2norm = clipped ? [0, 0] : [pt2vec[0] / pt2len * ptlen, pt2vec[1] / pt2len * ptlen];
+        const pt1norm = clipped ? [0, 0] : [(pt1vec[0] / pt1len) * ptlen, (pt1vec[1] / pt1len) * ptlen];
+        const pt2norm = clipped ? [0, 0] : [(pt2vec[0] / pt2len) * ptlen, (pt2vec[1] / pt2len) * ptlen];
         const pt1normlen = Math.sqrt(pt1norm[0] * pt1norm[0] + pt1norm[1] * pt1norm[1]) || 1;
         const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
         const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
@@ -203,7 +218,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     }
 
     render() {
-        if (!this.renderData) return (null);
+        if (!this.renderData) return null;
 
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
         LinkManager.currentLink = this.props.LinkDocs[0];
@@ -216,31 +231,37 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
 
         //access stroke color using index of the relationship in the color list (default black)
-        const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? "black" : linkColorList[currRelationshipIndex];
+        const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? 'black' : linkColorList[currRelationshipIndex];
         // const hexStroke = this.rgbToHex(stroke)
 
         //calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has)
         //thickness varies linearly from 3px to 12px for increasing link count
-        const strokeWidth = linkSize === -1 ? "3px" : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + "px";
+        const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
 
         if (this.props.LinkDocs[0].displayArrow === undefined) {
             this.props.LinkDocs[0].displayArrow = false;
         }
 
-        return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
-            <defs>
-                <marker id="arrowhead" markerWidth="4" markerHeight="3"
-                    refX="0" refY="1.5" orient="auto">
-                    <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
-                </marker>
-            </defs>
-            <path className="collectionfreeformlinkview-linkLine" style={{ pointerEvents: "all", opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
-                onClick={this.onClickLine}
-                d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
-                markerEnd={this.props.LinkDocs[0].displayArrow ? "url(#arrowhead)" : ""} />
-            {textX === undefined ? (null) : <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown} >
-                {Field.toString(this.props.LinkDocs[0].description as any as Field)}
-            </text>}
-        </>);
+        return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || (!this.props.LinkDocs[0].linkDisplay && !aActive && !bActive) ? null : (
+            <>
+                <defs>
+                    <marker id="arrowhead" markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
+                        <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
+                    </marker>
+                </defs>
+                <path
+                    className="collectionfreeformlinkview-linkLine"
+                    style={{ pointerEvents: 'all', opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
+                    onClick={this.onClickLine}
+                    d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
+                    markerEnd={this.props.LinkDocs[0].displayArrow ? 'url(#arrowhead)' : ''}
+                />
+                {textX === undefined ? null : (
+                    <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
+                        {Field.toString(this.props.LinkDocs[0].description as any as Field)}
+                    </text>
+                )}
+            </>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 9f6938e67..9e8d92d7d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,79 +1,82 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
 import * as mobxUtils from 'mobx-utils';
-import CursorField from "../../../../fields/CursorField";
-import { FieldResult } from "../../../../fields/Doc";
-import { List } from "../../../../fields/List";
-import { listSpec } from "../../../../fields/Schema";
-import { Cast } from "../../../../fields/Types";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { CollectionViewProps } from "../CollectionView";
-import "./CollectionFreeFormView.scss";
-import React = require("react");
-import v5 = require("uuid/v5");
+import CursorField from '../../../../fields/CursorField';
+import { Doc, FieldResult } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast } from '../../../../fields/Types';
+import { CollectionViewProps } from '../CollectionView';
+import './CollectionFreeFormView.scss';
+import React = require('react');
+import v5 = require('uuid/v5');
 
 @observer
 export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
-
     @computed protected get cursors(): CursorField[] {
         const doc = this.props.Document;
 
         let cursors: FieldResult<List<CursorField>>;
-        const { id } = CurrentUserUtils;
+        const id = Doc.UserDoc()[Id];
         if (!id || !(cursors = Cast(doc.cursors, listSpec(CursorField)))) {
             return [];
         }
         const now = mobxUtils.now();
-        return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && (now - metadata.timestamp) < 1000);
+        return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && now - metadata.timestamp < 1000);
     }
 
     @computed get renderedCursors() {
-        return this.cursors.map(({ data: { metadata, position: { x, y } } }) => {
-            return (
-                <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont"
-                    style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}
-                >
-                    <canvas className="collectionFreeFormRemoteCursors-canvas"
-                        ref={(el) => {
-                            if (el) {
-                                const ctx = el.getContext('2d');
-                                if (ctx) {
-                                    ctx.fillStyle = "#" + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + "22";
-                                    ctx.fillRect(0, 0, 20, 20);
+        return this.cursors.map(
+            ({
+                data: {
+                    metadata,
+                    position: { x, y },
+                },
+            }) => {
+                return (
+                    <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont" style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}>
+                        <canvas
+                            className="collectionFreeFormRemoteCursors-canvas"
+                            ref={el => {
+                                if (el) {
+                                    const ctx = el.getContext('2d');
+                                    if (ctx) {
+                                        ctx.fillStyle = '#' + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + '22';
+                                        ctx.fillRect(0, 0, 20, 20);
 
-                                    ctx.fillStyle = "black";
-                                    ctx.lineWidth = 0.5;
+                                        ctx.fillStyle = 'black';
+                                        ctx.lineWidth = 0.5;
 
-                                    ctx.beginPath();
+                                        ctx.beginPath();
 
-                                    ctx.moveTo(10, 0);
-                                    ctx.lineTo(10, 8);
+                                        ctx.moveTo(10, 0);
+                                        ctx.lineTo(10, 8);
 
-                                    ctx.moveTo(10, 20);
-                                    ctx.lineTo(10, 12);
+                                        ctx.moveTo(10, 20);
+                                        ctx.lineTo(10, 12);
 
-                                    ctx.moveTo(0, 10);
-                                    ctx.lineTo(8, 10);
+                                        ctx.moveTo(0, 10);
+                                        ctx.lineTo(8, 10);
 
-                                    ctx.moveTo(20, 10);
-                                    ctx.lineTo(12, 10);
+                                        ctx.moveTo(20, 10);
+                                        ctx.lineTo(12, 10);
 
-                                    ctx.stroke();
+                                        ctx.stroke();
+                                    }
                                 }
-                            }
-                        }}
-                        width={20}
-                        height={20}
-                    />
-                    <p className="collectionFreeFormRemoteCursors-symbol">
-                        {metadata.identifier[0].toUpperCase()}
-                    </p>
-                </div>
-            );
-        });
+                            }}
+                            width={20}
+                            height={20}
+                        />
+                        <p className="collectionFreeFormRemoteCursors-symbol">{metadata.identifier[0].toUpperCase()}</p>
+                    </div>
+                );
+            }
+        );
     }
 
     render() {
         return this.renderedCursors;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8444c9119..07ea26346 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -18,13 +18,13 @@ import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
 import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
 import { Docs, DocUtils } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { HistoryUtil } from '../../../util/History';
 import { InteractionUtils } from '../../../util/InteractionUtils';
 import { RecordingApi } from '../../../util/RecordingApi';
+import { ReplayMovements } from '../../../util/ReplayMovements';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { ColorScheme } from '../../../util/SettingsManager';
@@ -48,7 +48,6 @@ import { StyleProp } from '../../StyleProvider';
 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';
@@ -56,8 +55,6 @@ import './CollectionFreeFormView.scss';
 import { MarqueeView } from './MarqueeView';
 import React = require('react');
 import e = require('connect-flash');
-import { ReplayMovements } from '../../../util/ReplayMovements';
-
 
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -517,7 +514,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
                 !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)
             ) {
-                switch (CurrentUserUtils.ActiveTool) {
+                switch (Doc.ActiveTool) {
                     case InkTool.Highlighter:
                         break;
                     // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
@@ -556,7 +553,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.addMoveListeners();
                     this.removeEndListeners();
                     this.addEndListeners();
-                    if (CurrentUserUtils.ActiveTool === InkTool.None) {
+                    if (Doc.ActiveTool === InkTool.None) {
                         this._lastX = pt.pageX;
                         this._lastY = pt.pageY;
                         e.preventDefault();
@@ -577,14 +574,14 @@ 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.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, {
+                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), Doc.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.ActiveTool === InkTool.Write) {
+                if (Doc.ActiveTool === InkTool.Write) {
                     this.unprocessedDocs.push(inkDoc);
                     CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
                 }
@@ -775,7 +772,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)) {
-            CurrentUserUtils.ActiveTool = InkTool.None;
+            Doc.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
             if (this.tryDragCluster(e, this._hitCluster)) {
@@ -907,7 +904,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
             if (myTouches[0]) {
-                if (CurrentUserUtils.ActiveTool === InkTool.None) {
+                if (Doc.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();
@@ -1061,8 +1058,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        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 (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(Doc.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();
@@ -1079,7 +1075,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         Doc.UserDoc()?.presentationMode === 'recording' && RecordingApi.Instance.setRecordingFFView(this);
         // TODO: make this based off the specific recording FFView
         Doc.UserDoc()?.presentationMode === 'none' && RecordingApi.Instance.setPlayFFView(this);
-        
+
         // TODO: zzz + michael to figure out this merge in case of strange behaviour
         // if (Doc.UserDoc()?.presentationMode === 'watching') {
         //     RecordingApi.Instance.pauseVideoAndMovements();
@@ -1118,7 +1114,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 || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) {
+        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) {
             this._viewTransition = panTime;
             const scale = this.getLocalTransform().inverse().Scale;
             const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
@@ -2218,7 +2214,7 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
         const renderGridSpace = gridSpace * this.props.zoomScaling();
         const w = this.props.PanelWidth() + 2 * renderGridSpace;
         const h = this.props.PanelHeight() + 2 * renderGridSpace;
-        const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
+        const strokeStyle = Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
         return (
             <canvas
                 className="collectionFreeFormView-grid"
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ab8a34d5a..4513ffb39 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,36 +1,33 @@
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, DocListCastAsync, Opt } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { InkData, InkField, InkTool } from "../../../../fields/InkField";
-import { List } from "../../../../fields/List";
-import { RichTextField } from "../../../../fields/RichTextField";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-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";
-import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
-import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { ContextMenu } from "../../ContextMenu";
-import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PinViewProps, PresBox } from "../../nodes/trails/PresBox";
-import { PresMovement } from "../../nodes/trails/PresEnums";
-import { VideoBox } from "../../nodes/VideoBox";
-import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
-import { PreviewCursor } from "../../PreviewCursor";
-import { CollectionDockingView } from "../CollectionDockingView";
-import { SubCollectionViewProps } from "../CollectionSubView";
-import { TreeView } from "../TreeView";
-import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
-import "./MarqueeView.scss";
-import React = require("react");
-import { TabDocView } from "../TabDocView";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { InkData, InkField, InkTool } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+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';
+import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
+import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { PinViewProps, PresBox } from '../../nodes/trails/PresBox';
+import { VideoBox } from '../../nodes/VideoBox';
+import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
+import { PreviewCursor } from '../../PreviewCursor';
+import { SubCollectionViewProps } from '../CollectionSubView';
+import { TabDocView } from '../TabDocView';
+import { TreeView } from '../TreeView';
+import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import './MarqueeView.scss';
+import React = require('react');
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -46,15 +43,14 @@ interface MarqueeViewProps {
 }
 
 export interface MarqueeViewBounds {
-	left: number;
-	top: number;
-	width: number;
-	height: number;
+    left: number;
+    top: number;
+    width: number;
+    height: number;
 }
 
 @observer
-export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
-{
+export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
     private _commandExecuted = false;
     @observable _lastX: number = 0;
     @observable _lastY: number = 0;
@@ -64,18 +60,26 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     @observable _lassoPts: [number, number][] = [];
     @observable _lassoFreehand: boolean = false;
 
-    @computed get Transform() { return this.props.getTransform(); }
+    @computed get Transform() {
+        return this.props.getTransform();
+    }
     @computed get Bounds() {
         // nda - ternary argument to transformPoint is returning the lower of the downX/Y and lastX/Y and passing in as args x,y
         const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
         // nda - args to transformDirection is just x and y diff btw downX/Y and lastX/Y
         const size = this.Transform.transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-		const bounds:MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+        const bounds: MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
         return bounds;
     }
-    get inkDoc() { return this.props.Document; }
-    get ink() { return Cast(this.props.Document.ink, InkField); }
-    set ink(value: Opt<InkField>) { this.props.Document.ink = value; }
+    get inkDoc() {
+        return this.props.Document;
+    }
+    get ink() {
+        return Cast(this.props.Document.ink, InkField);
+    }
+    set ink(value: Opt<InkField>) {
+        this.props.Document.ink = value;
+    }
 
     componentDidMount() {
         this.props.setPreviewCursor?.(this.setPreviewCursor);
@@ -84,14 +88,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     @action
     cleanupInteractions = (all: boolean = false, hideMarquee: boolean = true) => {
         if (all) {
-            document.removeEventListener("pointerup", this.onPointerUp, true);
-            document.removeEventListener("pointermove", this.onPointerMove, true);
+            document.removeEventListener('pointerup', this.onPointerUp, true);
+            document.removeEventListener('pointermove', this.onPointerMove, true);
         }
-        document.removeEventListener("keydown", this.marqueeCommand, true);
+        document.removeEventListener('keydown', this.marqueeCommand, true);
         hideMarquee && this.hideMarquee();
 
         this._lassoPts = [];
-    }
+    };
 
     @undoBatch
     @action
@@ -100,76 +104,75 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         // tslint:disable-next-line:prefer-const
         const cm = ContextMenu.Instance;
         const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
-        if (e.key === "?") {
-            cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
-                Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: "bing", useCors: true }), "add:right"));
+        if (e.key === '?') {
+            cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), 'add:right'));
 
             cm.displayMenu(this._downX, this._downY, undefined, true);
             e.stopPropagation();
-        } else
-            if (e.key === "u" && this.props.ungroup) {
-                e.stopPropagation();
-                this.props.ungroup();
-            }
-            else if (e.key === ":") {
-                DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
+        } else if (e.key === 'u' && this.props.ungroup) {
+            e.stopPropagation();
+            this.props.ungroup();
+        } else if (e.key === ':') {
+            DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
 
-                cm.displayMenu(this._downX, this._downY, undefined, true);
-                e.stopPropagation();
-            } else if (e.key === "a" && (e.ctrlKey || e.metaKey)) {
-                e.preventDefault();
-                this.props.selectDocuments(this.props.activeDocuments());
-                e.stopPropagation();
-            } else if (e.key === "q" && e.ctrlKey) {
-                e.preventDefault();
-                (async () => {
-                    const text: string = await navigator.clipboard.readText();
-                    const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
-                    for (let i = 0; i < ns.length - 1; i++) {
-                        while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
-                            ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
-                            ns[i].endsWith(".\r") || ns[i].endsWith(".") ||
-                            ns[i].endsWith(":\r") || ns[i].endsWith(":")) && i < ns.length - 1) {
-                            const sub = ns[i].endsWith("\r") ? 1 : 0;
-                            const br = ns[i + 1].trim() === "";
-                            ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
-                            if (br) break;
-                        }
+            cm.displayMenu(this._downX, this._downY, undefined, true);
+            e.stopPropagation();
+        } else if (e.key === 'a' && (e.ctrlKey || e.metaKey)) {
+            e.preventDefault();
+            this.props.selectDocuments(this.props.activeDocuments());
+            e.stopPropagation();
+        } else if (e.key === 'q' && e.ctrlKey) {
+            e.preventDefault();
+            (async () => {
+                const text: string = await navigator.clipboard.readText();
+                const ns = text.split('\n').filter(t => t.trim() !== '\r' && t.trim() !== '');
+                for (let i = 0; i < ns.length - 1; i++) {
+                    while (
+                        !(ns[i].trim() === '' || ns[i].endsWith('-\r') || ns[i].endsWith('-') || ns[i].endsWith(';\r') || ns[i].endsWith(';') || ns[i].endsWith('.\r') || ns[i].endsWith('.') || ns[i].endsWith(':\r') || ns[i].endsWith(':')) &&
+                        i < ns.length - 1
+                    ) {
+                        const sub = ns[i].endsWith('\r') ? 1 : 0;
+                        const br = ns[i + 1].trim() === '';
+                        ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
+                        if (br) break;
                     }
-                    let ypos = y;
-                    ns.map(line => {
-                        const indent = line.search(/\S|$/);
-                        const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: ypos, title: line });
-                        this.props.addDocument?.(newBox);
-                        ypos += 40 * this.Transform.Scale;
-                    });
-                })();
-                e.stopPropagation();
-            } else if (e.key === "b" && e.ctrlKey) {
-                document.body.focus(); // so that we can access the clipboard without an error
-                setTimeout(() =>
-                    pasteImageBitmap((data: any, error: any) => {
-                        error && console.log(error);
-                        data && VideoBox.convertDataUri(data, this.props.Document[Id] + "-thumb-frozen").then(returnedfilename => {
-                            this.props.Document["thumb-frozen"] = new ImageField(returnedfilename);
+                }
+                let ypos = y;
+                ns.map(line => {
+                    const indent = line.search(/\S|$/);
+                    const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + (indent / 3) * 10, y: ypos, title: line });
+                    this.props.addDocument?.(newBox);
+                    ypos += 40 * this.Transform.Scale;
+                });
+            })();
+            e.stopPropagation();
+        } else if (e.key === 'b' && e.ctrlKey) {
+            document.body.focus(); // so that we can access the clipboard without an error
+            setTimeout(() =>
+                pasteImageBitmap((data: any, error: any) => {
+                    error && console.log(error);
+                    data &&
+                        VideoBox.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
+                            this.props.Document['thumb-frozen'] = new ImageField(returnedfilename);
                         });
-                    }));
-            } else if (e.key === "s" && e.ctrlKey) {
-                e.preventDefault();
-                const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
-                slide.x = x;
-                slide.y = y;
-                FormattedTextBox.SelectOnLoad = slide[Id];
-                TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
-                this.props.addDocument?.(slide);
-                e.stopPropagation();
-            } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
-                FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : "";
-                FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
-                this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xPadding === 0));
-                e.stopPropagation();
-            }
-    }
+                })
+            );
+        } else if (e.key === 's' && e.ctrlKey) {
+            e.preventDefault();
+            const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
+            slide.x = x;
+            slide.y = y;
+            FormattedTextBox.SelectOnLoad = slide[Id];
+            TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
+            this.props.addDocument?.(slide);
+            e.stopPropagation();
+        } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
+            FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : '';
+            FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('live text batch');
+            this.props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this.props.xPadding === 0));
+            e.stopPropagation();
+        }
+    };
     //heuristically converts pasted text into a table.
     // assumes each entry is separated by a tab
     // skips all rows until it gets to a row with more than one entry
@@ -178,26 +181,26 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     //         any row that has only one column is a section header-- this header is then added as a column to subsequent rows until the next header
     // assumes each cell is a string or a number
     pasteTable(ns: string[], x: number, y: number) {
-        while (ns.length > 0 && ns[0].split("\t").length < 2) {
+        while (ns.length > 0 && ns[0].split('\t').length < 2) {
             ns.splice(0, 1);
         }
         if (ns.length > 0) {
-            const columns = ns[0].split("\t");
+            const columns = ns[0].split('\t');
             const docList: Doc[] = [];
-            let groupAttr: string | number = "";
+            let groupAttr: string | number = '';
             const rowProto = new Doc();
             rowProto.title = rowProto.Id;
             rowProto._width = 200;
             rowProto.isPrototype = true;
             for (let i = 1; i < ns.length - 1; i++) {
-                const values = ns[i].split("\t");
+                const values = ns[i].split('\t');
                 if (values.length === 1 && columns.length > 1) {
                     groupAttr = values[0];
                     continue;
                 }
                 const docDataProto = Doc.MakeDelegate(rowProto);
                 docDataProto.isPrototype = true;
-                columns.forEach((col, i) => docDataProto[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
+                columns.forEach((col, i) => (docDataProto[columns[i]] = values.length > i ? (values[i].indexOf(Number(values[i]).toString()) !== -1 ? Number(values[i]) : values[i]) : undefined));
                 if (groupAttr) {
                     docDataProto._group = groupAttr;
                 }
@@ -206,7 +209,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 doc._width = 200;
                 docList.push(doc);
             }
-            const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", _width: 300, _height: 100 });
+            const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField('_group', '#f1efeb')] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, '#f1efeb'))], docList, {
+                x: x,
+                y: y,
+                title: 'droppedTable',
+                _width: 300,
+                _height: 100,
+            });
 
             this.props.addDocument?.(newCol);
         }
@@ -227,10 +236,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 // }
                 // bcz: do we need this?   it kills the context menu on the main collection if !altKey
                 // e.stopPropagation();
-            }
-            else PreviewCursor.Visible = false;
+            } else PreviewCursor.Visible = false;
         }
-    }
+    };
 
     @action
     onPointerMove = (e: PointerEvent): void => {
@@ -238,8 +246,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this._lastY = e.pageY;
         this._lassoPts.push([e.clientX, e.clientY]);
         if (!e.cancelBubble) {
-            if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD ||
-                Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
+            if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
                 if (!this._commandExecuted) {
                     this.showMarquee();
                 }
@@ -253,7 +260,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         if (PresBox.startMarquee) {
             e.stopPropagation();
         }
-    }
+    };
 
     @action
     onPointerUp = (e: PointerEvent): void => {
@@ -270,14 +277,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const hideMarquee = () => {
             this.hideMarquee();
             MarqueeOptionsMenu.Instance.fadeOut(true);
-            document.removeEventListener("pointerdown", hideMarquee);
-            document.removeEventListener("wheel", hideMarquee);
+            document.removeEventListener('pointerdown', hideMarquee);
+            document.removeEventListener('wheel', hideMarquee);
         };
         if (PresBox.startMarquee) {
             this.pinWithView();
             PresBox.startMarquee = false;
         }
-        if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100) && !PresBox.startMarquee) {
+        if (!this._commandExecuted && Math.abs(this.Bounds.height * this.Bounds.width) > 100 && !PresBox.startMarquee) {
             MarqueeOptionsMenu.Instance.createCollection = this.collection;
             MarqueeOptionsMenu.Instance.delete = this.delete;
             MarqueeOptionsMenu.Instance.summarize = this.summary;
@@ -285,19 +292,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
             MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
             MarqueeOptionsMenu.Instance.pinWithView = this.pinWithView;
-            document.addEventListener("pointerdown", hideMarquee);
-            document.addEventListener("wheel", hideMarquee);
+            document.addEventListener('pointerdown', hideMarquee);
+            document.addEventListener('wheel', hideMarquee);
         } else {
             this.hideMarquee();
         }
         this.cleanupInteractions(true, this._commandExecuted);
 
         e.altKey && e.preventDefault();
-    }
+    };
 
     clearSelection() {
-        if (window.getSelection) { window.getSelection()?.removeAllRanges(); }
-        else if (document.getSelection()) { document.getSelection()?.empty(); }
+        if (window.getSelection) {
+            window.getSelection()?.removeAllRanges();
+        } else if (document.getSelection()) {
+            document.getSelection()?.empty();
+        }
     }
 
     setPreviewCursor = action((x: number, y: number, drag: boolean, hide: boolean) => {
@@ -312,9 +322,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             this._commandExecuted = false;
             PreviewCursor.Visible = false;
             this.cleanupInteractions(true);
-            document.addEventListener("pointermove", this.onPointerMove, true);
-            document.addEventListener("pointerup", this.onPointerUp, true);
-            document.addEventListener("keydown", this.marqueeCommand, true);
+            document.addEventListener('pointermove', this.onPointerMove, true);
+            document.addEventListener('pointerup', this.onPointerUp, true);
+            document.addEventListener('keydown', this.marqueeCommand, true);
         } else {
             this._downX = x;
             this._downY = y;
@@ -328,9 +338,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     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.ActiveTool === InkTool.None) {
+        if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+            if (Doc.ActiveTool === InkTool.None) {
                 if (!(e.nativeEvent as any).marqueeHit) {
                     (e.nativeEvent as any).marqueeHit = true;
                     if (!this.props.trySelectCluster(e.shiftKey)) {
@@ -339,17 +348,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 }
             }
             // let the DocumentView stopPropagation of this event when it selects this document
-        } else {  // why do we get a click event when the cursor have moved a big distance?
+        } else {
+            // why do we get a click event when the cursor have moved a big distance?
             // let's cut it off here so no one else has to deal with it.
             e.stopPropagation();
         }
-    }
+    };
 
     @action
-    showMarquee = () => { this._visible = true; }
+    showMarquee = () => {
+        this._visible = true;
+    };
 
     @action
-    hideMarquee = () => { this._visible = false; }
+    hideMarquee = () => {
+        this._visible = false;
+    };
 
     @undoBatch
     @action
@@ -361,16 +375,18 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.cleanupInteractions(false);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
-    }
+    };
 
     getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
-        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(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc));
-            doc._panX = doc._panY = 0;
-            return doc;
-        })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+        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(Doc.MyFileOrphans, undefined, Doc.GetProto(doc));
+                  doc._panX = doc._panY = 0;
+                  return doc;
+              })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
         newCollection.system = undefined;
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
@@ -378,7 +394,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.forceActive = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
-        selected.forEach(d => d.context = newCollection);
+        selected.forEach(d => (d.context = newCollection));
         this.hideMarquee();
         return newCollection;
     });
@@ -393,74 +409,76 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
-    }
-
-	/**
-	 * This triggers the TabDocView.PinDoc method which is the universal method
-	 * used to pin documents to the currently active presentation trail. 
-	 * 
-	 * This one is unique in that it includes the bounds associated with marquee view. 
-	 */
+    };
+
+    /**
+     * This triggers the TabDocView.PinDoc method which is the universal method
+     * used to pin documents to the currently active presentation trail.
+     *
+     * This one is unique in that it includes the bounds associated with marquee view.
+     */
     @undoBatch
     @action
     pinWithView = async () => {
         const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
-		const doc = this.props.Document;
-		const viewOptions:PinViewProps = {
-			bounds: this.Bounds,
-			scale: scale
-		};
-		TabDocView.PinDoc(doc, {pinWithView: viewOptions});
-		MarqueeOptionsMenu.Instance.fadeOut(true);
+        const doc = this.props.Document;
+        const viewOptions: PinViewProps = {
+            bounds: this.Bounds,
+            scale: scale,
+        };
+        TabDocView.PinDoc(doc, { pinWithView: viewOptions });
+        MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
-    }
+    };
 
     @undoBatch
     @action
     collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => {
         const selected = this.marqueeSelect(false);
-        if (e instanceof KeyboardEvent ? "cg".includes(e.key) : true) {
-            selected.map(action(d => {
-                const dx = NumCast(d.x);
-                const dy = NumCast(d.y);
-                delete d.x;
-                delete d.y;
-                delete d.activeFrame;
-                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                d.x = dx - this.Bounds.left - this.Bounds.width / 2;
-                d.y = dy - this.Bounds.top - this.Bounds.height / 2;
-                return d;
-            }));
+        if (e instanceof KeyboardEvent ? 'cg'.includes(e.key) : true) {
+            selected.map(
+                action(d => {
+                    const dx = NumCast(d.x);
+                    const dy = NumCast(d.y);
+                    delete d.x;
+                    delete d.y;
+                    delete d.activeFrame;
+                    delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                    delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                    d.x = dx - this.Bounds.left - this.Bounds.width / 2;
+                    d.y = dy - this.Bounds.top - this.Bounds.height / 2;
+                    return d;
+                })
+            );
             this.props.removeDocument?.(selected);
         }
         // TODO: nda - this is the code to actually get a new grouped collection
-        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
+        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === 't' ? Docs.Create.StackingDocument : undefined, group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
-    }
+    };
 
     @undoBatch
     @action
     syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => {
         const selected = this.marqueeSelect(false);
-        if (e instanceof KeyboardEvent ? e.key === "i" : true) {
+        if (e instanceof KeyboardEvent ? e.key === 'i' : true) {
             const inks = selected.filter(s => s.type === DocumentType.INK);
             const setDocs = selected.filter(s => s.type === DocumentType.RTF && s.color);
-            const sets = setDocs.map((sd) => Cast(sd.data, RichTextField)?.Text as string);
+            const sets = setDocs.map(sd => Cast(sd.data, RichTextField)?.Text as string);
             const colors = setDocs.map(sd => FieldValue(sd.color) as string);
             const wordToColor = new Map<string, string>();
-            sets.forEach((st: string, i: number) => st.split(",").forEach(word => wordToColor.set(word, colors[i])));
+            sets.forEach((st: string, i: number) => st.split(',').forEach(word => wordToColor.set(word, colors[i])));
             const strokes: InkData[] = [];
             inks.filter(i => Cast(i.data, InkField)).forEach(i => {
                 const d = Cast(i.data, InkField, null);
-                const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
-                const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+                const left = Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
+                const top = Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
                 strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top })));
             });
-            CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
+            CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {
                 // const wordResults = results.filter((r: any) => r.category === "inkWord");
                 // for (const word of wordResults) {
                 //     const indices: number[] = word.strokeIds;
@@ -501,12 +519,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 //         }
                 //     });
                 // }
-                const lines = results.filter((r: any) => r.category === "line");
-                const text = lines.map((l: any) => l.recognizedText).join("\r\n");
+                const lines = results.filter((r: any) => r.category === 'line');
+                const text = lines.map((l: any) => l.recognizedText).join('\r\n');
                 this.props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
             });
         }
-    }
+    };
 
     @undoBatch
     @action
@@ -517,15 +535,15 @@ 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, _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", "");
+        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', '');
 
         portal.hidden = true;
         this.props.addDocument?.(portal);
         this.props.addLiveTextDocument(summary);
         MarqueeOptionsMenu.Instance.fadeOut(true);
-    }
+    };
 
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
@@ -534,33 +552,33 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
         setTimeout(() => this.props.selectDocuments([newCollection]));
-    }
+    };
 
     @undoBatch
     marqueeCommand = action((e: KeyboardEvent) => {
         if (this._commandExecuted || (e as any).propagationIsStopped) {
             return;
         }
-        if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") {
+        if (e.key === 'Backspace' || e.key === 'Delete' || e.key === 'd') {
             this._commandExecuted = true;
             e.stopPropagation();
             (e as any).propagationIsStopped = true;
             this.delete();
             e.stopPropagation();
         }
-        if ("cbtsSpg".indexOf(e.key) !== -1) {
+        if ('cbtsSpg'.indexOf(e.key) !== -1) {
             this._commandExecuted = true;
             e.stopPropagation();
             e.preventDefault();
             (e as any).propagationIsStopped = true;
-            if (e.key === "g") this.collection(e, true);
-            if (e.key === "c" || e.key === "t") this.collection(e);
-            if (e.key === "s" || e.key === "S") this.summary(e);
-            if (e.key === "b") this.background(e);
-            if (e.key === "p") this.pileup(e);
+            if (e.key === 'g') this.collection(e, true);
+            if (e.key === 'c' || e.key === 't') this.collection(e);
+            if (e.key === 's' || e.key === 'S') this.summary(e);
+            if (e.key === 'b') this.background(e);
+            if (e.key === 'p') this.pileup(e);
             this.cleanupInteractions(false);
         }
-        if (e.key === "r" || e.key === " ") {
+        if (e.key === 'r' || e.key === ' ') {
             this._commandExecuted = true;
             e.stopPropagation();
             e.preventDefault();
@@ -568,18 +586,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         }
     });
 
-    touchesLine(r1: { left: number, top: number, width: number, height: number }) {
+    touchesLine(r1: { left: number; top: number; width: number; height: number }) {
         for (const lassoPt of this._lassoPts) {
             const topLeft = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
-            if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width &&
-                r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
+            if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width && r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
                 return true;
             }
         }
         return false;
     }
 
-    boundingShape(r1: { left: number, top: number, width: number, height: number }) {
+    boundingShape(r1: { left: number; top: number; width: number; height: number }) {
         const xs = this._lassoPts.map(pair => pair[0]);
         const ys = this._lassoPts.map(pair => pair[1]);
         const tl = this.Transform.transformPoint(Math.min(...xs), Math.min(...ys));
@@ -592,10 +609,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             let hasRight = false;
             for (const lassoPt of this._lassoPts) {
                 const truePoint = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
-                hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
-                hasTop = hasTop || (truePoint[1] > tl[1] && truePoint[1] < r1.top) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
-                hasRight = hasRight || (truePoint[0] < br[0] && truePoint[0] > r1.left + r1.width) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
-                hasBottom = hasBottom || (truePoint[1] < br[1] && truePoint[1] > r1.top + r1.height) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
+                hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
+                hasTop = hasTop || (truePoint[1] > tl[1] && truePoint[1] < r1.top && truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
+                hasRight = hasRight || (truePoint[0] < br[0] && truePoint[0] > r1.left + r1.width && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
+                hasBottom = hasBottom || (truePoint[1] < br[1] && truePoint[1] > r1.top + r1.height && truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
                 if (hasTop && hasLeft && hasBottom && hasRight) {
                     return true;
                 }
@@ -615,9 +632,20 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 (this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
             }
         };
-        this.props.activeDocuments().filter(doc => !doc.z && !doc._lockedPosition).map(selectFunc);
-        if (!selection.length && selectBackgrounds) this.props.activeDocuments().filter(doc => doc.z === undefined).map(selectFunc);
-        if (!selection.length) this.props.activeDocuments().filter(doc => doc.z !== undefined).map(selectFunc);
+        this.props
+            .activeDocuments()
+            .filter(doc => !doc.z && !doc._lockedPosition)
+            .map(selectFunc);
+        if (!selection.length && selectBackgrounds)
+            this.props
+                .activeDocuments()
+                .filter(doc => doc.z === undefined)
+                .map(selectFunc);
+        if (!selection.length)
+            this.props
+                .activeDocuments()
+                .filter(doc => doc.z !== undefined)
+                .map(selectFunc);
         return selection;
     }
 
@@ -625,31 +653,42 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const cpt = this._lassoFreehand || !this._visible ? [0, 0] : [this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY];
         const p = this.props.getContainerTransform().transformPoint(cpt[0], cpt[1]);
         const v = this._lassoFreehand ? [0, 0] : this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-        return <div className="marquee" style={{
-            transform: `translate(${p[0]}px, ${p[1]}px)`,
-            width: Math.abs(v[0]),
-            height: Math.abs(v[1]),
-            zIndex: 2000
-        }}> {this._lassoFreehand ?
-            <svg height={2000} width={2000}>
-                <polyline points={this._lassoPts.reduce((s, pt) => s + pt[0] + "," + pt[1] + " ", "")} fill="none" stroke="black" strokeWidth="1" strokeDasharray="3" />
-            </svg>
-            :
-            <span className="marquee-legend" />}
-        </div>;
+        return (
+            <div
+                className="marquee"
+                style={{
+                    transform: `translate(${p[0]}px, ${p[1]}px)`,
+                    width: Math.abs(v[0]),
+                    height: Math.abs(v[1]),
+                    zIndex: 2000,
+                }}>
+                {' '}
+                {this._lassoFreehand ? (
+                    <svg height={2000} width={2000}>
+                        <polyline points={this._lassoPts.reduce((s, pt) => s + pt[0] + ',' + pt[1] + ' ', '')} fill="none" stroke="black" strokeWidth="1" strokeDasharray="3" />
+                    </svg>
+                ) : (
+                    <span className="marquee-legend" />
+                )}
+            </div>
+        );
     }
 
     render() {
-        return <div className="marqueeView"
-            style={{
-                overflow: StrCast(this.props.Document._overflow),
-                cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
-            }}
-
-            onDragOver={e => e.preventDefault()}
-            onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
-            {this._visible ? this.marqueeDiv : null}
-            {this.props.children}
-        </div>;
+        return (
+            <div
+                className="marqueeView"
+                style={{
+                    overflow: StrCast(this.props.Document._overflow),
+                    cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible || PresBox.startMarquee ? 'crosshair' : 'pointer',
+                }}
+                onDragOver={e => e.preventDefault()}
+                onScroll={e => (e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0)}
+                onClick={this.onClick}
+                onPointerDown={this.onPointerDown}>
+                {this._visible ? this.marqueeDiv : null}
+                {this.props.children}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 8adfdc70b..0d7d67dd8 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -7,20 +7,17 @@ import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
-import { DocUtils } from '../../../documents/Documents';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { Colors, Shadows } from '../../global/globalEnums';
-import { AudioBox } from '../../nodes/AudioBox';
 import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
 import { DocumentView } from '../../nodes/DocumentView';
 import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
 import { StyleProp } from '../../StyleProvider';
 import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
 import { CollectionSubView } from '../CollectionSubView';
-import { CollectionViewType } from '../CollectionView';
 import './CollectionLinearView.scss';
 
 /**
@@ -228,7 +225,7 @@ export class CollectionLinearView extends CollectionSubView() {
                         }}>
                         {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
                     </div>
-                    {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : (
+                    {!DocumentLinksButton.StartLink || this.layoutDoc !== Doc.MyDockedBtns ? null : (
                         <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
                             <span className="bottomPopup-text">
                                 Creating link from:{' '}
@@ -263,7 +260,7 @@ export class CollectionLinearView extends CollectionSubView() {
                             </Tooltip>
                         </span>
                     )}
-                    {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : (
+                    {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== Doc.MyDockedBtns ? null : (
                         <span className="bottomPopup-background">
                             <span className="bottomPopup-text">
                                 Currently playing:
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 1e7f4f10b..ed856a4ab 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -11,6 +11,7 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
 import { Hypothesis } from '../../util/HypothesisUtils';
+import { LinkFollower } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { undoBatch } from '../../util/UndoManager';
 import { DocumentView } from '../nodes/DocumentView';
@@ -118,7 +119,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                             : undefined;
 
                     if (focusDoc) this.props.docView.ComponentView?.scrollFocus?.(focusDoc, true);
-                    LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
+                    LinkFollower.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
                 }
             }
         );
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index a6f6bd35f..0bcb68f82 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -1,17 +1,16 @@
 import { action, observable } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import { EditorView } from 'prosemirror-view';
+import { Doc } from '../../../fields/Doc';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { SearchBox } from '../search/SearchBox';
 import { DefaultStyleProvider } from '../StyleProvider';
 import './LinkPopup.scss';
-import React = require("react");
-import { Doc, Opt } from '../../../fields/Doc';
+import React = require('react');
 
 interface LinkPopupProps {
     showPopup: boolean;
@@ -23,33 +22,30 @@ interface LinkPopupProps {
 }
 
 /**
- * Popup component for creating links from text to Dash documents 
+ * Popup component for creating links from text to Dash documents
  */
 
 @observer
 export class LinkPopup extends React.Component<LinkPopupProps> {
-    @observable private linkURL: string = "";
+    @observable private linkURL: string = '';
     @observable public view?: EditorView;
 
-
-
     // TODO: should check for valid URL
     @undoBatch
     makeLinkToURL = (target: string, lcoation: string) => {
-        ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target);
-    }
+        ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, 'onRadd:rightight', target, target);
+    };
 
     @action
     onLinkChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.linkURL = e.target.value;
-    }
-
+    };
 
     getPWidth = () => 500;
     getPHeight = () => 500;
 
     render() {
-        const popupVisibility = this.props.showPopup ? "block" : "none";
+        const popupVisibility = this.props.showPopup ? 'block' : 'none';
         const linkDoc = this.props.linkFrom ? this.props.linkFrom : undefined;
         return (
             <div className="linkPopup-container" style={{ display: popupVisibility }}>
@@ -68,8 +64,8 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         className="linkPopup-searchBox searchBox-input" /> */}
 
                     <SearchBox
-                        Document={CurrentUserUtils.MySearcher}
-                        DataDoc={CurrentUserUtils.MySearcher}
+                        Document={Doc.MySearcher}
+                        DataDoc={Doc.MySearcher}
                         linkFrom={linkDoc}
                         linkSearch={true}
                         fieldKey="data"
@@ -96,9 +92,10 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         docRangeFilters={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
                         ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined} />
+                        ContainingCollectionDoc={undefined}
+                    />
                 </div>
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index c42c2306a..8437736ae 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -1,31 +1,29 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { DateField } from "../../../fields/DateField";
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, DateCast, NumCast } from "../../../fields/Types";
-import { AudioField, nullAudio } from "../../../fields/URLField";
-import { emptyFunction, formatTime, OmitKeys, returnFalse, setupMoveUpEvents } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { Networking } from "../../Network";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DragManager } from "../../util/DragManager";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionStackedTimeline, TrimScope } from "../collections/CollectionStackedTimeline";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
-import "./AudioBox.scss";
-import { FieldView, FieldViewProps } from "./FieldView";
-
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { DateField } from '../../../fields/DateField';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { ComputedField } from '../../../fields/ScriptField';
+import { Cast, DateCast, NumCast } from '../../../fields/Types';
+import { AudioField, nullAudio } from '../../../fields/URLField';
+import { emptyFunction, formatTime, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { Networking } from '../../Network';
+import { DragManager } from '../../util/DragManager';
+import { undoBatch } from '../../util/UndoManager';
+import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import './AudioBox.scss';
+import { FieldView, FieldViewProps } from './FieldView';
 
 /**
  * AudioBox
  * Main component: AudioBox.tsx
  * Supporting Components: CollectionStackedTimeline, AudioWaveform
- * 
+ *
  * AudioBox is a node that supports the recording and playback of audio files in Dash.
  * When an audio file is importeed into Dash, it is immediately rendered as an AudioBox document.
  * When a blank AudioBox node is created in Dash, audio recording controls are displayed and the user can start a recording which can be paused or stopped, and can use dictation to create a text transcript.
@@ -34,24 +32,23 @@ import { FieldView, FieldViewProps } from "./FieldView";
  * User can trim audio: nondestructive, just sets new bounds for playback and rendering timelin
  */
 
-
 // used as a wrapper class for MediaStream from MediaDevices API
 declare class MediaRecorder {
     constructor(e: any); // whatever MediaRecorder has
 }
 
 enum media_state {
-    PendingRecording = "pendingRecording",
-    Recording = "recording",
-    Paused = "paused",
-    Playing = "playing"
+    PendingRecording = 'pendingRecording',
+    Recording = 'recording',
+    Paused = 'paused',
+    Playing = 'playing',
 }
 
-
 @observer
 export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(AudioBox, fieldKey);
+    }
     public static Enabled = false;
 
     static topControlsHeight = 30; // height of upper controls above timeline
@@ -73,27 +70,41 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @observable _muted: boolean = false;
     @observable _paused: boolean = false; // is recording paused
     // @observable rawDuration: number = 0; // computed from the length of the audio element when loaded
-    @computed get recordingStart() { return DateCast(this.dataDoc[this.fieldKey + "-recordingStart"])?.date.getTime(); }
-    @computed get rawDuration() { return NumCast(this.dataDoc[`${this.fieldKey}-duration`]); } // bcz: shouldn't be needed since it's computed from audio element
+    @computed get recordingStart() {
+        return DateCast(this.dataDoc[this.fieldKey + '-recordingStart'])?.date.getTime();
+    }
+    @computed get rawDuration() {
+        return NumCast(this.dataDoc[`${this.fieldKey}-duration`]);
+    } // bcz: shouldn't be needed since it's computed from audio element
     // mehek: not 100% sure but i think due to the order in which things are loaded this is necessary ^^
     // if you get rid of it and set the value to 0 the timeline and waveform will set their bounds incorrectly
 
-    @computed get miniPlayer() { return this.props.PanelHeight() < 50; } // used to collapse timeline when node is shrunk
-    @computed get links() { return DocListCast(this.dataDoc.links); }
-    @computed get mediaState() { return this.dataDoc.mediaState as media_state; }
-    @computed get path() { // returns the path of the audio file
-        const path = Cast(this.props.Document[this.fieldKey], AudioField, null)?.url.href || "";
-        return path === nullAudio ? "" : path;
+    @computed get miniPlayer() {
+        return this.props.PanelHeight() < 50;
+    } // used to collapse timeline when node is shrunk
+    @computed get links() {
+        return DocListCast(this.dataDoc.links);
+    }
+    @computed get mediaState() {
+        return this.dataDoc.mediaState as media_state;
+    }
+    @computed get path() {
+        // returns the path of the audio file
+        const path = Cast(this.props.Document[this.fieldKey], AudioField, null)?.url.href || '';
+        return path === nullAudio ? '' : path;
+    }
+    set mediaState(value) {
+        this.dataDoc.mediaState = value;
     }
-    set mediaState(value) { this.dataDoc.mediaState = value; }
-
-    @computed get timeline() { return this._stackedTimeline; } // returns CollectionStackedTimeline ref
 
+    @computed get timeline() {
+        return this._stackedTimeline;
+    } // returns CollectionStackedTimeline ref
 
     componentWillUnmount() {
         this.removeCurrentlyPlaying();
         this._dropDisposer?.();
-        Object.values(this._disposers).forEach((disposer) => disposer?.());
+        Object.values(this._disposers).forEach(disposer => disposer?.());
 
         this.mediaState === media_state.Recording && this.stopRecording();
     }
@@ -110,14 +121,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         }
     }
 
-
     getLinkData(l: Doc) {
         let la1 = l.anchor1 as Doc;
         let la2 = l.anchor2 as Doc;
-        const linkTime =
-            this.timeline?.anchorStart(la2) ||
-            this.timeline?.anchorStart(la1) ||
-            0;
+        const linkTime = this.timeline?.anchorStart(la2) || this.timeline?.anchorStart(la1) || 0;
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
             la1 = l.anchor2 as Doc;
             la2 = l.anchor1 as Doc;
@@ -126,20 +133,17 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     getAnchor = () => {
-        return CollectionStackedTimeline.createAnchor(
-            this.rootDoc,
-            this.dataDoc,
-            this.annotationKey,
-            "_timecodeToShow" /* audioStart */,
-            "_timecodeToHide" /* audioEnd */,
-            this._ele?.currentTime ||
-            Cast(this.props.Document._currentTimecode, "number", null) ||
-            (this.mediaState === media_state.Recording
-                ? (Date.now() - (this.recordingStart || 0)) / 1000
-                : undefined)
-        ) || this.rootDoc;
-    }
-
+        return (
+            CollectionStackedTimeline.createAnchor(
+                this.rootDoc,
+                this.dataDoc,
+                this.annotationKey,
+                '_timecodeToShow' /* audioStart */,
+                '_timecodeToHide' /* audioEnd */,
+                this._ele?.currentTime || Cast(this.props.Document._currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined)
+            ) || this.rootDoc
+        );
+    };
 
     // updates timecode and shows it in timeline, follows links at time
     @action
@@ -148,24 +152,23 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this.links
                 .map(l => this.getLinkData(l))
                 .forEach(({ la1, la2, linkTime }) => {
-                    if (linkTime > NumCast(this.layoutDoc._currentTimecode) &&
-                        linkTime < this._ele!.currentTime) {
+                    if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < this._ele!.currentTime) {
                         Doc.linkFollowHighlight(la1);
                     }
                 });
             this.layoutDoc._currentTimecode = this._ele.currentTime;
             this.timeline?.scrollToTime(NumCast(this.layoutDoc._currentTimecode));
         }
-    }
+    };
 
     // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range
     @action
     playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => {
         clearTimeout(this._play); // abort any previous clip ending
-        if (Number.isNaN(this._ele?.duration)) { // audio element isn't loaded yet... wait 1/2 second and try again
+        if (Number.isNaN(this._ele?.duration)) {
+            // audio element isn't loaded yet... wait 1/2 second and try again
             setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
-        }
-        else if (this.timeline && this._ele && AudioBox.Enabled) {
+        } else if (this.timeline && this._ele && AudioBox.Enabled) {
             // trimBounds override requested playback bounds
             const end = Math.min(this.timeline.trimEnd, endTime ?? this.timeline.trimEnd);
             const start = Math.max(this.timeline.trimStart, seekTimeInSeconds);
@@ -175,21 +178,18 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this._ele.play();
                 this.mediaState = media_state.Playing;
                 this.addCurrentlyPlaying();
-                this._play = setTimeout(
-                    () => {
-                        // need to keep track of if end of clip is reached so on next play, clip restarts
-                        if (fullPlay) this._finished = true;
-                        // removes from currently playing if playback has reached end of range marker
-                        else this.removeCurrentlyPlaying();
-                        this.Pause();
-                    },
-                    (end - start) * 1000);
+                this._play = setTimeout(() => {
+                    // need to keep track of if end of clip is reached so on next play, clip restarts
+                    if (fullPlay) this._finished = true;
+                    // removes from currently playing if playback has reached end of range marker
+                    else this.removeCurrentlyPlaying();
+                    this.Pause();
+                }, (end - start) * 1000);
             } else {
                 this.Pause();
             }
         }
-    }
-
+    };
 
     // removes from currently playing display
     @action
@@ -198,7 +198,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
             index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
         }
-    }
+    };
 
     // adds doc to currently playing display
     @action
@@ -209,8 +209,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
             CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
         }
-    }
-
+    };
 
     // update the recording time
     updateRecordTime = () => {
@@ -220,13 +219,13 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this._pausedTime) / 1000;
             }
         }
-    }
+    };
 
     // starts recording
     recordAudioAnnotation = async () => {
         this._stream = await navigator.mediaDevices.getUserMedia({ audio: true });
         this._recorder = new MediaRecorder(this._stream);
-        this.dataDoc[this.fieldKey + "-recordingStart"] = new DateField();
+        this.dataDoc[this.fieldKey + '-recordingStart'] = new DateField();
         DocUtils.ActiveRecordings.push(this);
         this._recorder.ondataavailable = async (e: any) => {
             const [{ result }] = await Networking.UploadFilesToServer(e.data);
@@ -235,11 +234,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             }
         };
         this._recordStart = new Date().getTime();
-        runInAction(() => this.mediaState = media_state.Recording);
+        runInAction(() => (this.mediaState = media_state.Recording));
         setTimeout(this.updateRecordTime);
         this._recorder.start();
         setTimeout(this.stopRecording, 60 * 60 * 1000); // stop after an hour
-    }
+    };
 
     // stops recording
     @action
@@ -249,52 +248,59 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._recorder = undefined;
             const now = new Date().getTime();
             this._paused && (this._pausedTime += now - this._pauseStart);
-            this.dataDoc[this.fieldKey + "-duration"] = (now - this._recordStart - this._pausedTime) / 1000;
+            this.dataDoc[this.fieldKey + '-duration'] = (now - this._recordStart - this._pausedTime) / 1000;
             this.mediaState = media_state.Paused;
             this._stream?.getAudioTracks()[0].stop();
             const ind = DocUtils.ActiveRecordings.indexOf(this);
             ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1);
         }
-    }
-
+    };
 
     // context menu
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
         funcs.push({
-            description: (this.layoutDoc.hideAnchors ? "Don't hide" : "Hide") + " anchors",
-            event: e => this.layoutDoc.hideAnchors = !this.layoutDoc.hideAnchors,
-            icon: "expand-arrows-alt",
+            description: (this.layoutDoc.hideAnchors ? "Don't hide" : 'Hide') + ' anchors',
+            event: e => (this.layoutDoc.hideAnchors = !this.layoutDoc.hideAnchors),
+            icon: 'expand-arrows-alt',
         });
         funcs.push({
-            description: (this.layoutDoc.dontAutoFollowLinks ? "" : "Don't") + " follow links when encountered",
-            event: e => this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks,
-            icon: "expand-arrows-alt",
+            description: (this.layoutDoc.dontAutoFollowLinks ? '' : "Don't") + ' follow links when encountered',
+            event: e => (this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks),
+            icon: 'expand-arrows-alt',
         });
         funcs.push({
-            description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected",
-            event: e => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks,
-            icon: "expand-arrows-alt",
+            description: (this.layoutDoc.dontAutoPlayFollowedLinks ? '' : "Don't") + ' play when link is selected',
+            event: e => (this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks),
+            icon: 'expand-arrows-alt',
         });
         funcs.push({
-            description: (this.layoutDoc.autoPlayAnchors ? "Don't auto" : "Auto") + " play anchors onClick",
-            event: e => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors,
-            icon: "expand-arrows-alt",
+            description: (this.layoutDoc.autoPlayAnchors ? "Don't auto" : 'Auto') + ' play anchors onClick',
+            event: e => (this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors),
+            icon: 'expand-arrows-alt',
         });
         ContextMenu.Instance?.addItem({
-            description: "Options...",
+            description: 'Options...',
             subitems: funcs,
-            icon: "asterisk",
+            icon: 'asterisk',
         });
-    }
-
+    };
 
     // button for starting and stopping the recording
     Record = (e: React.PointerEvent) => {
-        e.button === 0 && !e.ctrlKey && setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            this._recorder ? this.stopRecording() : this.recordAudioAnnotation();
-        }), false);
-    }
+        e.button === 0 &&
+            !e.ctrlKey &&
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                returnFalse,
+                action(() => {
+                    this._recorder ? this.stopRecording() : this.recordAudioAnnotation();
+                }),
+                false
+            );
+    };
 
     // for play button
     Play = (e?: any) => {
@@ -314,7 +320,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
             this.playFrom(start, this.timeline.trimEnd, true);
         }
-    }
+    };
 
     // pause play back
     @action
@@ -327,60 +333,73 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             if (!this._finished) clearTimeout(this._play);
             this.removeCurrentlyPlaying();
         }
-    }
+    };
 
     // for dictation button, creates a text document for dictation
     onFile = (e: any) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            const newDoc = CurrentUserUtils.GetNewTextDoc(
-                "",
-                NumCast(this.rootDoc.x),
-                NumCast(this.rootDoc.y) +
-                NumCast(this.layoutDoc._height) +
-                10,
-                NumCast(this.layoutDoc._width),
-                2 * NumCast(this.layoutDoc._height)
-            );
-            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");
-            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(CurrentUserUtils.MyOverlayDocs, undefined, newDoc);
-            } else {
-                this.props.addDocument?.(newDoc);
-            }
-        }), false);
-    }
-
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            returnFalse,
+            action(() => {
+                const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+                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');
+                if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc)) {
+                    newDoc.x = this.rootDoc.x;
+                    newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
+                    Doc.AddDocToList(Doc.MyOverlayDocs, undefined, newDoc);
+                } else {
+                    this.props.addDocument?.(newDoc);
+                }
+            }),
+            false
+        );
+    };
 
     // sets <audio> ref for updating time
     setRef = (e: HTMLAudioElement | null) => {
-        e?.addEventListener("timeupdate", this.timecodeChanged);
-        e?.addEventListener("ended", () => { this._finished = true; this.Pause(); });
+        e?.addEventListener('timeupdate', this.timecodeChanged);
+        e?.addEventListener('ended', () => {
+            this._finished = true;
+            this.Pause();
+        });
         this._ele = e;
-    }
-
+    };
 
     // pause the time during recording phase
     recordPause = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            this._pauseStart = new Date().getTime();
-            this._paused = true;
-            this._recorder.pause();
-        }), false);
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            returnFalse,
+            action(() => {
+                this._pauseStart = new Date().getTime();
+                this._paused = true;
+                this._recorder.pause();
+            }),
+            false
+        );
+    };
 
     // continue the recording
     recordPlay = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            this._paused = false;
-            this._pausedTime += new Date().getTime() - this._pauseStart;
-            this._recorder.resume();
-        }), false);
-    }
-
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            returnFalse,
+            action(() => {
+                this._paused = false;
+                this._pausedTime += new Date().getTime() - this._pauseStart;
+                this._recorder.resume();
+            }),
+            false
+        );
+    };
 
     // plays link
     playLink = (link: Doc) => {
@@ -392,8 +411,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             }
         } else {
             this.links
-                .filter((l) => l.anchor1 === link || l.anchor2 === link)
-                .forEach((l) => {
+                .filter(l => l.anchor1 === link || l.anchor2 === link)
+                .forEach(l => {
                     const { la1, la2 } = this.getLinkData(l);
                     const startTime = this.timeline?.anchorStart(la1) || this.timeline?.anchorStart(la2);
                     const endTime = this.timeline?.anchorEnd(la1) || this.timeline?.anchorEnd(la2);
@@ -406,17 +425,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     }
                 });
         }
-    }
-
+    };
 
     @action
-    timelineWhenChildContentsActiveChanged = (isActive: boolean) =>
-        this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)
+    timelineWhenChildContentsActiveChanged = (isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive));
 
-    timelineScreenToLocal = () =>
-        this.props.ScreenToLocalTransform().translate(0, -AudioBox.bottomControlsHeight)
+    timelineScreenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -AudioBox.bottomControlsHeight);
 
-    setPlayheadTime = (time: number) => this._ele!.currentTime = this.layoutDoc._currentTimecode = time;
+    setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._currentTimecode = time);
 
     playing = () => this.mediaState === media_state.Playing;
 
@@ -424,7 +440,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // timeline dimensions
     timelineWidth = () => this.props.PanelWidth();
-    timelineHeight = () => (this.props.PanelHeight() - (AudioBox.topControlsHeight + AudioBox.bottomControlsHeight));
+    timelineHeight = () => this.props.PanelHeight() - (AudioBox.topControlsHeight + AudioBox.bottomControlsHeight);
 
     // ends trim, hides trim controls and displays new clip
     @undoBatch
@@ -432,32 +448,38 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.Pause();
         this.setPlayheadTime(Math.max(Math.min(this.timeline?.trimEnd || 0, this._ele!.currentTime), this.timeline?.trimStart || 0));
         this.timeline?.StopTrimming();
-    }
+    };
 
     // displays trim controls to start trimming clip
     startTrim = (scope: TrimScope) => {
         this.Pause();
         this.timeline?.StartTrimming(scope);
-    }
+    };
 
     // for trim button, double click displays full clip, single displays curr trim bounds
     onClipPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
-        this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
-            if (doubleTap) {
-                this.startTrim(TrimScope.All);
-            } else if (this.timeline) {
-                this.Pause();
-                this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
-            }
-        }));
-    }
-
+        this.timeline &&
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                returnFalse,
+                action((e: PointerEvent, doubleTap?: boolean) => {
+                    if (doubleTap) {
+                        this.startTrim(TrimScope.All);
+                    } else if (this.timeline) {
+                        this.Pause();
+                        this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
+                    }
+                })
+            );
+    };
 
     // for zoom slider, sets timeline waveform zoom
     zoom = (zoom: number) => {
         this.timeline?.setZoom(zoom);
-    }
+    };
 
     // for volume slider sets volume
     @action
@@ -469,7 +491,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.toggleMute();
             }
         }
-    }
+    };
 
     // toggles audio muted
     @action
@@ -478,135 +500,156 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._muted = !this._muted;
             this._ele.muted = this._muted;
         }
-    }
-
+    };
 
     setupTimelineDrop = (r: HTMLDivElement | null) => {
         if (r && this.timeline) {
             this._dropDisposer?.();
-            this._dropDisposer = DragManager.MakeDropTarget(r,
+            this._dropDisposer = DragManager.MakeDropTarget(
+                r,
                 (e, de) => {
                     const [xp, yp] = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
                     de.complete.docDragData && this.timeline.internalDocDrop(e, de, de.complete.docDragData, xp);
                 },
-                this.layoutDoc, undefined);
+                this.layoutDoc,
+                undefined
+            );
         }
-    }
-
+    };
 
     // UI for recording, initially displayed when new audio created in Dash
     @computed get recordingControls() {
-        return <div className="audiobox-recorder">
-            <div className="audiobox-dictation" onPointerDown={this.onFile}>
-                <FontAwesomeIcon
-                    size="2x"
-                    icon="file-alt" />
-            </div>
-            {[media_state.Recording, media_state.Playing].includes(this.mediaState) ?
-                <div className="recording-controls" onClick={e => e.stopPropagation()}>
-                    <div className="record-button" onPointerDown={this.Record}>
-                        <FontAwesomeIcon
-                            size="2x"
-                            icon="stop" />
-                    </div>
-                    <div className="record-button" onPointerDown={this._paused ? this.recordPlay : this.recordPause}>
-                        <FontAwesomeIcon
-                            size="2x"
-                            icon={this._paused ? "play" : "pause"} />
+        return (
+            <div className="audiobox-recorder">
+                <div className="audiobox-dictation" onPointerDown={this.onFile}>
+                    <FontAwesomeIcon size="2x" icon="file-alt" />
+                </div>
+                {[media_state.Recording, media_state.Playing].includes(this.mediaState) ? (
+                    <div className="recording-controls" onClick={e => e.stopPropagation()}>
+                        <div className="record-button" onPointerDown={this.Record}>
+                            <FontAwesomeIcon size="2x" icon="stop" />
+                        </div>
+                        <div className="record-button" onPointerDown={this._paused ? this.recordPlay : this.recordPause}>
+                            <FontAwesomeIcon size="2x" icon={this._paused ? 'play' : 'pause'} />
+                        </div>
+                        <div className="record-timecode">{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}</div>
                     </div>
-                    <div className="record-timecode">
-                        {formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
+                ) : (
+                    <div className="audiobox-start-record" onPointerDown={this.Record}>
+                        <FontAwesomeIcon icon="microphone" />
+                        RECORD
                     </div>
-                </div>
-                :
-                <div className="audiobox-start-record" onPointerDown={this.Record}>
-                    <FontAwesomeIcon icon="microphone" />
-                    RECORD
-                </div>}
-        </div>;
+                )}
+            </div>
+        );
     }
 
     // UI for playback, displayed for imported or recorded clips, hides timeline and collapses controls when node is shrunk vertically
     @computed get playbackControls() {
-        return <div className="audiobox-file" style={{
-            pointerEvents: this._isAnyChildContentActive || this.props.isContentActive() ? "all" : "none",
-            flexDirection: this.miniPlayer ? "row" : "column",
-            justifyContent: this.miniPlayer ? "flex-start" : "space-between"
-        }}>
-            <div className="audiobox-controls">
-                <div className="controls-left">
-                    <div className="audiobox-button"
-                        title={this.mediaState === media_state.Paused ? "play" : "pause"}
-                        onPointerDown={this.mediaState === media_state.Paused ? this.Play : (e) => { e.stopPropagation(); this.Pause(); }}>
-                        <FontAwesomeIcon icon={this.mediaState === media_state.Paused ? "play" : "pause"} size={"1x"} />
+        return (
+            <div
+                className="audiobox-file"
+                style={{
+                    pointerEvents: this._isAnyChildContentActive || this.props.isContentActive() ? 'all' : 'none',
+                    flexDirection: this.miniPlayer ? 'row' : 'column',
+                    justifyContent: this.miniPlayer ? 'flex-start' : 'space-between',
+                }}>
+                <div className="audiobox-controls">
+                    <div className="controls-left">
+                        <div
+                            className="audiobox-button"
+                            title={this.mediaState === media_state.Paused ? 'play' : 'pause'}
+                            onPointerDown={
+                                this.mediaState === media_state.Paused
+                                    ? this.Play
+                                    : e => {
+                                          e.stopPropagation();
+                                          this.Pause();
+                                      }
+                            }>
+                            <FontAwesomeIcon icon={this.mediaState === media_state.Paused ? 'play' : 'pause'} size={'1x'} />
+                        </div>
+
+                        {!this.miniPlayer && (
+                            <div className="audiobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish' : 'trim'} onPointerDown={this.onClipPointerDown}>
+                                <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} size={'1x'} />
+                            </div>
+                        )}
                     </div>
-
-                    {!this.miniPlayer &&
-                        <div className="audiobox-button"
-                            title={this.timeline?.IsTrimming !== TrimScope.None ? "finish" : "trim"}
-                            onPointerDown={this.onClipPointerDown}>
-                            <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} size={"1x"} />
-                        </div>}
-                </div>
-                <div className="controls-right">
-                    <div className="audiobox-button"
-                        title={this._muted ? "unmute" : "mute"}
-                        onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
-                        <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
+                    <div className="controls-right">
+                        <div
+                            className="audiobox-button"
+                            title={this._muted ? 'unmute' : 'mute'}
+                            onPointerDown={e => {
+                                e.stopPropagation();
+                                this.toggleMute();
+                            }}>
+                            <FontAwesomeIcon icon={this._muted ? 'volume-mute' : 'volume-up'} />
+                        </div>
+                        <input
+                            type="range"
+                            step="0.1"
+                            min="0"
+                            max="1"
+                            value={this._muted ? 0 : this._volume}
+                            className="toolbar-slider volume"
+                            onPointerDown={(e: React.PointerEvent) => {
+                                e.stopPropagation();
+                            }}
+                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
+                        />
                     </div>
-                    <input type="range" step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
-                        className="toolbar-slider volume"
-                        onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
-                    />
-                </div>
-            </div>
-
-            <div className="audiobox-playback" style={{ width: this.miniPlayer ? 0 : "100%" }}>
-                <div className="audiobox-timeline">
-                    {this.renderTimeline}
                 </div>
-            </div>
-
-            {this.audio}
 
-            <div className="audiobox-timecodes">
-                <div className="timecode-current">
-                    {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}
+                <div className="audiobox-playback" style={{ width: this.miniPlayer ? 0 : '100%' }}>
+                    <div className="audiobox-timeline">{this.renderTimeline}</div>
                 </div>
-                {this.miniPlayer ?
-                    <div>/</div>
-                    :
-                    <div className="bottom-controls-middle">
-                        <FontAwesomeIcon icon="search-plus" />
-                        <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
-                            className="toolbar-slider" id="zoom-slider"
-                            onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
-                        />
-                    </div>}
 
-                <div className="timecode-duration">
-                    {this.timeline && formatTime(Math.round(this.timeline.clipDuration))}
+                {this.audio}
+
+                <div className="audiobox-timecodes">
+                    <div className="timecode-current">{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}</div>
+                    {this.miniPlayer ? (
+                        <div>/</div>
+                    ) : (
+                        <div className="bottom-controls-middle">
+                            <FontAwesomeIcon icon="search-plus" />
+                            <input
+                                type="range"
+                                step="0.1"
+                                min="1"
+                                max="5"
+                                value={this.timeline?._zoomFactor}
+                                className="toolbar-slider"
+                                id="zoom-slider"
+                                onPointerDown={(e: React.PointerEvent) => {
+                                    e.stopPropagation();
+                                }}
+                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                                    this.zoom(Number(e.target.value));
+                                }}
+                            />
+                        </div>
+                    )}
+
+                    <div className="timecode-duration">{this.timeline && formatTime(Math.round(this.timeline.clipDuration))}</div>
                 </div>
             </div>
-
-
-        </div>;
+        );
     }
 
     // gets CollectionStackedTimeline
     @computed get renderTimeline() {
         return (
             <CollectionStackedTimeline
-                ref={action((r: any) => this._stackedTimeline = r)}
-                {...OmitKeys(this.props, ["CollectionFreeFormDocumentView"]).omit}
+                ref={action((r: any) => (this._stackedTimeline = r))}
+                {...OmitKeys(this.props, ['CollectionFreeFormDocumentView']).omit}
                 fieldKey={this.annotationKey}
-                dictationKey={this.fieldKey + "-dictation"}
+                dictationKey={this.fieldKey + '-dictation'}
                 mediaPath={this.path}
                 renderDepth={this.props.renderDepth + 1}
-                startTag={"_timecodeToShow" /* audioStart */}
-                endTag={"_timecodeToHide" /* audioEnd */}
+                startTag={'_timecodeToShow' /* audioStart */}
+                endTag={'_timecodeToHide' /* audioEnd */}
                 bringToFront={emptyFunction}
                 CollectionView={undefined}
                 playFrom={this.playFrom}
@@ -631,26 +674,22 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // returns the html audio element
     @computed get audio() {
-        return <audio ref={this.setRef}
-            className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}
-            onLoadedData={action(e =>
-                (this._ele?.duration && this._ele?.duration !== Infinity) &&
-                (this.dataDoc[this.fieldKey + "-duration"] = this._ele.duration)
-            )}
-        >
-            <source src={this.path} type="audio/mpeg" />
-            Not supported.
-        </audio>;
+        return (
+            <audio
+                ref={this.setRef}
+                className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}
+                onLoadedData={action(e => this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '-duration'] = this._ele.duration))}>
+                <source src={this.path} type="audio/mpeg" />
+                Not supported.
+            </audio>
+        );
     }
 
     render() {
-        return <div
-            ref={this.setupTimelineDrop}
-            className="audiobox-container"
-            onContextMenu={this.specificContextMenu}
-            style={{ pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined }}
-        >
-            {!this.path ? this.recordingControls : this.playbackControls}
-        </div>;
+        return (
+            <div ref={this.setupTimelineDrop} className="audiobox-container" onContextMenu={this.specificContextMenu} style={{ pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined }}>
+                {!this.path ? this.recordingControls : this.playbackControls}
+            </div>
+        );
     }
 }
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 6d05fca5e..c229a966a 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -1,23 +1,24 @@
-import React = require("react");
-import { action } from "mobx";
-import { observer } from "mobx-react";
+import React = require('react');
+import { action } from 'mobx';
+import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
-import { InkTool } from "../../../fields/InkField";
-import { StrCast } from "../../../fields/Types";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { SelectionManager } from "../../util/SelectionManager";
-import { undoBatch } from "../../util/UndoManager";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
-import "./ColorBox.scss";
+import { InkTool } from '../../../fields/InkField';
+import { StrCast } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { SelectionManager } from '../../util/SelectionManager';
+import { undoBatch } from '../../util/UndoManager';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
+import './ColorBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
-import { RichTextMenu } from "./formattedText/RichTextMenu";
+import { RichTextMenu } from './formattedText/RichTextMenu';
 
 @observer
 export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(ColorBox, fieldKey);
+    }
 
     @undoBatch
     @action
@@ -25,17 +26,22 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
         SetActiveInkColor(color.hex);
 
         SelectionManager.Views().map(view => {
-            const targetDoc = view.props.Document.dragFactory instanceof Doc ? view.props.Document.dragFactory :
-                view.props.Document.layout instanceof Doc ? view.props.Document.layout :
-                    view.props.Document.isTemplateForField ? view.props.Document : Doc.GetProto(view.props.Document);
+            const targetDoc =
+                view.props.Document.dragFactory instanceof Doc
+                    ? view.props.Document.dragFactory
+                    : view.props.Document.layout instanceof Doc
+                    ? view.props.Document.layout
+                    : view.props.Document.isTemplateForField
+                    ? view.props.Document
+                    : Doc.GetProto(view.props.Document);
             if (targetDoc) {
-                if (view.props.LayoutTemplate?.() || view.props.LayoutTemplateString) {  // this situation typically occurs when you have a link dot 
-                    targetDoc.backgroundColor = color.hex;  // bcz: don't know how to change the color of an inline template...
-                }
-                else if (RichTextMenu.Instance?.TextViewFieldKey && window.getSelection()?.toString() !== "") {
-                    Doc.Layout(view.props.Document)[RichTextMenu.Instance.TextViewFieldKey + "-color"] = color.hex;
+                if (view.props.LayoutTemplate?.() || view.props.LayoutTemplateString) {
+                    // this situation typically occurs when you have a link dot
+                    targetDoc.backgroundColor = color.hex; // bcz: don't know how to change the color of an inline template...
+                } else if (RichTextMenu.Instance?.TextViewFieldKey && window.getSelection()?.toString() !== '') {
+                    Doc.Layout(view.props.Document)[RichTextMenu.Instance.TextViewFieldKey + '-color'] = color.hex;
                 } else {
-                    Doc.Layout(view.props.Document)._backgroundColor = color.hex + (color.rgb.a ? Math.round(color.rgb.a * 255).toString(16) : ""); // '_backgroundColor' is template specific.  'backgroundColor' would apply to all templates, but has no UI at the moment
+                    Doc.Layout(view.props.Document)._backgroundColor = color.hex + (color.rgb.a ? Math.round(color.rgb.a * 255).toString(16) : ''); // '_backgroundColor' is template specific.  'backgroundColor' would apply to all templates, but has no UI at the moment
                 }
             }
         });
@@ -43,24 +49,34 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     render() {
         const scaling = Math.min(this.layoutDoc.fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]());
-        return <div className={`colorBox-container${this.isContentActive() ? "-interactive" : ""}`}
-            onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} onClick={e => e.stopPropagation()}
-            style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }} >
-
-            <SketchPicker
-                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']}
-            />
+        return (
+            <div
+                className={`colorBox-container${this.isContentActive() ? '-interactive' : ''}`}
+                onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
+                onClick={e => e.stopPropagation()}
+                style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }}>
+                <SketchPicker
+                    onChange={c => Doc.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']}
+                />
 
-            <div style={{ width: this.props.PanelWidth() / scaling, display: "flex", paddingTop: "10px" }}>
-                <div> {ActiveInkWidth()}</div>
-                <input type="range" defaultValue={ActiveInkWidth()} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
-                    SetActiveInkWidth(e.target.value);
-                    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(e.target.value));
-                }} />
+                <div style={{ width: this.props.PanelWidth() / scaling, display: 'flex', paddingTop: '10px' }}>
+                    <div> {ActiveInkWidth()}</div>
+                    <input
+                        type="range"
+                        defaultValue={ActiveInkWidth()}
+                        min={1}
+                        max={100}
+                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                            SetActiveInkWidth(e.target.value);
+                            SelectionManager.Views()
+                                .filter(i => StrCast(i.rootDoc.type) === DocumentType.INK)
+                                .map(i => (i.rootDoc.strokeWidth = Number(e.target.value)));
+                        }}
+                    />
+                </div>
             </div>
-        </div>;
+        );
     }
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7569b209d..f89c65052 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,56 +1,57 @@
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, StrListCast, WidthSym } from "../../../fields/Doc";
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Document } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
-import { List } from "../../../fields/List";
-import { ObjectField } from "../../../fields/ObjectField";
-import { listSpec } from "../../../fields/Schema";
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
-import { AudioField } from "../../../fields/URLField";
+import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnTrue, returnFalse, returnVal, simulateMouseClick, Utils } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
-import { Docs, DocUtils } from "../../documents/Documents";
-import { DocumentType } from '../../documents/DocumentTypes';
-import { Networking } from "../../Network";
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { DocumentManager } from "../../util/DocumentManager";
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { DocServer } from '../../DocServer';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { Networking } from '../../Network';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
+import { LinkFollower } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { SelectionManager } from "../../util/SelectionManager";
+import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
-import { Transform } from "../../util/Transform";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { CollectionView, CollectionViewType } from '../collections/CollectionView';
-import { ContextMenu } from "../ContextMenu";
+import { Transform } from '../../util/Transform';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { CollectionView } from '../collections/CollectionView';
+import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { DocComponent } from "../DocComponent";
+import { DocComponent } from '../DocComponent';
 import { EditableView } from '../EditableView';
-import { InkingStroke } from "../InkingStroke";
-import { LightboxView } from "../LightboxView";
-import { StyleProp } from "../StyleProvider";
-import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
-import { DocumentContentsView } from "./DocumentContentsView";
+import { InkingStroke } from '../InkingStroke';
+import { LightboxView } from '../LightboxView';
+import { StyleProp } from '../StyleProvider';
+import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
+import { DocumentContentsView } from './DocumentContentsView';
 import { DocumentLinksButton } from './DocumentLinksButton';
-import "./DocumentView.scss";
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
+import './DocumentView.scss';
+import { FieldViewProps } from './FieldView';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
-import { LinkDocPreview } from "./LinkDocPreview";
+import { LinkDocPreview } from './LinkDocPreview';
 import { RadialMenu } from './RadialMenu';
-import { ScriptingBox } from "./ScriptingBox";
+import { ScriptingBox } from './ScriptingBox';
 import { PresBox } from './trails/PresBox';
-import React = require("react");
-import { DocServer } from "../../DocServer";
-import { FieldViewProps } from "./FieldView";
+import React = require('react');
 const { Howl } = require('howler');
 
 interface Window {
@@ -64,16 +65,16 @@ declare class MediaRecorder {
 
 export enum ViewAdjustment {
     resetView = 1,
-    doNothing = 0
+    doNothing = 0,
 }
 
-export const ViewSpecPrefix = "viewSpec";  // field prefix for anchor fields that are immediately copied over to the target document when link is followed.  Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document)
+export const ViewSpecPrefix = 'viewSpec'; // field prefix for anchor fields that are immediately copied over to the target document when link is followed.  Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document)
 
 export interface DocFocusOptions {
     originalTarget?: Doc; // set in JumpToDocument, used by TabDocView to determine whether to fit contents to tab
-    willZoom?: boolean;   // determines whether to zoom in on target document
-    scale?: number;       // percent of containing frame to zoom into document
-    afterFocus?: DocAfterFocusFunc;  // function to call after focusing on a document
+    willZoom?: boolean; // determines whether to zoom in on target document
+    scale?: number; // percent of containing frame to zoom into document
+    afterFocus?: DocAfterFocusFunc; // function to call after focusing on a document
     docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom.  when this happens the docTransform must accumulate all the transforms of each level of the hierarchy
     instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom)
 }
@@ -84,9 +85,9 @@ export interface DocComponentView {
     updateIcon?: () => void; // updates the icon representation of the document
     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
+    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/ 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
+    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
     getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
@@ -99,20 +100,20 @@ export interface DocComponentView {
     annotationKey?: string;
     getTitle?: () => string;
     getScrollHeight?: () => number;
-    getCenter?: (xf: Transform) => { X: number, Y: number };
-    ptToScreen?: (pt: { X: number, Y: number }) => { X: number, Y: number };
-    ptFromScreen?: (pt: { X: number, Y: number }) => { X: number, Y: number };
-    snapPt?: (pt: { X: number, Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number, Y: number }, distance: number };
+    getCenter?: (xf: Transform) => { X: number; Y: number };
+    ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
+    ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
+    snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
     search?: (str: string, bwd?: boolean, clear?: boolean) => boolean;
 }
-// These props are passed to both FieldViews and DocumentViews 
+// These props are passed to both FieldViews and DocumentViews
 export interface DocumentViewSharedProps {
     fieldKey?: string; // only used by FieldViews but helpful here to allow styleProviders to access fieldKey of FieldViewProps.  In priniciple, passing a fieldKey to a documentView could override or be the default fieldKey for fieldViews
     DocumentView?: () => DocumentView;
     renderDepth: number;
     Document: Doc;
     DataDoc?: Doc;
-    contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
+    contentBounds?: () => undefined | { x: number; y: number; r: number; b: number };
     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>;
@@ -137,7 +138,7 @@ export interface DocumentViewSharedProps {
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: string) => boolean;
-    filterAddDocument?: (doc: Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+    filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
     addDocument?: (doc: Doc | Doc[]) => boolean;
     removeDocument?: (doc: Doc | Doc[]) => boolean;
     moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
@@ -161,8 +162,8 @@ export interface DocumentViewSharedProps {
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
     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
+    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
     hideDocumentButtonBar?: boolean;
     hideOpenButton?: boolean;
     hideDeleteButton?: boolean;
@@ -172,19 +173,19 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     radialMenu?: String[];
     LayoutTemplateString?: string;
-    dontCenter?: "x" | "y" | "xy";
+    dontCenter?: 'x' | 'y' | 'xy';
     dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling
     ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
     NativeWidth?: () => number;
     NativeHeight?: () => number;
     LayoutTemplate?: () => Opt<Doc>;
-    contextMenuItems?: () => { script: ScriptField, filter?: ScriptField, label: string, icon: string }[];
+    contextMenuItems?: () => { script: ScriptField; filter?: ScriptField; label: string; icon: string }[];
     onClick?: () => ScriptField;
     onDoubleClick?: () => ScriptField;
     onPointerDown?: () => ScriptField;
     onPointerUp?: () => ScriptField;
-    onBrowseClick?: () => (ScriptField | undefined);
-    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined);
+    onBrowseClick?: () => ScriptField | undefined;
+    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
 }
 
 // these props are only available in DocumentViewIntenral
@@ -219,34 +220,88 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     @observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
 
-    private get topMost() { return this.props.renderDepth === 0 && !LightboxView.LightboxDoc; }
-    public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
-    public get ContentDiv() { return this._mainCont.current; }
-    public get LayoutFieldKey() { return Doc.LayoutFieldKey(this.layoutDoc); }
-    @computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt<string>); }
-    @computed get ContentScale() { return this.props.ContentScaling?.() || 1; }
-    @computed get thumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png"); }
-    @computed get hidden() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden); }
-    @computed get opacity() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity); }
-    @computed get boxShadow() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow); }
-    @computed get borderRounding() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); }
-    @computed get hideLinkButton() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.props.isSelected() ? ":selected" : "")); }
-    @computed get widgetDecorations() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations + (this.props.isSelected() ? ":selected" : "")); }
-    @computed get backgroundColor() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor); }
-    @computed get docContents() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.DocContents); }
-    @computed get headerMargin() { return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; }
-    @computed get titleHeight() { return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0; }
-    @computed get pointerEvents() { return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ":selected" : "")); }
-    @computed get finalLayoutKey() { return StrCast(this.Document.layoutKey, "layout"); }
-    @computed get nativeWidth() { return this.props.NativeWidth(); }
-    @computed get nativeHeight() { return this.props.NativeHeight(); }
-    @computed get onClickHandler() { return this.props.onClick?.() ?? (this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null))); }
-    @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
-    @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
-    @computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
-
-    componentWillUnmount() { this.cleanupHandlers(true); }
-    componentDidMount() { this.setupHandlers(); }
+    private get topMost() {
+        return this.props.renderDepth === 0 && !LightboxView.LightboxDoc;
+    }
+    public get displayName() {
+        return 'DocumentView(' + this.props.Document.title + ')';
+    } // this makes mobx trace() statements more descriptive
+    public get ContentDiv() {
+        return this._mainCont.current;
+    }
+    public get LayoutFieldKey() {
+        return Doc.LayoutFieldKey(this.layoutDoc);
+    }
+    @computed get ShowTitle() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
+    }
+    @computed get ContentScale() {
+        return this.props.ContentScaling?.() || 1;
+    }
+    @computed get thumb() {
+        return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
+    }
+    @computed get hidden() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden);
+    }
+    @computed get opacity() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity);
+    }
+    @computed get boxShadow() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow);
+    }
+    @computed get borderRounding() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
+    }
+    @computed get hideLinkButton() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.props.isSelected() ? ':selected' : ''));
+    }
+    @computed get widgetDecorations() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations + (this.props.isSelected() ? ':selected' : ''));
+    }
+    @computed get backgroundColor() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
+    }
+    @computed get docContents() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.DocContents);
+    }
+    @computed get headerMargin() {
+        return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
+    }
+    @computed get titleHeight() {
+        return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
+    }
+    @computed get pointerEvents() {
+        return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
+    }
+    @computed get finalLayoutKey() {
+        return StrCast(this.Document.layoutKey, 'layout');
+    }
+    @computed get nativeWidth() {
+        return this.props.NativeWidth();
+    }
+    @computed get nativeHeight() {
+        return this.props.NativeHeight();
+    }
+    @computed get onClickHandler() {
+        return this.props.onClick?.() ?? this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
+    }
+    @computed get onDoubleClickHandler() {
+        return this.props.onDoubleClick?.() ?? Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick;
+    }
+    @computed get onPointerDownHandler() {
+        return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown);
+    }
+    @computed get onPointerUpHandler() {
+        return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp);
+    }
+
+    componentWillUnmount() {
+        this.cleanupHandlers(true);
+    }
+    componentDidMount() {
+        this.setupHandlers();
+    }
     //componentDidUpdate() { this.setupHandlers(); }
     setupHandlers() {
         this.cleanupHandlers(false);
@@ -268,8 +323,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
         this.removeMoveListeners();
         this.removeEndListeners();
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointerup', this.onPointerUp);
         if (RadialMenu.Instance._display === false) {
             this.addHoldMoveListeners();
             this.addHoldEndListeners();
@@ -278,7 +333,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._firstX = pt.pageX;
             this._firstY = pt.pageY;
         }
-    }
+    };
 
     handle1PointerHoldMove = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
@@ -289,7 +344,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (Math.abs(pt.pageX - this._firstX) > 150 || Math.abs(pt.pageY - this._firstY) > 150) {
             this.handle1PointerHoldEnd(e, me);
         }
-    }
+    };
 
     handle1PointerHoldEnd = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         this.removeHoldMoveListeners();
@@ -304,7 +359,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (RadialMenu.Instance.used) {
             this.onContextMenu(undefined, me.touches[0].pageX, me.touches[0].pageY);
         }
-    }
+    };
 
     handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
         if (!e.nativeEvent.cancelBubble && !this.props.isSelected()) {
@@ -316,7 +371,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this.removeEndListeners();
             this.addEndListeners();
         }
-    }
+    };
 
     handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
         SelectionManager.DeselectAll();
@@ -326,7 +381,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 && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
+                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
                 this.removeEndListeners();
@@ -334,24 +389,23 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 e.stopPropagation();
             }
         }
-    }
+    };
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
         if (e.cancelBubble && this.props.isDocumentActive?.()) {
             this.removeMoveListeners();
-        }
-        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+        } else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.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)) {
                     this.cleanUpInteractions();
-                    this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined);
+                    this.startDragging(this._downX, this._downY, this.Document.dropAction ? (this.Document.dropAction as any) : e.ctrlKey || e.altKey ? 'alias' : undefined);
                 }
             }
             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();
         }
-    }
+    };
 
     @action
     handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
@@ -362,8 +416,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const oldPoint2 = this.prevPoints.get(pt2.identifier);
         const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!);
         if (pinching !== 0 && oldPoint1 && oldPoint2) {
-            const dW = (Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX));
-            const dH = (Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY));
+            const dW = Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX);
+            const dH = Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY);
             const dX = -1 * Math.sign(dW);
             const dY = -1 * Math.sign(dH);
 
@@ -372,33 +426,32 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 const layoutDoc = Document(Doc.Layout(this.props.Document));
                 let nwidth = Doc.NativeWidth(layoutDoc);
                 let nheight = Doc.NativeHeight(layoutDoc);
-                const width = (layoutDoc._width || 0);
-                const height = (layoutDoc._height || (nheight / nwidth * width));
+                const width = layoutDoc._width || 0;
+                const height = layoutDoc._height || (nheight / nwidth) * width;
                 const scale = this.props.ScreenToLocalTransform().Scale * this.ContentScale;
-                const actualdW = Math.max(width + (dW * scale), 20);
-                const actualdH = Math.max(height + (dH * scale), 20);
+                const actualdW = Math.max(width + dW * scale, 20);
+                const actualdH = Math.max(height + dH * scale, 20);
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
                 const fixedAspect = e.ctrlKey || (nwidth && nheight);
                 if (fixedAspect && (!nwidth || !nheight)) {
-                    Doc.SetNativeWidth(layoutDoc, nwidth = layoutDoc._width || 0);
-                    Doc.SetNativeHeight(layoutDoc, nheight = layoutDoc._height || 0);
+                    Doc.SetNativeWidth(layoutDoc, (nwidth = layoutDoc._width || 0));
+                    Doc.SetNativeHeight(layoutDoc, (nheight = layoutDoc._height || 0));
                 }
                 if (nwidth > 0 && nheight > 0) {
                     if (Math.abs(dW) > Math.abs(dH)) {
                         if (!fixedAspect) {
-                            Doc.SetNativeWidth(layoutDoc, actualdW / (layoutDoc._width || 1) * Doc.NativeWidth(layoutDoc));
+                            Doc.SetNativeWidth(layoutDoc, (actualdW / (layoutDoc._width || 1)) * Doc.NativeWidth(layoutDoc));
                         }
                         layoutDoc._width = actualdW;
-                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._height = nheight / nwidth * layoutDoc._width;
+                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._height = (nheight / nwidth) * layoutDoc._width;
                         else layoutDoc._height = actualdH;
-                    }
-                    else {
+                    } else {
                         if (!fixedAspect) {
-                            Doc.SetNativeHeight(layoutDoc, actualdH / (layoutDoc._height || 1) * Doc.NativeHeight(doc));
+                            Doc.SetNativeHeight(layoutDoc, (actualdH / (layoutDoc._height || 1)) * Doc.NativeHeight(doc));
                         }
                         layoutDoc._height = actualdH;
-                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._width = nwidth / nheight * layoutDoc._height;
+                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._width = (nwidth / nheight) * layoutDoc._height;
                         else layoutDoc._width = actualdW;
                     }
                 } else {
@@ -410,7 +463,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             e.stopPropagation();
             e.preventDefault();
         }
-    }
+    };
 
     @action
     onRadialMenu = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): void => {
@@ -419,19 +472,30 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "map-pin", selected: -1 });
         const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
-        (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 });
+        (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) &&
+            RadialMenu.Instance.addItem({
+                description: 'Delete',
+                event: () => {
+                    this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu();
+                },
+                icon: 'external-link-square-alt',
+                selected: -1,
+            });
         // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 });
-        RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 });
-        RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 });
+        RadialMenu.Instance.addItem({ description: 'Pin', event: () => this.props.pinToPres(this.props.Document), icon: 'map-pin', selected: -1 });
+        RadialMenu.Instance.addItem({ description: 'Open', event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: 'trash', selected: -1 });
 
         SelectionManager.DeselectAll();
-    }
+    };
 
     startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
         if (this._mainCont.current) {
             const dragData = new DragManager.DocumentDragData([this.props.Document]);
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
-            dragData.offset = this.props.ScreenToLocalTransform().scale(this.ContentScale).transformDirection(x - left, y - top);
+            dragData.offset = this.props
+                .ScreenToLocalTransform()
+                .scale(this.ContentScale)
+                .transformDirection(x - left, y - top);
             dragData.offset[0] = Math.min(this.rootDoc[WidthSym](), dragData.offset[0]);
             dragData.offset[1] = Math.min(this.rootDoc[HeightSym](), dragData.offset[1]);
             dragData.dropAction = dropAction;
@@ -440,8 +504,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             dragData.moveDocument = this.props.moveDocument;
             const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
             ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
-            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart) },
-                () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))); // this needs to happen after the drop event is processed.
+            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart) }, () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))); // this needs to happen after the drop event is processed.
             ffview?.setupDragLines(false);
         }
     }
@@ -450,91 +513,114 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.altKey && !e.nativeEvent.cancelBubble) {
             e.stopPropagation();
             e.preventDefault();
-            if (e.key === "†" || e.key === "t") {
-                if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = "title";
+            if (e.key === '†' || e.key === 't') {
+                if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = 'title';
                 if (!this._titleRef.current) setTimeout(() => this._titleRef.current?.setIsFocused(true), 0);
-                else if (!this._titleRef.current.setIsFocused(true)) { // if focus didn't change, focus on interior text...
+                else if (!this._titleRef.current.setIsFocused(true)) {
+                    // if focus didn't change, focus on interior text...
                     this._titleRef.current?.setIsFocused(false);
                     this._componentView?.setFocus?.();
                 }
             }
         }
-    }
+    };
 
     focus = (anchor: Doc, options?: DocFocusOptions) => {
-        LightboxView.SetCookie(StrCast(anchor["cookies-set"]));
+        LightboxView.SetCookie(StrCast(anchor['cookies-set']));
         // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
-        Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
-            this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
-        });
+        Array.from(Object.keys(Doc.GetProto(anchor)))
+            .filter(key => key.startsWith(ViewSpecPrefix))
+            .forEach(spec => {
+                this.layoutDoc[spec.replace(ViewSpecPrefix, '')] = (field => (field instanceof ObjectField ? ObjectField.MakeCopy(field) : field))(anchor[spec]);
+            });
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
         const focusSpeed = this._componentView?.scrollFocus?.(anchor, options?.instant === false || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
-        const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => (options?.afterFocus?.(true) ?? ViewAdjustment.doNothing);
+        const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
-            ...options, afterFocus: (didFocus: boolean) =>
-                new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? 
-                    await endFocus(didFocus||focusSpeed !== undefined) : 
-                    ViewAdjustment.doNothing), focusSpeed ?? 0)
-                    )
+            ...options,
+            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), focusSpeed ?? 0)),
         });
-
-    }
+    };
     onClick = action((e: React.MouseEvent | React.PointerEvent) => {
-        if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 &&
-            (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
+        if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             let stopPropagate = true;
             let preventDefault = true;
             const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
             (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._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);
                     this._pendingDoubleClick = false;
                     this._timeout = undefined;
                 }
-                if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
+                if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) {
+                    // bcz: hack? don't execute script if you're clicking on a scripting box itself
                     const { clientX, clientY, shiftKey } = e;
-                    const func = () => this.onDoubleClickHandler.script.run({
-                        this: this.layoutDoc,
-                        self: this.rootDoc,
-                        scriptContext: this.props.scriptContext,
-                        thisContainer: this.props.ContainingCollectionDoc,
-                        documentView: this.props.DocumentView(),
-                        clientX, clientY, shiftKey
-                    }, console.log);
-                    UndoManager.RunInBatch(() => func().result?.select === true ? this.props.select(false) : "", "on double click");
+                    const func = () =>
+                        this.onDoubleClickHandler.script.run(
+                            {
+                                this: this.layoutDoc,
+                                self: this.rootDoc,
+                                scriptContext: this.props.scriptContext,
+                                thisContainer: this.props.ContainingCollectionDoc,
+                                documentView: this.props.DocumentView(),
+                                clientX,
+                                clientY,
+                                shiftKey,
+                            },
+                            console.log
+                        );
+                    UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
                 } else if (!Doc.IsSystem(this.rootDoc)) {
-                    UndoManager.RunInBatch(() =>
-                        LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.(), this.props.addDocTab)
-                        , "double tap");
+                    UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, 'lightbox', this.props.LayoutTemplate?.(), this.props.addDocTab), 'double tap');
                     SelectionManager.DeselectAll();
                     Doc.UnBrushDoc(this.props.Document);
                 }
-            } else if (this.onClickHandler?.script && !isScriptBox()) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
+            } else if (this.onClickHandler?.script && !isScriptBox()) {
+                // bcz: hack? don't execute script if you're clicking on a scripting box itself
                 const { clientX, clientY, shiftKey } = e;
-                const func = () => this.onClickHandler.script.run({
-                    this: this.layoutDoc,
-                    self: this.rootDoc,
-                    _readOnly_: false,
-                    scriptContext: this.props.scriptContext,
-                    thisContainer: this.props.ContainingCollectionDoc,
-                    documentView: this.props.DocumentView(),
-                    clientX, clientY, shiftKey
-                }, console.log).result?.select === true ? this.props.select(false) : "";
-                const clickFunc = () => this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, "on click");
+                const func = () =>
+                    this.onClickHandler.script.run(
+                        {
+                            this: this.layoutDoc,
+                            self: this.rootDoc,
+                            _readOnly_: false,
+                            scriptContext: this.props.scriptContext,
+                            thisContainer: this.props.ContainingCollectionDoc,
+                            documentView: this.props.DocumentView(),
+                            clientX,
+                            clientY,
+                            shiftKey,
+                        },
+                        console.log
+                    ).result?.select === true
+                        ? this.props.select(false)
+                        : '';
+                const clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
                 if (this.onDoubleClickHandler) {
-                    runInAction(() => this._pendingDoubleClick = true);
-                    this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 350);
+                    runInAction(() => (this._pendingDoubleClick = true));
+                    this._timeout = setTimeout(() => {
+                        this._timeout = undefined;
+                        clickFunc();
+                    }, 350);
                 } else clickFunc();
             } else if (this.allLinks && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
-                this.allLinks.length && LinkManager.FollowLink(undefined, this.props.Document, this.props, e.altKey);
+                this.allLinks.length && LinkFollower.FollowLink(undefined, this.props.Document, this.props, e.altKey);
             } else {
-                if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {  // onDragStart implies a button doc that we don't want to select when clicking.   RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
+                if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
+                    // onDragStart implies a button doc that we don't want to select when clicking.   RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
                 } else {
-                    runInAction(() => this._pendingDoubleClick = true);
-                    this._timeout = setTimeout(action(() => { this._pendingDoubleClick = false; this._timeout = undefined; }), 350);
+                    runInAction(() => (this._pendingDoubleClick = true));
+                    this._timeout = setTimeout(
+                        action(() => {
+                            this._pendingDoubleClick = false;
+                            this._timeout = undefined;
+                        }),
+                        350
+                    );
                     this.props.select(e.ctrlKey || e.shiftKey);
                 }
                 preventDefault = false;
@@ -545,9 +631,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     });
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.ActiveTool === InkTool.Eraser) return;
+        if (this.rootDoc.type === DocumentType.INK && Doc.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.ActiveTool))) {
+        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.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
@@ -557,50 +643,54 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         this._downX = e.clientX;
         this._downY = e.clientY;
-        if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.layoutDoc.onDragStart) &&
+        if (
+            (!e.nativeEvent.cancelBubble || this.onClickHandler || this.layoutDoc.onDragStart) &&
             // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
-            !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
-            if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
+            !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))
+        ) {
+            if (
+                (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
                 !this.props.onBrowseClick?.() &&
                 !this.Document.ignoreClick &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+                !DocListCast(Doc.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();
             }
             if (this.props.isDocumentActive?.()) {
-                document.removeEventListener("pointermove", this.onPointerMove);
-                document.addEventListener("pointermove", this.onPointerMove);
+                document.removeEventListener('pointermove', this.onPointerMove);
+                document.addEventListener('pointermove', this.onPointerMove);
             }
-            document.removeEventListener("pointerup", this.onPointerUp);
-            document.addEventListener("pointerup", this.onPointerUp);
+            document.removeEventListener('pointerup', this.onPointerUp);
+            document.addEventListener('pointerup', this.onPointerUp);
         }
-    }
+    };
 
     onPointerMove = (e: PointerEvent): void => {
         if (e.cancelBubble) return;
-        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) return;
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.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);
-                    document.removeEventListener("pointerup", this.onPointerUp);
-                    this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && "alias") || (this.props.dropAction || this.Document.dropAction || undefined) as dropActionType);
+                    document.removeEventListener('pointermove', this.onPointerMove);
+                    document.removeEventListener('pointerup', this.onPointerUp);
+                    this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.props.dropAction || this.Document.dropAction || undefined) as dropActionType));
                 }
             }
             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();
         }
-    }
+    };
 
     cleanupPointerEvents = () => {
         this.cleanUpInteractions();
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointerup', this.onPointerUp);
+    };
 
     onPointerUp = (e: PointerEvent): void => {
         this.cleanupPointerEvents();
@@ -608,13 +698,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
             this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
         } else {
-            this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2);
+            this._doubleTap = Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2;
             // bcz: this is a placeholder.  documents, when selected, should stopPropagation on doubleClicks if they want to keep the DocumentView from getting them
-            if (!this.props.isSelected(true) || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this.rootDoc.type) as any)) this._lastTap = Date.now();// don't want to process the start of a double tap if the doucment is selected
+            if (!this.props.isSelected(true) || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this.rootDoc.type) as any)) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
         }
-    }
+    };
 
-    @undoBatch @action
+    @undoBatch
+    @action
     toggleFollowLink = (location: Opt<string>, zoom?: boolean, setPushpin?: boolean): void => {
         this.Document.ignoreClick = false;
         if (setPushpin) {
@@ -628,46 +719,48 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this.Document.followLinkLocation = location;
         } else if (this.Document._isLinkButton && this.onClickHandler) {
             this.Document._isLinkButton = false;
-            this.Document["onClick-rawScript"] = this.dataDoc["onClick-rawScript"] = this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
+            this.Document['onClick-rawScript'] = this.dataDoc['onClick-rawScript'] = this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
         }
-    }
-    @undoBatch @action
+    };
+    @undoBatch
+    @action
     toggleTargetOnClick = (): void => {
         this.Document.ignoreClick = false;
         this.Document._isLinkButton = true;
         this.Document.isPushpin = true;
-    }
-    @undoBatch @action
-    followLinkOnClick = (location: Opt<string>, zoom: boolean,): void => {
+    };
+    @undoBatch
+    @action
+    followLinkOnClick = (location: Opt<string>, zoom: boolean): void => {
         this.Document.ignoreClick = false;
         this.Document._isLinkButton = true;
         this.Document.isPushpin = false;
         this.Document.followLinkZoom = zoom;
         this.Document.followLinkLocation = location;
-    }
-    @undoBatch @action
+    };
+    @undoBatch
+    @action
     selectOnClick = (): void => {
         this.Document.ignoreClick = false;
         this.Document._isLinkButton = false;
         this.Document.isPushpin = false;
         this.Document.onClick = this.layoutDoc.onClick = undefined;
-    }
+    };
     @undoBatch
     noOnClick = (): void => {
         this.Document.ignoreClick = false;
         this.Document._isLinkButton = false;
-    }
+    };
 
     @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
-    @undoBatch setToggleDetail = () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${StrCast(this.Document.layoutKey).replace("layout_", "")}")`, { documentView: "any" });
+    @undoBatch setToggleDetail = () => (this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${StrCast(this.Document.layoutKey).replace('layout_', '')}")`, { documentView: 'any' }));
 
-    @undoBatch @action
+    @undoBatch
+    @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
         if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return;
-        if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
-            alert((e.target as any)?.closest?.("*.lm_content") ?
-                "You can't perform this move most likely because you don't have permission to modify the destination." :
-                "Linking to document tabs not yet supported. Drop link on document content.");
+        if (this.props.Document === Doc.ActiveDashboard) {
+            alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
             return;
         }
         const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
@@ -682,20 +775,20 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
             }
         }
-    }
+    };
 
     @undoBatch
     @action
     makeIntoPortal = async () => {
         const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document);
         if (!portalLink) {
-            const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + " [Portal]" });
-            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to:portal from");
+            const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' });
+            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, 'portal to:portal from');
         }
-        this.Document.followLinkLocation = "inPlace";
+        this.Document.followLinkLocation = 'inPlace';
         this.Document.followLinkZoom = true;
         this.Document._isLinkButton = true;
-    }
+    };
 
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
@@ -707,7 +800,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
         if (e) {
-            if (e.button === 0 && !e.ctrlKey || e.isDefaultPrevented()) {
+            if ((e.button === 0 && !e.ctrlKey) || e.isDefaultPrevented()) {
                 e.preventDefault();
                 return;
             }
@@ -715,7 +808,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             e.stopPropagation();
             e.persist();
 
-            if (!navigator.userAgent.includes("Mozilla") && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
+            if (!navigator.userAgent.includes('Mozilla') && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
                 return;
             }
         }
@@ -724,17 +817,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
 
         if (e && !(e.nativeEvent as any).dash) {
-            const onDisplay = () => setTimeout(() => {
-                DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
+            const onDisplay = () =>
                 setTimeout(() => {
-                    const ele = document.elementFromPoint(e.clientX, e.clientY);
-                    simulateMouseClick(ele, e.clientX, e.clientY, e.screenX, e.screenY);
+                    DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
+                    setTimeout(() => {
+                        const ele = document.elementFromPoint(e.clientX, e.clientY);
+                        simulateMouseClick(ele, e.clientX, e.clientY, e.screenX, e.screenY);
+                    });
                 });
-            });
-            if (navigator.userAgent.includes("Macintosh")) {
+            if (navigator.userAgent.includes('Macintosh')) {
                 cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15, undefined, undefined, onDisplay);
-            }
-            else {
+            } else {
                 onDisplay();
             }
             return;
@@ -742,232 +835,291 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
         StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
-            cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
-        this.props.contextMenuItems?.().forEach(item =>
-            item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp }));
+            cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: 'sticky-note' })
+        );
+        this.props
+            .contextMenuItems?.()
+            .forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp }));
 
         if (!this.props.Document.isFolder) {
             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.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");
-                    alias.title = "newfield";
-                    alias._height = 35;
-                    alias._width = 100;
-                    alias.syncLayoutFieldWithTitle = true;
-                    alias.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc.width);
-                    alias.y = NumCast(this.rootDoc.y);
-                    this.props.addDocument?.(alias);
-                }, icon: "eye"
-            });
-            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" });
+            const appearance = cm.findByDescription('UI Controls...');
+            const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
+            !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');
+                        alias.title = 'newfield';
+                        alias._height = 35;
+                        alias._width = 100;
+                        alias.syncLayoutFieldWithTitle = true;
+                        alias.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc.width);
+                        alias.y = NumCast(this.rootDoc.y);
+                        this.props.addDocument?.(alias);
+                    },
+                    icon: 'eye',
+                });
+            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.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
-                !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 : [];
+                !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 : [];
 
-                const zorders = cm.findByDescription("ZOrder...");
-                const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : [];
+                const zorders = cm.findByDescription('ZOrder...');
+                const zorderItems: ContextMenuProps[] = zorders && 'subitems' in zorders ? zorders.subitems : [];
                 if (this.props.bringToFront !== emptyFunction) {
-                    zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
-                    zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
-                    zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
+                    zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: 'expand-arrows-alt' });
+                    zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: 'expand-arrows-alt' });
+                    zorderItems.push({
+                        description: this.rootDoc._raiseWhenDragged !== false ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
+                        event: undoBatch(action(() => (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))),
+                        icon: 'expand-arrows-alt',
+                    });
                 }
-                !zorders && cm.addItem({ description: "ZOrder...", noexpand: true, subitems: zorderItems, icon: "compass" });
+                !zorders && cm.addItem({ description: 'ZOrder...', noexpand: true, subitems: zorderItems, icon: 'compass' });
 
-                !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" });
+                !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) {
-                    const options = cm.findByDescription("Options...");
-                    const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
-                    !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
-
-                    onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
-                    onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
-                    !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" });
-                    onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
-                    onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
-                    onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
-                    !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+                    const options = cm.findByDescription('Options...');
+                    const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
+                    !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
+
+                    onClicks.push({ description: this.Document.ignoreClick ? 'Select' : 'Do Nothing', event: () => (this.Document.ignoreClick = !this.Document.ignoreClick), icon: this.Document.ignoreClick ? 'unlock' : 'lock' });
+                    onClicks.push({ description: this.Document.isLinkButton ? 'Remove Follow Behavior' : 'Follow Link in Place', event: () => this.toggleFollowLink('inPlace', true, false), icon: 'link' });
+                    !this.Document.isLinkButton && onClicks.push({ description: 'Follow Link on Right', event: () => this.toggleFollowLink('add:right', false, false), icon: 'link' });
+                    onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(undefined, false, false), icon: 'link' });
+                    onClicks.push({ description: (this.Document.isPushpin ? 'Remove' : 'Make') + ' Pushpin', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
+                    onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
+                    !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
                 } else if (DocListCast(this.Document.links).length) {
-                    onClicks.push({ description: "Select on Click", event: () => this.selectOnClick(), icon: "link" });
-                    onClicks.push({ description: "Follow Link on Click", event: () => this.followLinkOnClick(undefined, false), icon: "link" });
-                    onClicks.push({ description: "Toggle Link Target on Click", event: () => this.toggleTargetOnClick(), icon: "map-pin" });
-                    !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+                    onClicks.push({ description: 'Select on Click', event: () => this.selectOnClick(), icon: 'link' });
+                    onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(undefined, false), icon: 'link' });
+                    onClicks.push({ description: 'Toggle Link Target on Click', event: () => this.toggleTargetOnClick(), icon: 'map-pin' });
+                    !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, subitems: onClicks, icon: 'mouse-pointer' });
                 }
             }
 
             const funcs: ContextMenuProps[] = [];
             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 });
-                cm.addItem({ description: "OnDrag...", noexpand: true, subitems: funcs, icon: "asterisk" });
+                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) });
+                cm.addItem({ description: 'OnDrag...', noexpand: true, subitems: funcs, icon: 'asterisk' });
             }
 
-            const more = cm.findByDescription("More...");
-            const moreItems = more && "subitems" in more ? more.subitems : [];
+            const more = cm.findByDescription('More...');
+            const moreItems = more && 'subitems' in more ? more.subitems : [];
             if (!Doc.IsSystem(this.rootDoc)) {
-                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
+                (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" });
+                    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' });
 
                     if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
-                        moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
-                        moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
-                        moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+                        moreItems.push({ description: 'Export to Google Photos Album', event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: 'caret-square-right' });
+                        moreItems.push({ description: 'Tag Child Images via Google Photos', event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: 'caret-square-right' });
+                        moreItems.push({ description: 'Write Back Link to Album', event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: 'caret-square-right' });
                     }
-                    moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: "fingerprint" });
+                    moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' });
                 }
             }
 
-            if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && CurrentUserUtils.ActiveDashboard !== this.props.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
-                moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+            if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && Doc.ActiveDashboard !== this.props.Document) {
+                // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
+                moreItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
             }
-            !more && moreItems.length && cm.addItem({ description: "More...", subitems: moreItems, icon: "compass" });
-
-            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.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" });
+            !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
+
+            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.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' });
         }
 
         if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event
         cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
-    }
+    };
 
     collectionFilters = () => StrListCast(this.props.Document._docFilters);
     collectionRangeDocFilters = () => StrListCast(this.props.Document._docRangeFilters);
     @computed get showFilterIcon() {
-        return this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
-            this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined;
+        return this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined;
     }
     rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
-    setHeight = (height: number) => this.layoutDoc._height = height;
-    setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
+    setHeight = (height: number) => (this.layoutDoc._height = height);
+    setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
     isContentActive = (outsideReaction?: boolean) => {
-        return this.props.isContentActive() === false ? false : (
-            CurrentUserUtils.ActiveTool !== InkTool.None ||
-            SnappingManager.GetIsDragging() ||
-            this.rootSelected() ||
-            this.props.Document.forceActive ||
-            this.props.isSelected(outsideReaction) ||
-            this._componentView?.isAnyChildContentActive?.() ||
-            this.props.isContentActive()) ? true : undefined;
-    }
+        return this.props.isContentActive() === false
+            ? false
+            : Doc.ActiveTool !== InkTool.None ||
+              SnappingManager.GetIsDragging() ||
+              this.rootSelected() ||
+              this.props.Document.forceActive ||
+              this.props.isSelected(outsideReaction) ||
+              this._componentView?.isAnyChildContentActive?.() ||
+              this.props.isContentActive()
+            ? true
+            : undefined;
+    };
     @observable _retryThumb = 1;
     thumbShown = () => {
-        return !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+        return !this.props.isSelected() &&
+            LightboxView.LightboxDoc !== this.rootDoc &&
+            this.thumb &&
             !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
             !Doc.isBrushedHighlightedDegree(this.props.Document) &&
-            !this._componentView?.isAnyChildContentActive?.() ? true : false;
-    }
+            !this._componentView?.isAnyChildContentActive?.()
+            ? true
+            : false;
+    };
     @computed get contents() {
         TraceMobx();
-        const audioView = !this.layoutDoc._showAudio ? (null) :
-            <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter} >
-                <FontAwesomeIcon className="documentView-audioFont"
-                    style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._mediaState] }}
-                    icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
-            </div>;
-
-        return <div className="documentView-contentsView"
-            style={{
-                pointerEvents: this.props.pointerEvents?.() as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" :
-                    this.props.contentPointerEvents as any ? this.props.contentPointerEvents as any :
-                        this.rootDoc.type !== DocumentType.INK && this.isContentActive() ? "all" :
-                            "none",
-                height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
-            }}>
-            {!this._retryThumb || !this.thumbShown() ? (null) :
-                <img style={{ background: "white", top: 0, position: "relative" }} src={this.thumb} // + '?d=' + (new Date()).getTime()}
-                    width={this.props.PanelWidth()} height={this.props.PanelHeight()}
-                    onError={(e: any) => {
-                        setTimeout(action(() => this._retryThumb = 0), 0);
-                        setTimeout(action(() => this._retryThumb = 1), 150);
-                    }} />}
-            <DocumentContentsView key={1}
-                {...this.props}
-                docViewPath={this.props.viewPath}
-                thumbShown={this.thumbShown}
-                isHovering={this.isHovering}
-                setContentView={this.setContentView}
-                scaling={this.contentScaling}
-                PanelHeight={this.panelHeight}
-                setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
-                isContentActive={this.isContentActive}
-                ScreenToLocalTransform={this.screenToLocal}
-                rootSelected={this.rootSelected}
-                onClick={this.onClickFunc}
-                focus={this.focus}
-                layoutKey={this.finalLayoutKey} />
-            {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
-            {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
-                <DocumentLinksButton View={this.props.DocumentView()}
-                    ContentScaling={this.props.ContentScaling}
-                    Offset={[this.topMost ? 0 : !this.props.isSelected() ? - 15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? - 15 : -30]} />
-            }
-            {audioView}
-        </div>;
+        const audioView = !this.layoutDoc._showAudio ? null : (
+            <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter}>
+                <FontAwesomeIcon
+                    className="documentView-audioFont"
+                    style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']).length ? 'blue' : 'gray', 'green', 'red'][this._mediaState] }}
+                    icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']).length ? 'microphone' : 'file-audio'}
+                    size="sm"
+                />
+            </div>
+        );
+
+        return (
+            <div
+                className="documentView-contentsView"
+                style={{
+                    pointerEvents:
+                        (this.props.pointerEvents?.() as any) ?? this.rootDoc.layoutKey === 'layout_icon'
+                            ? 'none'
+                            : (this.props.contentPointerEvents as any)
+                            ? (this.props.contentPointerEvents as any)
+                            : this.rootDoc.type !== DocumentType.INK && this.isContentActive()
+                            ? 'all'
+                            : 'none',
+                    height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
+                }}>
+                {!this._retryThumb || !this.thumbShown() ? null : (
+                    <img
+                        style={{ background: 'white', top: 0, position: 'relative' }}
+                        src={this.thumb} // + '?d=' + (new Date()).getTime()}
+                        width={this.props.PanelWidth()}
+                        height={this.props.PanelHeight()}
+                        onError={(e: any) => {
+                            setTimeout(
+                                action(() => (this._retryThumb = 0)),
+                                0
+                            );
+                            setTimeout(
+                                action(() => (this._retryThumb = 1)),
+                                150
+                            );
+                        }}
+                    />
+                )}
+                <DocumentContentsView
+                    key={1}
+                    {...this.props}
+                    docViewPath={this.props.viewPath}
+                    thumbShown={this.thumbShown}
+                    isHovering={this.isHovering}
+                    setContentView={this.setContentView}
+                    scaling={this.contentScaling}
+                    PanelHeight={this.panelHeight}
+                    setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
+                    isContentActive={this.isContentActive}
+                    ScreenToLocalTransform={this.screenToLocal}
+                    rootSelected={this.rootSelected}
+                    onClick={this.onClickFunc}
+                    focus={this.focus}
+                    layoutKey={this.finalLayoutKey}
+                />
+                {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
+                {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? null : (
+                    <DocumentLinksButton
+                        View={this.props.DocumentView()}
+                        ContentScaling={this.props.ContentScaling}
+                        Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -30]}
+                    />
+                )}
+                {audioView}
+            </div>
+        );
     }
 
     get indicatorIcon() {
-        if (this.props.Document["acl-Public"] !== SharingPermissions.None) return "globe-americas";
-        else if (this.props.Document.numGroupsShared || NumCast(this.props.Document.numUsersShared, 0) > 1) return "users";
-        else return "user";
+        if (this.props.Document['acl-Public'] !== SharingPermissions.None) return 'globe-americas';
+        else if (this.props.Document.numGroupsShared || NumCast(this.props.Document.numUsersShared, 0) > 1) return 'users';
+        else return 'user';
     }
 
     @undoBatch
-    hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true)
+    hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true);
     anchorPanelWidth = () => this.props.PanelWidth() || 1;
     anchorPanelHeight = () => this.props.PanelHeight() || 1;
     anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         switch (property) {
-            case StyleProp.ShowTitle: return "";
-            case StyleProp.PointerEvents: return "none";
-            case StyleProp.LinkSource: return this.props.Document;// pass the LinkSource to the LinkAnchorBox
-            default: return this.props.styleProvider?.(doc, props, property);
+            case StyleProp.ShowTitle:
+                return '';
+            case StyleProp.PointerEvents:
+                return 'none';
+            case StyleProp.LinkSource:
+                return this.props.Document; // pass the LinkSource to the LinkAnchorBox
+            default:
+                return this.props.styleProvider?.(doc, props, property);
         }
-    }
-    // We need to use allrelatedLinks to get not just links to the document as a whole, but links to 
-    // anchors that are not rendered as DocumentViews (marked as 'unrendered' with their 'annotationOn' set to this document).  e.g., 
+    };
+    // We need to use allrelatedLinks to get not just links to the document as a whole, but links to
+    // anchors that are not rendered as DocumentViews (marked as 'unrendered' with their 'annotationOn' set to this document).  e.g.,
     //     - PDF text regions are rendered as an Annotations without generating a DocumentView, '
     //     - RTF selections are rendered via Prosemirror and have a mark which contains the Document ID for the annotation link
     //     - and links to PDF/Web docs at a certain scroll location never create an explicit view.
     // For each of these, we create LinkAnchorBox's on the border of the DocumentView.
     @computed get directLinks() {
-        TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(link =>
-            Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
-            Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
-            ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) ||
-            ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc))
+        TraceMobx();
+        return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(
+            link =>
+                Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
+                Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
+                ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) ||
+                ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc))
         );
     }
-    @computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); }
-    @computed get allLinkEndpoints() {  // the small blue dots that mark the endpoints of links
+    @computed get allLinks() {
+        TraceMobx();
+        return LinkManager.Instance.getAllRelatedLinks(this.rootDoc);
+    }
+    @computed get allLinkEndpoints() {
+        // the small blue dots that mark the endpoints of links
         TraceMobx();
         if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
-        if (this.rootDoc.type=== DocumentType.PRES || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
+        if (this.rootDoc.type === DocumentType.PRES || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return null;
         const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
-        return filtered.map((link, i) =>
+        return filtered.map((link, i) => (
             <div className="documentView-anchorCont" key={link[Id]}>
-                <DocumentView {...this.props}
+                <DocumentView
+                    {...this.props}
                     isContentActive={returnFalse}
                     Document={link}
                     PanelWidth={this.anchorPanelWidth}
@@ -979,80 +1131,87 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     styleProvider={this.anchorStyleProvider}
                     removeDocument={this.hideLinkAnchor}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(link, this.rootDoc)}`)} />
-            </div >);
+                    LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(link, this.rootDoc)}`)}
+                />
+            </div>
+        ));
     }
 
     @action
     onPointerEnter = () => {
         const self = this;
-        const audioAnnos = DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]);
+        const audioAnnos = DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']);
         if (audioAnnos && audioAnnos.length && this._mediaState === 0) {
             const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
-            anno.data instanceof AudioField && new Howl({
-                src: [anno.data.url.href],
-                format: ["mp3"],
-                autoplay: true,
-                loop: false,
-                volume: 0.5,
-                onend: function () {
-                    runInAction(() => self._mediaState = 0);
-                }
-            });
+            anno.data instanceof AudioField &&
+                new Howl({
+                    src: [anno.data.url.href],
+                    format: ['mp3'],
+                    autoplay: true,
+                    loop: false,
+                    volume: 0.5,
+                    onend: function () {
+                        runInAction(() => (self._mediaState = 0));
+                    },
+                });
             this._mediaState = 1;
         }
-    }
+    };
     recordAudioAnnotation = () => {
         let gumStream: any;
         let recorder: any;
         const self = this;
-        navigator.mediaDevices.getUserMedia({
-            audio: true
-        }).then(function (stream) {
-            gumStream = stream;
-            recorder = new MediaRecorder(stream);
-            recorder.ondataavailable = async (e: any) => {
-                const [{ result }] = await Networking.UploadFilesToServer(e.data);
-                if (!(result instanceof Error)) {
-                    const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: "audio test", _width: 200, _height: 32 });
-                    audioDoc.treeViewExpandedView = "layout";
-                    const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"], listSpec(Doc));
-                    if (audioAnnos === undefined) {
-                        self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"] = new List([audioDoc]);
-                    } else {
-                        audioAnnos.push(audioDoc);
+        navigator.mediaDevices
+            .getUserMedia({
+                audio: true,
+            })
+            .then(function (stream) {
+                gumStream = stream;
+                recorder = new MediaRecorder(stream);
+                recorder.ondataavailable = async (e: any) => {
+                    const [{ result }] = await Networking.UploadFilesToServer(e.data);
+                    if (!(result instanceof Error)) {
+                        const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: 'audio test', _width: 200, _height: 32 });
+                        audioDoc.treeViewExpandedView = 'layout';
+                        const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'], listSpec(Doc));
+                        if (audioAnnos === undefined) {
+                            self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'] = new List([audioDoc]);
+                        } else {
+                            audioAnnos.push(audioDoc);
+                        }
                     }
-                }
-            };
-            runInAction(() => self._mediaState = 2);
-            recorder.start();
-            setTimeout(() => {
-                recorder.stop();
-                runInAction(() => self._mediaState = 0);
-                gumStream.getAudioTracks()[0].stop();
-            }, 5000);
-        });
-    }
+                };
+                runInAction(() => (self._mediaState = 2));
+                recorder.start();
+                setTimeout(() => {
+                    recorder.stop();
+                    runInAction(() => (self._mediaState = 0));
+                    gumStream.getAudioTracks()[0].stop();
+                }, 5000);
+            });
+    };
 
-    captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ":caption");
+    captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
     @computed get innards() {
         TraceMobx();
-        const ffscale = () => (this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1);
-        const showTitle = this.ShowTitle?.split(":")[0];
-        const showTitleHover = this.ShowTitle?.includes(":hover");
+        const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1;
+        const showTitle = this.ShowTitle?.split(':')[0];
+        const showTitleHover = this.ShowTitle?.includes(':hover');
         const showCaption = !this.props.hideCaptions && this.Document._viewType !== CollectionViewType.Carousel ? StrCast(this.layoutDoc._showCaption) : undefined;
-        const captionView = !showCaption ? (null) :
-            <div className="documentView-captionWrapper"
+        const captionView = !showCaption ? null : (
+            <div
+                className="documentView-captionWrapper"
                 style={{
-                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined,
+                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                     minWidth: 50 * ffscale(),
-                    maxHeight: `max(100%, ${20 * ffscale()}px)`
+                    maxHeight: `max(100%, ${20 * ffscale()}px)`,
                 }}>
-                <FormattedTextBox {...OmitKeys(this.props, ['children']).omit}
+                <FormattedTextBox
+                    {...OmitKeys(this.props, ['children']).omit}
                     yPadding={10}
                     xPadding={10}
                     fieldKey={showCaption}
-                    fontSize={12 * Math.max(1, 2 * ffscale() / 3)}
+                    fontSize={12 * Math.max(1, (2 * ffscale()) / 3)}
                     styleProvider={this.captionStyleProvider}
                     dontRegisterView={true}
                     noSidebar={true}
@@ -1060,192 +1219,272 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     isContentActive={this.isContentActive}
                     onClick={this.onClickFunc}
                 />
-            </div>;
-        const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
-        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
-            Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
-        const titleView = !showTitle ? (null) :
-            <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
-                position: this.headerMargin ? "relative" : "absolute",
-                height: this.titleHeight,
-                color: lightOrDark(background),
-                background,
-                pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined,
-            }}>
-                <EditableView ref={this._titleRef}
-                    contents={showTitle.split(";").map(field => field.trim()).map(field => targetDoc[field]?.toString()).join("\\")}
-                    display={"block"}
+            </div>
+        );
+        const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
+        const background = StrCast(
+            SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
+            Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)'
+        );
+        const titleView = !showTitle ? null : (
+            <div
+                className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`}
+                key="title"
+                style={{
+                    position: this.headerMargin ? 'relative' : 'absolute',
+                    height: this.titleHeight,
+                    color: lightOrDark(background),
+                    background,
+                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+                }}>
+                <EditableView
+                    ref={this._titleRef}
+                    contents={showTitle
+                        .split(';')
+                        .map(field => field.trim())
+                        .map(field => targetDoc[field]?.toString())
+                        .join('\\')}
+                    display={'block'}
                     fontSize={10}
-                    GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle}
+                    GetValue={() => (showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle)}
                     SetValue={undoBatch((input: string) => {
-                        if (input?.startsWith("#")) {
+                        if (input?.startsWith('#')) {
                             if (this.props.showTitle) {
                                 this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined;
                             } else {
-                                Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : "creationDate";
+                                Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : 'creationDate';
                             }
                         } else {
-                            var value = input.replace(new RegExp(showTitle + "="), "") as string | number;
-                            if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
-                            if (showTitle.includes("Date") || showTitle === "author") return true;
+                            var value = input.replace(new RegExp(showTitle + '='), '') as string | number;
+                            if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value);
+                            if (showTitle.includes('Date') || showTitle === 'author') return true;
                             Doc.SetInPlace(targetDoc, showTitle, value, true);
                         }
                         return true;
                     })}
                 />
-            </div>;
-        return this.props.hideTitle || (!showTitle && !showCaption) ?
-            this.contents :
-            <div className="documentView-styleWrapper" >
-                {!this.headerMargin ? <> {this.contents} {titleView} </> : <> {titleView} {this.contents} </>}
+            </div>
+        );
+        return this.props.hideTitle || (!showTitle && !showCaption) ? (
+            this.contents
+        ) : (
+            <div className="documentView-styleWrapper">
+                {!this.headerMargin ? (
+                    <>
+                        {' '}
+                        {this.contents} {titleView}{' '}
+                    </>
+                ) : (
+                    <>
+                        {' '}
+                        {titleView} {this.contents}{' '}
+                    </>
+                )}
                 {captionView}
-            </div>;
+            </div>
+        );
     }
     isHovering = () => this._isHovering;
     @observable _isHovering = false;
-    @observable _: string = "";
+    @observable _: string = '';
     @computed get renderDoc() {
         TraceMobx();
-        const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
+        const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
         const isButton = this.props.Document.type === DocumentType.FONTICON;
         if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || (this.hidden && !this.props.treeViewDoc)) return null;
-        return this.docContents ??
-            <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
-                id={this.props.Document[Id]}
-                onPointerEnter={action(() => this._isHovering = true)}
-                onPointerLeave={action(() => this._isHovering = false)}
-                style={{
-                    background: isButton || thumb ? undefined : this.backgroundColor,
-                    opacity: this.opacity,
-                    color: StrCast(this.layoutDoc.color, "inherit"),
-                    fontFamily: StrCast(this.Document._fontFamily, "inherit"),
-                    fontSize: Cast(this.Document._fontSize, "string", null),
-                    transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
-                    transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this._animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
-                }}>
-
-                {this.innards}
-                {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : (null)}
-                {this.widgetDecorations ?? null}
-            </div>;
+        return (
+            this.docContents ?? (
+                <div
+                    className={`documentView-node${this.topMost ? '-topmost' : ''}`}
+                    id={this.props.Document[Id]}
+                    onPointerEnter={action(() => (this._isHovering = true))}
+                    onPointerLeave={action(() => (this._isHovering = false))}
+                    style={{
+                        background: isButton || thumb ? undefined : this.backgroundColor,
+                        opacity: this.opacity,
+                        color: StrCast(this.layoutDoc.color, 'inherit'),
+                        fontFamily: StrCast(this.Document._fontFamily, 'inherit'),
+                        fontSize: Cast(this.Document._fontSize, 'string', null),
+                        transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
+                        transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this._animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? 'in' : 'out'}`,
+                    }}>
+                    {this.innards}
+                    {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : null}
+                    {this.widgetDecorations ?? null}
+                </div>
+            )
+        );
     }
     render() {
         TraceMobx();
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : Doc.DocBrushStatus.unbrushed) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange", "lightBlue"][highlightIndex];
-        const highlightStyle = ["solid", "dashed", "solid", "solid", "solid"][highlightIndex];
+        const highlightColor = ['transparent', 'rgb(68, 118, 247)', 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
+        const highlightStyle = ['solid', 'dashed', 'solid', 'solid', 'solid'][highlightIndex];
         const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
         let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
-        highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]";  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
+        highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== '[pres element template]'; // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
 
         const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined };
         const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
-        const boxShadow = this.props.treeViewDoc ? null : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
-            this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
-
-        // Return surrounding highlight 
-        return <div className={`${DocumentView.ROOT_DIV} docView-hack`} ref={this._mainCont}
-            onContextMenu={this.onContextMenu}
-            onKeyDown={this.onKeyDown}
-            onPointerDown={this.onPointerDown}
-            onClick={this.onClick}
-            onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
-            onPointerLeave={action(e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document))}
-            style={{
-                display: this.hidden ? "inline" : undefined,
-                borderRadius: this.borderRounding,
-                pointerEvents: this.pointerEvents,
-                outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
-                border: highlighting && this.borderRounding && highlightStyle === "dashed" ? `${highlightStyle} ${highlightColor} ${highlightIndex}px` : undefined,
-                boxShadow,
-                clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined
-            }}>
-            {!borderPath.path ? internal :
-                <>
-                    {/* <div style={{ clipPath: `path('${borderPath.fill}')` }}>
+        const boxShadow = this.props.treeViewDoc
+            ? null
+            : highlighting && this.borderRounding && highlightStyle !== 'dashed'
+            ? `0 0 0 ${highlightIndex}px ${highlightColor}`
+            : this.boxShadow || (this.props.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
+
+        // Return surrounding highlight
+        return (
+            <div
+                className={`${DocumentView.ROOT_DIV} docView-hack`}
+                ref={this._mainCont}
+                onContextMenu={this.onContextMenu}
+                onKeyDown={this.onKeyDown}
+                onPointerDown={this.onPointerDown}
+                onClick={this.onClick}
+                onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
+                onPointerLeave={action(e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document))}
+                style={{
+                    display: this.hidden ? 'inline' : undefined,
+                    borderRadius: this.borderRounding,
+                    pointerEvents: this.pointerEvents,
+                    outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : 'solid 0px',
+                    border: highlighting && this.borderRounding && highlightStyle === 'dashed' ? `${highlightStyle} ${highlightColor} ${highlightIndex}px` : undefined,
+                    boxShadow,
+                    clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined,
+                }}>
+                {!borderPath.path ? (
+                    internal
+                ) : (
+                    <>
+                        {/* <div style={{ clipPath: `path('${borderPath.fill}')` }}>
                         {internal}
                     </div> */}
-                    {internal}
-                    <div key="border2" className="documentView-customBorder" style={{ pointerEvents: "none" }} >
-                        <svg style={{ overflow: "visible" }} viewBox={`0 0 ${this.props.PanelWidth()} ${this.props.PanelHeight()}`}>
-                            <path d={borderPath.path} style={{ stroke: "black", fill: "transparent", strokeWidth: borderPath.width }} />
-                        </svg>
-                    </div>
-                </>
-            }
-            {this.showFilterIcon ?
-                <FontAwesomeIcon icon={"filter"} size="lg"
-                    style={{ position: 'absolute', top: '1%', right: '1%', cursor: "pointer", padding: 1, color: this.showFilterIcon === "hasFilter" ? '#18c718bd' : "orange", zIndex: 1 }}
-                    onPointerDown={action(e => { this.props.select(false); CurrentUserUtils.propertiesWidth = 250; e.stopPropagation(); })}
-                />
-                : (null)}
-        </div>;
+                        {internal}
+                        <div key="border2" className="documentView-customBorder" style={{ pointerEvents: 'none' }}>
+                            <svg style={{ overflow: 'visible' }} viewBox={`0 0 ${this.props.PanelWidth()} ${this.props.PanelHeight()}`}>
+                                <path d={borderPath.path} style={{ stroke: 'black', fill: 'transparent', strokeWidth: borderPath.width }} />
+                            </svg>
+                        </div>
+                    </>
+                )}
+                {this.showFilterIcon ? (
+                    <FontAwesomeIcon
+                        icon={'filter'}
+                        size="lg"
+                        style={{ position: 'absolute', top: '1%', right: '1%', cursor: 'pointer', padding: 1, color: this.showFilterIcon === 'hasFilter' ? '#18c718bd' : 'orange', zIndex: 1 }}
+                        onPointerDown={action(e => {
+                            this.props.select(false);
+                            SettingsManager.propertiesWidth = 250;
+                            e.stopPropagation();
+                        })}
+                    />
+                ) : null}
+            </div>
+        );
     }
 }
 
 @observer
 export class DocumentView extends React.Component<DocumentViewProps> {
-    public static ROOT_DIV = "documentView-effectsWrapper";
-    public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
+    public static ROOT_DIV = 'documentView-effectsWrapper';
+    public get displayName() {
+        return 'DocumentView(' + this.props.Document?.title + ')';
+    } // this makes mobx trace() statements more descriptive
     public ContentRef = React.createRef<HTMLDivElement>();
     private _disposers: { [name: string]: IReactionDisposer } = {};
 
     public static showBackLinks(linkSource: Doc) {
-        const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + "-pivotish";
+        const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish';
         DocServer.GetRefField(docid).then(docx => {
             const rootAlias = () => {
                 const rootAlias = Doc.MakeAlias(linkSource);
                 rootAlias.x = rootAlias.y = 0;
                 return rootAlias;
             };
-            const linkCollection = ((docx instanceof Doc) && docx) || Docs.Create.StackingDocument([/*rootAlias()*/], { title: linkSource.title + "-pivot", _width: 500, _height: 500, }, docid);
+            const linkCollection =
+                (docx instanceof Doc && docx) ||
+                Docs.Create.StackingDocument(
+                    [
+                        /*rootAlias()*/
+                    ],
+                    { title: linkSource.title + '-pivot', _width: 500, _height: 500 },
+                    docid
+                );
             linkCollection.linkSource = linkSource;
-            if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript("updateLinkCollection(self)");
+            if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript('updateLinkCollection(self)');
             LightboxView.SetLightboxDoc(linkCollection);
         });
     }
 
     @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; }
-    get dataDoc() { return this.docView?.dataDoc || this.Document; }
-    get finalLayoutKey() { return this.docView?.finalLayoutKey || "layout"; }
-    get ContentDiv() { return this.docView?.ContentDiv; }
-    get ComponentView() { return this.docView?._componentView; }
-    get allLinks() { return this.docView?.allLinks || []; }
-    get LayoutFieldKey() { return this.docView?.LayoutFieldKey || "layout"; }
-    get fitWidth() { return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth; }
-
-    @computed get docViewPath(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; }
-    @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); }
+    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;
+    }
+    get dataDoc() {
+        return this.docView?.dataDoc || this.Document;
+    }
+    get finalLayoutKey() {
+        return this.docView?.finalLayoutKey || 'layout';
+    }
+    get ContentDiv() {
+        return this.docView?.ContentDiv;
+    }
+    get ComponentView() {
+        return this.docView?._componentView;
+    }
+    get allLinks() {
+        return this.docView?.allLinks || [];
+    }
+    get LayoutFieldKey() {
+        return this.docView?.LayoutFieldKey || 'layout';
+    }
+    get fitWidth() {
+        return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth;
+    }
+
+    @computed get docViewPath(): DocumentView[] {
+        return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this];
+    }
+    @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.fitWidth));
+        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : 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.fitWidth));
+        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
     }
     @computed get shouldNotScale() {
-        return (this.fitWidth && !this.nativeWidth) ||
-            this.props.dontScaleFilter?.(this.Document) ||
-            this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+        return (this.fitWidth && !this.nativeWidth) || this.props.dontScaleFilter?.(this.Document) || this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+    }
+    @computed get effectiveNativeWidth() {
+        return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width);
+    }
+    @computed get effectiveNativeHeight() {
+        return this.shouldNotScale ? 0 : this.nativeHeight || NumCast(this.layoutDoc.height);
     }
-    @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); }
-    @computed get effectiveNativeHeight() { return this.shouldNotScale ? 0 : (this.nativeHeight || NumCast(this.layoutDoc.height)); }
     @computed get nativeScaling() {
         if (this.shouldNotScale) return 1;
         const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
         if (this.fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
-            return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1));  // width-limited or fitWidth
+            return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or fitWidth
         }
         return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled
     }
 
-    @computed get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); }
+    @computed get panelWidth() {
+        return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth();
+    }
     @computed get panelHeight() {
         if (this.effectiveNativeHeight && !this.layoutDoc.nativeHeightUnfrozen) {
             const scrollHeight = this.fitWidth ? Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight)) : 0;
@@ -1253,10 +1492,18 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
         return this.props.PanelHeight();
     }
-    @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.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (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.props.dontCenter?.includes("y") ? 0 : this.Yshift; }
+    @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.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (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.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);
@@ -1264,23 +1511,23 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
             return undefined;
         }
-        const xf = (this.docView?.props.ScreenToLocalTransform().scale(this.nativeScaling)).inverse();
+        const xf = this.docView?.props.ScreenToLocalTransform().scale(this.nativeScaling).inverse();
         const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
         if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
-            const docuBox = this.docView.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
+            const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
             if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined };
         }
         return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) };
-    }
+    };
 
     public iconify(finished?: () => void) {
         this.ComponentView?.updateIcon?.();
-        const layoutKey = Cast(this.Document.layoutKey, "string", null);
-        if (layoutKey !== "layout_icon") {
-            this.switchViews(true, "icon", finished);
-            if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") this.Document.deiconifyLayout = layoutKey.replace("layout_", "");
+        const layoutKey = Cast(this.Document.layoutKey, 'string', null);
+        if (layoutKey !== 'layout_icon') {
+            this.switchViews(true, 'icon', finished);
+            if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') this.Document.deiconifyLayout = layoutKey.replace('layout_', '');
         } else {
-            const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null);
+            const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
             this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finished);
             this.Document.deiconifyLayout = undefined;
             this.props.bringToFront(this.rootDoc);
@@ -1291,17 +1538,23 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     setCustomView = (custom: boolean, layout: string): void => {
         Doc.setNativeView(this.props.Document);
         custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
-    }
+    };
     switchViews = action((custom: boolean, view: string, finished?: () => void) => {
-        this.docView && (this.docView._animateScalingTo = 0.1);  // shrink doc
-        setTimeout(action(() => {
-            this.setCustomView(custom, view);
-            this.docView && (this.docView._animateScalingTo = 1); // expand it
-            setTimeout(action(() => {
-                this.docView && (this.docView._animateScalingTo = 0);
-                finished?.();
-            }), this.docView!._animateScaleTime - 10);
-        }), this.docView!._animateScaleTime - 10);
+        this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc
+        setTimeout(
+            action(() => {
+                this.setCustomView(custom, view);
+                this.docView && (this.docView._animateScalingTo = 1); // expand it
+                setTimeout(
+                    action(() => {
+                        this.docView && (this.docView._animateScalingTo = 0);
+                        finished?.();
+                    }),
+                    this.docView!._animateScaleTime - 10
+                );
+            }),
+            this.docView!._animateScaleTime - 10
+        );
     });
 
     startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource);
@@ -1315,7 +1568,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     PanelHeight = () => this.panelHeight;
     ContentScale = () => this.nativeScaling;
     selfView = () => this;
-    screenToLocalTransform = () =>this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).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,
@@ -1341,32 +1598,41 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
         const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES;
         const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
-        return (<div className="contentFittingDocumentView">
-            {!this.props.Document || !this.props.PanelWidth() ? (null) : (
-                <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
-                    style={{
-                        transition: this.props.dataTransition,
-                        position: this.props.Document.isInkMask ? "absolute" : undefined,
-                        transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                        width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
-                        height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
-                            `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
-                    }}>
-                    <DocumentViewInternal {...this.props}
-                        DocumentView={this.selfView}
-                        viewPath={this.docViewPathFunc}
-                        PanelWidth={this.PanelWidth}
-                        PanelHeight={this.PanelHeight}
-                        NativeWidth={this.NativeWidth}
-                        NativeHeight={this.NativeHeight}
-                        isSelected={this.isSelected}
-                        select={this.select}
-                        ContentScaling={this.ContentScale}
-                        ScreenToLocalTransform={this.screenToLocalTransform}
-                        focus={this.props.focus || emptyFunction}
-                        ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))} />
-                </div>)}
-        </div>);
+        return (
+            <div className="contentFittingDocumentView">
+                {!this.props.Document || !this.props.PanelWidth() ? null : (
+                    <div
+                        className="contentFittingDocumentView-previewDoc"
+                        ref={this.ContentRef}
+                        style={{
+                            transition: this.props.dataTransition,
+                            position: this.props.Document.isInkMask ? 'absolute' : undefined,
+                            transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
+                            width: isButton || isPresTreeElement ? '100%' : xshift() ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
+                            height:
+                                isButton || this.props.forceAutoHeight
+                                    ? undefined
+                                    : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
+                        }}>
+                        <DocumentViewInternal
+                            {...this.props}
+                            DocumentView={this.selfView}
+                            viewPath={this.docViewPathFunc}
+                            PanelWidth={this.PanelWidth}
+                            PanelHeight={this.PanelHeight}
+                            NativeWidth={this.NativeWidth}
+                            NativeHeight={this.NativeHeight}
+                            isSelected={this.isSelected}
+                            select={this.select}
+                            ContentScaling={this.ContentScale}
+                            ScreenToLocalTransform={this.screenToLocalTransform}
+                            focus={this.props.focus || emptyFunction}
+                            ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
+                        />
+                    </div>
+                )}
+            </div>
+        );
     }
 }
 
@@ -1380,7 +1646,7 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
 });
 
 ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
-    if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout");
+    if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
     else dv.switchViews(true, detailLayoutKeySuffix);
 });
 
@@ -1398,8 +1664,8 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
             alias.y = 0;
             alias._lockedPosition = false;
             wid += otherdoc[WidthSym]();
-            Doc.AddDocToList(Doc.GetProto(linkCollection), "data", alias);
+            Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', alias);
         }
     });
     return links;
-});
\ No newline at end of file
+});
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 17b57cb3b..ff04a293c 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -1,52 +1,80 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import Select from "react-select";
-import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from "../../../fields/Doc";
-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, DocCast } from "../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { UserOptions } from "../../util/GroupManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { CollectionTreeView } from "../collections/CollectionTreeView";
-import { CollectionView } from "../collections/CollectionView";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { EditableView } from "../EditableView";
-import { SearchBox } from "../search/SearchBox";
-import { DashboardToggleButton, DefaultStyleProvider, StyleProp } from "../StyleProvider";
-import { DocumentViewProps } from "./DocumentView";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import Select from 'react-select';
+import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
+import { Docs, DocumentOptions } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { UserOptions } from '../../util/GroupManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SelectionManager } from '../../util/SelectionManager';
+import { CollectionTreeView } from '../collections/CollectionTreeView';
+import { CollectionView } from '../collections/CollectionView';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { EditableView } from '../EditableView';
+import { SearchBox } from '../search/SearchBox';
+import { DashboardToggleButton, DefaultStyleProvider, StyleProp } from '../StyleProvider';
+import { DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import './FilterBox.scss';
-const higflyout = require("@hig/flyout");
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
 @observer
 export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
-
     constructor(props: Readonly<FieldViewProps>) {
         super(props);
         const targetDoc = FilterBox.targetDoc;
-        if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = FilterBox.createFilterDoc();
+    }
+
+    /// 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);
+    }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(FilterBox, fieldKey);
     }
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); }
 
     public _filterSelected = false;
-    public _filterMatch = "matched";
+    public _filterMatch = 'matched';
 
     @computed static get currentContainingCollectionDoc() {
         let docView: any = SelectionManager.Views()[0];
         let doc = docView.Document;
 
-        while (docView && doc && doc.type !== "collection") {
+        while (docView && doc && doc.type !== 'collection') {
             doc = docView.props.ContainingCollectionDoc;
             docView = docView.props.ContainingCollectionView;
         }
@@ -58,7 +86,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //  * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
     //  */
 
-
     // trying to get this to work rather than the version below this
 
     // @computed static get targetDoc() {
@@ -70,31 +97,33 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //     // return FilterBox._filterScope === "Current Collection" ? SelectionManager.Views()[0].Document || CollectionDockingView.Instance.props.Document : CollectionDockingView.Instance.props.Document;
     // }
 
-
     /**
-    * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
-    */
+     * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
+     */
     @computed static get targetDoc() {
-        return SelectionManager.Docs().length ? SelectionManager.Docs()[0] : CurrentUserUtils.ActiveDashboard;
+        return SelectionManager.Docs().length ? SelectionManager.Docs()[0] : Doc.ActiveDashboard;
     }
     @computed static get targetDocChildKey() {
         if (SelectionManager.Views().length) {
-            return SelectionManager.Views()[0].ComponentView?.annotationKey || SelectionManager.Views()[0].ComponentView?.fieldKey || "data";
+            return SelectionManager.Views()[0].ComponentView?.annotationKey || SelectionManager.Views()[0].ComponentView?.fieldKey || 'data';
         }
-        return "data";
+        return 'data';
     }
     @computed static get targetDocChildren() {
-        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard?.data);
+        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || Doc.ActiveDashboard?.data);
     }
 
     @observable _loaded = false;
     componentDidMount() {
-        reaction(() => DocListCastAsync(this.layoutDoc.data),
-            async (activeTabsAsync) => {
+        reaction(
+            () => DocListCastAsync(this.layoutDoc.data),
+            async activeTabsAsync => {
                 const activeTabs = await activeTabsAsync;
                 activeTabs && (await SearchBox.foreachRecursiveDocAsync(activeTabs, emptyFunction));
-                runInAction(() => this._loaded = true);
-            }, { fireImmediately: true });
+                runInAction(() => (this._loaded = true));
+            },
+            { fireImmediately: true }
+        );
     }
 
     @computed get allDocs() {
@@ -109,16 +138,18 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get _allFacets() {
         // trace();
-        const noviceReqFields = ["author", "tags", "text", "type"];
-        const noviceLayoutFields: string[] = [];//["_curPage"];
+        const noviceReqFields = ['author', 'tags', 'text', 'type'];
+        const noviceLayoutFields: string[] = []; //["_curPage"];
         const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
 
         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.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();
     }
 
-
     /**
      * The current attributes selected to filter based on
      */
@@ -136,26 +167,26 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     gatherFieldValues(childDocs: Doc[], facetKey: string) {
         const valueSet = new Set<string>();
         let rtFields = 0;
-        childDocs.forEach((d) => {
+        childDocs.forEach(d => {
             const facetVal = d[facetKey];
             if (facetVal instanceof RichTextField) rtFields++;
             valueSet.add(Field.toString(facetVal as Field));
             const fieldKey = Doc.LayoutFieldKey(d);
-            const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-            const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+            const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+            const data = d[annos ? fieldKey + '-annotations' : fieldKey];
             if (data !== undefined) {
                 let subDocs = DocListCast(data);
                 if (subDocs.length > 0) {
                     let newarray: Doc[] = [];
                     while (subDocs.length > 0) {
                         newarray = [];
-                        subDocs.forEach((t) => {
+                        subDocs.forEach(t => {
                             const facetVal = t[facetKey];
                             if (facetVal instanceof RichTextField) rtFields++;
                             facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
                             const fieldKey = Doc.LayoutFieldKey(t);
-                            const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
-                            DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+                            const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
+                            DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                         });
                         subDocs = newarray;
                     }
@@ -164,7 +195,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return { strings: Array.from(valueSet.keys()), rtFields };
     }
-    removeFilterDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false;
+    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;
         if (targetDoc) {
@@ -173,24 +204,24 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             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"));
+                const docFilter = Cast(targetDoc._docFilters, listSpec('string'));
                 if (docFilter) {
                     let index: number;
-                    while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                    while ((index = docFilter.findIndex(item => item.split(':')[0] === filterName)) !== -1) {
                         docFilter.splice(index, 1);
                     }
                 }
-                const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
+                const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec('string'));
                 if (docRangeFilters) {
                     let index: number;
-                    while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                    while ((index = docRangeFilters.findIndex(item => item.split(':')[0] === filterName)) !== -1) {
                         docRangeFilters.splice(index, 3);
                     }
                 }
             }
         }
         return true;
-    }
+    };
     /**
      * Responds to clicking the check box in the flyout menu
      */
@@ -205,7 +236,8 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const facetValues = this.gatherFieldValues(targetDocChildren, facetHeader);
 
             let nonNumbers = 0;
-            let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
+            let minVal = Number.MAX_VALUE,
+                maxVal = -Number.MAX_VALUE;
             facetValues.strings.map(val => {
                 const num = val ? Number(val) : Number.NaN;
                 if (Number.isNaN(num)) {
@@ -216,40 +248,56 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             });
             let newFacet: Opt<Doc>;
-            if (facetHeader === "text" || facetValues.rtFields / allCollectionDocs.length > 0.1) {
-                newFacet = Docs.Create.TextDocument("", {
-                    title: facetHeader, system: true, target: targetDoc, _width: 100, _height: 25, _stayInCollection: true,
-                    treeViewExpandedView: "layout", _treeViewOpen: true, _forceActive: true, ignoreClick: true,
+            if (facetHeader === 'text' || facetValues.rtFields / allCollectionDocs.length > 0.1) {
+                newFacet = Docs.Create.TextDocument('', {
+                    title: facetHeader,
+                    system: true,
+                    target: targetDoc,
+                    _width: 100,
+                    _height: 25,
+                    _stayInCollection: true,
+                    treeViewExpandedView: 'layout',
+                    _treeViewOpen: true,
+                    _forceActive: true,
+                    ignoreClick: true,
                 });
                 Doc.GetProto(newFacet).forceActive = true; // required for FormattedTextBox to be able to gain focus since it will never be 'selected'
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
                 newFacet._textBoxPaddingX = newFacet._textBoxPaddingY = 4;
                 const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`;
-                newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
-            } else if (facetHeader !== "tags" && nonNumbers / facetValues.strings.length < .1) {
+                newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: 'string' });
+            } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {
                 newFacet = Docs.Create.SliderDocument({
-                    title: facetHeader, system: true, target: targetDoc, _fitWidth: true, _height: 40, _stayInCollection: true,
-                    treeViewExpandedView: "layout", _treeViewOpen: true, _forceActive: true, _overflow: "visible",
+                    title: facetHeader,
+                    system: true,
+                    target: targetDoc,
+                    _fitWidth: true,
+                    _height: 40,
+                    _stayInCollection: true,
+                    treeViewExpandedView: 'layout',
+                    _treeViewOpen: true,
+                    _forceActive: true,
+                    _overflow: 'visible',
                 });
                 const newFacetField = Doc.LayoutFieldKey(newFacet);
                 const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
-                const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * .1));
-                const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * .05)));
-                newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
-                newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
-                Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
-                Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
+                const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
+                const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
+                newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0];
+                newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1];
+                Doc.GetProto(newFacet)[newFacetField + '-minThumb'] = extendedMinVal;
+                Doc.GetProto(newFacet)[newFacetField + '-maxThumb'] = extendedMaxVal;
                 const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`;
-                newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
+                newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: 'number' });
                 newFacet.data = ComputedField.MakeFunction(`readNumFacetData(self.target, self, "${FilterBox.targetDocChildKey}", "${facetHeader}")`);
             } else {
                 newFacet = new Doc();
                 newFacet.system = true;
                 newFacet.title = facetHeader;
                 newFacet.treeViewOpen = true;
-                newFacet.layout = CollectionView.LayoutString("data");
-                newFacet.layoutKey = "layout";
+                newFacet.layout = CollectionView.LayoutString('data');
+                newFacet.layoutKey = 'layout';
                 newFacet.type = DocumentType.COL;
                 newFacet.target = targetDoc;
                 newFacet.data = ComputedField.MakeFunction(`readFacetData(self.target, "${FilterBox.targetDocChildKey}", "${facetHeader}")`);
@@ -257,11 +305,11 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             newFacet.hideContextMenu = true;
             Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
         }
-    }
+    };
 
     @computed get scriptField() {
-        const scriptText = "setDocFilter(this?.target, heading, this.title, checked)";
-        const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
+        const scriptText = 'setDocFilter(this?.target, heading, this.title, checked)';
+        const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: 'string', checked: 'string', containingTreeView: Doc.name });
         return script ? () => script : undefined;
     }
 
@@ -271,7 +319,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     changeBool = (e: any) => {
         FilterBox.targetDoc && (DocCast(FilterBox.targetDoc.currentFilter).filterBoolean = e.currentTarget.value);
-    }
+    };
 
     /**
      * Changes whether to select matched or unmatched documents
@@ -279,7 +327,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     changeMatch = (e: any) => {
         this._filterMatch = e.currentTarget.value;
-    }
+    };
 
     @action
     changeSelected = () => {
@@ -290,36 +338,42 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this._filterSelected = true;
             // helper method to select specified docs
         }
-    }
+    };
 
     FilteringStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) {
-        switch (property.split(":")[0]) {
+        switch (property.split(':')[0]) {
             case StyleProp.Decorations:
                 if (doc && !doc.treeViewHideHeaderFields) {
-                    return <>
-                        <div style={{ marginRight: "5px", fontSize: "10px" }}>
-                            <select className="filterBox-selection">
-                                <option value="Is" key="Is">Is</option>
-                                <option value="Is Not" key="Is Not">Is Not</option>
-                            </select>
-                        </div>
-                        <div className="filterBox-treeView-close" onClick={e => this.removeFilter(StrCast(doc.title))}>
-                            <FontAwesomeIcon icon={"times"} size="sm" />
-                        </div>
-                    </>;
+                    return (
+                        <>
+                            <div style={{ marginRight: '5px', fontSize: '10px' }}>
+                                <select className="filterBox-selection">
+                                    <option value="Is" key="Is">
+                                        Is
+                                    </option>
+                                    <option value="Is Not" key="Is Not">
+                                        Is Not
+                                    </option>
+                                </select>
+                            </div>
+                            <div className="filterBox-treeView-close" onClick={e => this.removeFilter(StrCast(doc.title))}>
+                                <FontAwesomeIcon icon={'times'} size="sm" />
+                            </div>
+                        </>
+                    );
                 }
         }
         return DefaultStyleProvider(doc, props, property);
     }
 
-    suppressChildClick = () => ScriptField.MakeScript("")!;
+    suppressChildClick = () => ScriptField.MakeScript('')!;
 
     /**
      * Adds a filterDoc to the list of saved filters
      */
     saveFilter = () => {
-        Doc.AddDocToList(Doc.UserDoc(), "savedFilters", this.props.Document);
-    }
+        Doc.AddDocToList(Doc.UserDoc(), 'savedFilters', this.props.Document);
+    };
 
     /**
      * Changes the title of the filterDoc
@@ -327,116 +381,106 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     onTitleValueChange = (val: string) => {
         this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc?.title}`;
         return true;
-    }
+    };
 
     /**
      * The flyout from which you can select a saved filter to apply
      */
     @computed get flyoutPanel() {
         return DocListCast(Doc.UserDoc().savedFilters).map(doc => {
-            return <>
-                <div className="filterBox-tempFlyout" onWheel={e => e.stopPropagation()} style={{ height: 50, border: "2px" }} onPointerDown={() => this.props.updateFilterDoc?.(doc)}>
-                    {StrCast(doc.title)}
-                </div>
-            </>;
-        }
-        );
+            return (
+                <>
+                    <div className="filterBox-tempFlyout" onWheel={e => e.stopPropagation()} style={{ height: 50, border: '2px' }} onPointerDown={() => this.props.updateFilterDoc?.(doc)}>
+                        {StrCast(doc.title)}
+                    </div>
+                </>
+            );
+        });
     }
     setTreeHeight = (hgt: number) => {
         this.layoutDoc._height = NumCast(this.layoutDoc._autoHeightMargins) + 150; // need to add all the border sizes together.
-    }
+    };
 
     /**
      * add lock and hide button decorations for the "Dashboards" flyout TreeView
      */
     FilterStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
-        if (property.split(":")[0] === StyleProp.Decorations) {
-            return !doc || doc.treeViewHideHeaderFields ? (null) :
-                DashboardToggleButton(doc, "hidden", "trash", "trash", () => this.removeFilter(StrCast(doc.title)));
+        if (property.split(':')[0] === StyleProp.Decorations) {
+            return !doc || doc.treeViewHideHeaderFields ? null : DashboardToggleButton(doc, 'hidden', 'trash', 'trash', () => this.removeFilter(StrCast(doc.title)));
         }
         return this.props.styleProvider?.(doc, props, property);
-    }
+    };
 
     layoutHeight = () => this.layoutDoc[HeightSym]();
     render() {
         const facetCollection = this.props.Document;
 
         const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
-        return this.props.dontRegisterView ? (null) : <div className="filterBox-treeView" style={{ width: "100%" }}>
-
-            <div className="filterBox-title">
-                <EditableView
-                    key="editableView"
-                    contents={this.props.Document.title}
-                    height={24}
-                    fontSize={15}
-                    GetValue={() => StrCast(this.props.Document.title)}
-                    SetValue={this.onTitleValueChange}
-                />
-            </div>
+        return this.props.dontRegisterView ? null : (
+            <div className="filterBox-treeView" style={{ width: '100%' }}>
+                <div className="filterBox-title">
+                    <EditableView key="editableView" contents={this.props.Document.title} height={24} fontSize={15} GetValue={() => StrCast(this.props.Document.title)} SetValue={this.onTitleValueChange} />
+                </div>
 
-            <div className="filterBox-select-bool">
-                <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>
-                <div className="filterBox-select-text">filters together</div>
-            </div>
+                <div className="filterBox-select-bool">
+                    <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>
+                    <div className="filterBox-select-text">filters together</div>
+                </div>
 
-            <div className="filterBox-select">
-                <Select
-                    placeholder="Add a filter..."
-                    options={options}
-                    isMulti={false}
-                    onChange={val => this.facetClick((val as UserOptions).value)}
-                    onKeyDown={e => e.stopPropagation()}
-                    value={null}
-                    closeMenuOnSelect={true}
-                />
-            </div>
+                <div className="filterBox-select">
+                    <Select placeholder="Add a filter..." options={options} isMulti={false} onChange={val => this.facetClick((val as UserOptions).value)} onKeyDown={e => e.stopPropagation()} value={null} closeMenuOnSelect={true} />
+                </div>
 
-            <div className="filterBox-tree" key="tree">
-                <CollectionTreeView
-                    Document={facetCollection}
-                    DataDoc={Doc.GetProto(facetCollection)}
-                    fieldKey={this.props.fieldKey}
-                    CollectionView={undefined}
-                    disableDocBrushing={true}
-                    setHeight={this.setTreeHeight} // if the tree view can trigger the height of the filter box to change, then this needs to be filled in.
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    childDocumentsActive={returnTrue}
-                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                    ContainingCollectionView={this.props.ContainingCollectionView}
-                    PanelWidth={this.props.PanelWidth}
-                    PanelHeight={this.layoutHeight}
-                    rootSelected={this.props.rootSelected}
-                    renderDepth={1}
-                    dropAction={this.props.dropAction}
-                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                    isAnyChildContentActive={returnFalse}
-                    addDocTab={returnFalse}
-                    pinToPres={returnFalse}
-                    isSelected={returnFalse}
-                    select={returnFalse}
-                    bringToFront={emptyFunction}
-                    isContentActive={returnTrue}
-                    whenChildContentsActiveChanged={returnFalse}
-                    treeViewHideTitle={true}
-                    focus={returnFalse}
-                    onCheckedClick={this.scriptField}
-                    treeViewHideHeaderFields={false}
-                    dontRegisterView={true}
-                    styleProvider={this.FilterStyleProvider}
-                    docViewPath={this.props.docViewPath}
-                    scriptContext={this.props.scriptContext}
-                    moveDocument={returnFalse}
-                    removeDocument={this.removeFilterDoc}
-                    addDocument={returnFalse} />
-            </div>
-            <div className="filterBox-bottom">
-                {/* <div className="filterBox-select-matched">
+                <div className="filterBox-tree" key="tree">
+                    <CollectionTreeView
+                        Document={facetCollection}
+                        DataDoc={Doc.GetProto(facetCollection)}
+                        fieldKey={this.props.fieldKey}
+                        CollectionView={undefined}
+                        disableDocBrushing={true}
+                        setHeight={this.setTreeHeight} // if the tree view can trigger the height of the filter box to change, then this needs to be filled in.
+                        docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
+                        childDocumentsActive={returnTrue}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                        ContainingCollectionView={this.props.ContainingCollectionView}
+                        PanelWidth={this.props.PanelWidth}
+                        PanelHeight={this.layoutHeight}
+                        rootSelected={this.props.rootSelected}
+                        renderDepth={1}
+                        dropAction={this.props.dropAction}
+                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                        isAnyChildContentActive={returnFalse}
+                        addDocTab={returnFalse}
+                        pinToPres={returnFalse}
+                        isSelected={returnFalse}
+                        select={returnFalse}
+                        bringToFront={emptyFunction}
+                        isContentActive={returnTrue}
+                        whenChildContentsActiveChanged={returnFalse}
+                        treeViewHideTitle={true}
+                        focus={returnFalse}
+                        onCheckedClick={this.scriptField}
+                        treeViewHideHeaderFields={false}
+                        dontRegisterView={true}
+                        styleProvider={this.FilterStyleProvider}
+                        docViewPath={this.props.docViewPath}
+                        scriptContext={this.props.scriptContext}
+                        moveDocument={returnFalse}
+                        removeDocument={this.removeFilterDoc}
+                        addDocument={returnFalse}
+                    />
+                </div>
+                <div className="filterBox-bottom">
+                    {/* <div className="filterBox-select-matched">
                     <input className="filterBox-select-box" type="checkbox"
                         onChange={this.changeSelected} />
                     <div className="filterBox-select-text">select</div>
@@ -447,38 +491,35 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <div className="filterBox-select-text">documents</div>
                 </div> */}
 
-                <div style={{ display: "flex" }}>
-                    <div className="filterBox-saveWrapper">
-                        <div className="filterBox-saveBookmark"
-                            onPointerDown={this.saveFilter}
-                        >
-                            <div>SAVE</div>
+                    <div style={{ display: 'flex' }}>
+                        <div className="filterBox-saveWrapper">
+                            <div className="filterBox-saveBookmark" onPointerDown={this.saveFilter}>
+                                <div>SAVE</div>
+                            </div>
                         </div>
-                    </div>
-                    <div className="filterBox-saveWrapper">
-                        <div className="filterBox-saveBookmark">
-                            <Flyout className="myFilters-flyout" anchorPoint={anchorPoints.TOP} content={this.flyoutPanel}>
-                                <div>FILTERS</div>
-                            </Flyout>
+                        <div className="filterBox-saveWrapper">
+                            <div className="filterBox-saveBookmark">
+                                <Flyout className="myFilters-flyout" anchorPoint={anchorPoints.TOP} content={this.flyoutPanel}>
+                                    <div>FILTERS</div>
+                                </Flyout>
+                            </div>
                         </div>
-                    </div>
-                    <div className="filterBox-saveWrapper">
-                        <div className="filterBox-saveBookmark"
-                            onPointerDown={this.props.createNewFilterDoc}
-                        >
-                            <div>NEW</div>
+                        <div className="filterBox-saveWrapper">
+                            <div className="filterBox-saveBookmark" onPointerDown={this.props.createNewFilterDoc}>
+                                <div>NEW</div>
+                            </div>
                         </div>
                     </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 }
 
 ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
-    const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
+    const docFilters = Cast(layoutDoc._docFilters, listSpec('string'), []);
     for (const filter of docFilters) {
-        const fields = filter.split(":"); // split into key:value:modifiers
+        const fields = filter.split(':'); // split into key:value:modifiers
         if (fields[0] === facetHeader && fields[1] === facetValue) {
             return fields[2];
         }
@@ -490,11 +531,17 @@ ScriptingGlobals.add(function readNumFacetData(layoutDoc: Doc, facetDoc: Doc, ch
     const activeTabs = DocListCast(layoutDoc[childKey]);
     SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
     const set = new Set<string>();
-    if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
+    if (facetHeader === 'tags')
+        allCollectionDocs.forEach(child =>
+            Field.toString(child[facetHeader] as Field)
+                .split(':')
+                .forEach(key => set.add(key))
+        );
     else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
     const facetValues = Array.from(set).filter(v => v);
 
-    let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
+    let minVal = Number.MAX_VALUE,
+        maxVal = -Number.MAX_VALUE;
     facetValues.map(val => {
         const num = val ? Number(val) : Number.NaN;
         if (!Number.isNaN(num)) {
@@ -504,26 +551,31 @@ ScriptingGlobals.add(function readNumFacetData(layoutDoc: Doc, facetDoc: Doc, ch
     });
     const newFacetField = Doc.LayoutFieldKey(facetDoc);
     const ranged = Doc.readDocRangeFilter(layoutDoc, facetHeader);
-    const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * .1));
-    const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * .05)));
-    facetDoc[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
-    facetDoc[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
-    Doc.GetProto(facetDoc)[newFacetField + "-minThumb"] = extendedMinVal;
-    Doc.GetProto(facetDoc)[newFacetField + "-maxThumb"] = extendedMaxVal;
-})
+    const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
+    const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
+    facetDoc[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0];
+    facetDoc[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1];
+    Doc.GetProto(facetDoc)[newFacetField + '-minThumb'] = extendedMinVal;
+    Doc.GetProto(facetDoc)[newFacetField + '-maxThumb'] = extendedMaxVal;
+});
 ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
     const allCollectionDocs = new Set<Doc>();
     const activeTabs = DocListCast(layoutDoc[childKey]);
     SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
     const set = new Set<string>();
-    if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
+    if (facetHeader === 'tags')
+        allCollectionDocs.forEach(child =>
+            Field.toString(child[facetHeader] as Field)
+                .split(':')
+                .forEach(key => set.add(key))
+        );
     else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
     const facetValues = Array.from(set).filter(v => v);
 
     let nonNumbers = 0;
 
     facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
-    const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
+    const facetValueDocSet = (nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
         const doc = new Doc();
         doc.system = true;
         doc.title = facetValue.toString();
@@ -531,8 +583,8 @@ ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, fa
         doc.facetHeader = facetHeader;
         doc.facetValue = facetValue;
         doc.treeViewHideHeaderFields = true;
-        doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(self.target, self.facetHeader, self.facetValue)");
+        doc.treeViewChecked = ComputedField.MakeFunction('determineCheckedState(self.target, self.facetHeader, self.facetValue)');
         return doc;
     });
     return new List<Doc>(facetValueDocSet);
-});
\ No newline at end of file
+});
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 60eb48114..ffa839fcb 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,5 +1,5 @@
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import { extname } from 'path';
 import { DataSym, Doc, DocListCast, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -14,13 +14,12 @@ import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
-import { ContextMenu } from "../../views/ContextMenu";
+import { ContextMenu } from '../../views/ContextMenu';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
@@ -29,62 +28,64 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
 import { FaceRectangles } from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
-import "./ImageBox.scss";
-import React = require("react");
+import './ImageBox.scss';
+import React = require('react');
 
 export const pageSchema = createSchema({
-    googlePhotosUrl: "string",
-    googlePhotosTags: "string"
+    googlePhotosUrl: 'string',
+    googlePhotosTags: 'string',
 });
 const uploadIcons = {
-    idle: "downarrow.png",
-    loading: "loading.gif",
-    success: "greencheck.png",
-    failure: "redx.png"
+    idle: 'downarrow.png',
+    loading: 'loading.gif',
+    success: 'greencheck.png',
+    failure: 'redx.png',
 };
 
 @observer
 export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
+    protected _multiTouchDisposer?: import('../../util/InteractionUtils').InteractionUtils.MultiTouchEventDisposer | undefined;
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(ImageBox, fieldKey);
+    }
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
-    @observable _curSuffix = "";
+    @observable _curSuffix = '';
     @observable _uploadIcon = uploadIcons.idle;
 
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer?.();
         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
-    }
-    setViewSpec = (anchor: Doc, preview: boolean) => {
-
-    } // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-
+    };
+    setViewSpec = (anchor: Doc, preview: boolean) => {}; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
     getAnchor = () => {
         const anchor = this._getAnchor?.(this._savedAnnotations);
         anchor && this.addDocument(anchor);
         return anchor ?? this.rootDoc;
-    }
+    };
 
     componentDidMount() {
         this.props.setContentView?.(this); // bcz: do not remove this.  without it, stepping into an image in the lightbox causes an infinite loop....
-        this._disposers.sizer = reaction(() => (
-            {
+        this._disposers.sizer = reaction(
+            () => ({
                 forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
                 scrSize: this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth,
-                selected: this.props.isSelected()
+                selected: this.props.isSelected(),
             }),
-            ({ forceFull, scrSize, selected }) => this._curSuffix = this.fieldKey === "icon" ? "_m" : forceFull ? "_o" : scrSize < 0.25 ? "_s" : scrSize < 0.5 ? "_m" : scrSize < 0.8 || !selected ? "_l" : "_o",
-            { fireImmediately: true, delay: 1000 });
-        this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
+            ({ forceFull, scrSize, selected }) => (this._curSuffix = this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 || !selected ? '_l' : '_o'),
+            { fireImmediately: true, delay: 1000 }
+        );
+        this._disposers.path = reaction(
+            () => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
             ({ nativeSize, width }) => {
                 if (true || !this.layoutDoc._height) {
-                    this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth;
+                    this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth;
                 }
             },
-            { fireImmediately: true });
+            { fireImmediately: true }
+        );
     }
 
     componentWillUnmount() {
@@ -96,10 +97,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData) {
             if (de.metaKey) {
-                de.complete.docDragData.droppedDocuments.forEach(action((drop: Doc) => {
-                    Doc.AddDocToList(this.dataDoc, this.fieldKey + "-alternates", drop);
-                    e.stopPropagation();
-                }));
+                de.complete.docDragData.droppedDocuments.forEach(
+                    action((drop: Doc) => {
+                        Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
+                        e.stopPropagation();
+                    })
+                );
             } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
                 const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
                 const targetField = Doc.LayoutFieldKey(layoutDoc);
@@ -112,20 +115,20 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 }
             }
         }
-    }
+    };
 
     @undoBatch
-    resolution = () => this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes
+    resolution = () => (this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes);
 
     @undoBatch
     rotate = action(() => {
-        const nw = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
-        const nh = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
+        const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
+        const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
         const w = this.layoutDoc._width;
         const h = this.layoutDoc._height;
-        this.dataDoc[this.fieldKey + "-rotation"] = (NumCast(this.dataDoc[this.fieldKey + "-rotation"]) + 90) % 360;
-        this.dataDoc[this.fieldKey + "-nativeWidth"] = nh;
-        this.dataDoc[this.fieldKey + "-nativeHeight"] = nw;
+        this.dataDoc[this.fieldKey + '-rotation'] = (NumCast(this.dataDoc[this.fieldKey + '-rotation']) + 90) % 360;
+        this.dataDoc[this.fieldKey + '-nativeWidth'] = nh;
+        this.dataDoc[this.fieldKey + '-nativeHeight'] = nw;
         this.layoutDoc._width = h;
         this.layoutDoc._height = w;
     });
@@ -134,15 +137,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (!region) return;
         const cropping = Doc.MakeCopy(region, true);
         Doc.GetProto(region).lockedPosition = true;
-        Doc.GetProto(region).title = "region:" + this.rootDoc.title;
+        Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
         Doc.GetProto(region).isPushpin = true;
         this.addDocument(region);
         const anchx = NumCast(cropping.x);
         const anchy = NumCast(cropping.y);
         const anchw = NumCast(cropping._width);
         const anchh = NumCast(cropping._height);
-        const viewScale = NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]) / anchw;
-        cropping.title = "crop: " + this.rootDoc.title;
+        const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw;
+        cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
         cropping._width = anchw * (this.props.scaling?.() || 1);
@@ -153,70 +156,70 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         croppingProto.isPrototype = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
-        croppingProto.layout = ImageBox.LayoutString("data");
+        croppingProto.layout = ImageBox.LayoutString('data');
         croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
-        croppingProto["data-nativeWidth"] = anchw;
-        croppingProto["data-nativeHeight"] = anchh;
+        croppingProto['data-nativeWidth'] = anchw;
+        croppingProto['data-nativeHeight'] = anchh;
         croppingProto.viewScale = viewScale;
         croppingProto.viewScaleMin = viewScale;
         croppingProto.panX = anchx / viewScale;
         croppingProto.panY = anchy / viewScale;
-        croppingProto.panXMin = (anchx) / viewScale;
-        croppingProto.panXMax = (anchw) / viewScale;
-        croppingProto.panYMin = (anchy) / viewScale;
-        croppingProto.panYMax = (anchh) / viewScale;
+        croppingProto.panXMin = anchx / viewScale;
+        croppingProto.panXMax = anchw / viewScale;
+        croppingProto.panYMin = anchy / viewScale;
+        croppingProto.panYMax = anchh / viewScale;
         if (addCrop) {
-            DocUtils.MakeLink({ doc: region }, { doc: cropping }, "cropped image", "");
+            DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
         }
         this.props.bringToFront(cropping);
         return cropping;
-    }
+    };
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const field = Cast(this.dataDoc[this.fieldKey], ImageField);
         if (field) {
             const funcs: ContextMenuProps[] = [];
-            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" });
+            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.noviceMode) {
-                funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
+                funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });
 
-                const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers...");
-                const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
-                modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
-                modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
+                const existingAnalyze = ContextMenu.Instance?.findByDescription('Analyzers...');
+                const modes: ContextMenuProps[] = existingAnalyze && 'subitems' in existingAnalyze ? existingAnalyze.subitems : [];
+                modes.push({ description: 'Generate Tags', event: this.generateMetadata, icon: 'tag' });
+                modes.push({ description: 'Find Faces', event: this.extractFaces, icon: 'camera' });
                 //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" });
-                !existingAnalyze && ContextMenu.Instance?.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
+                !existingAnalyze && ContextMenu.Instance?.addItem({ description: 'Analyzers...', subitems: modes, icon: 'hand-point-right' });
             }
 
-            ContextMenu.Instance?.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+            ContextMenu.Instance?.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
         }
-    }
+    };
 
     extractFaces = () => {
         const converter = (results: any) => {
             return results.map((face: CognitiveServices.Image.Face) => Doc.Get.FromJson({ data: face, title: `Face: ${face.faceId}` })!);
         };
-        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-faces"], this.url, Service.Face, converter);
-    }
+        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + '-faces'], this.url, Service.Face, converter);
+    };
 
     generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
         const converter = (results: any) => {
-            const tagDoc = new Doc;
+            const tagDoc = new Doc();
             const tagsList = new List();
             results.tags.map((tag: Tag) => {
                 tagsList.push(tag.name);
-                const sanitized = tag.name.replace(" ", "_");
+                const sanitized = tag.name.replace(' ', '_');
                 tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
             });
-            this.dataDoc[this.fieldKey + "-generatedTags"] = tagsList;
-            tagDoc.title = "Generated Tags Doc";
+            this.dataDoc[this.fieldKey + '-generatedTags'] = tagsList;
+            tagDoc.title = 'Generated Tags Doc';
             tagDoc.confidence = threshold;
             return tagDoc;
         };
-        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-generatedTagsDoc"], this.url, Service.ComputerVision, converter);
-    }
+        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + '-generatedTagsDoc'], this.url, Service.ComputerVision, converter);
+    };
 
     @computed private get url() {
         const data = Cast(this.dataDoc[this.fieldKey], ImageField);
@@ -225,9 +228,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     choosePath(url: URL) {
         const lower = url.href.toLowerCase();
-        if (url.protocol === "data") return url.href;
+        if (url.protocol === 'data') return url.href;
         if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
-        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return url.href;  //Why is this here
+        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return url.href; //Why is this here
 
         const ext = extname(url.href);
         return url.href.replace(ext, this._curSuffix + ext);
@@ -235,40 +238,36 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     considerGooglePhotosLink = () => {
         const remoteUrl = this.dataDoc.googlePhotosUrl;
-        return !remoteUrl ? (null) : (<img draggable={false}
-            style={{ transformOrigin: "bottom right" }}
-            id={"google-photos"}
-            src={"/assets/google_photos.png"}
-            onClick={() => window.open(remoteUrl)}
-        />);
-    }
+        return !remoteUrl ? null : <img draggable={false} style={{ transformOrigin: 'bottom right' }} id={'google-photos'} src={'/assets/google_photos.png'} onClick={() => window.open(remoteUrl)} />;
+    };
 
     considerGooglePhotosTags = () => {
         const tags = this.dataDoc.googlePhotosTags;
-        return !tags ? (null) : (<img id={"google-tags"} src={"/assets/google_tags.png"} />);
-    }
+        return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
+    };
 
     @computed
     private get considerDownloadIcon() {
         const data = this.dataDoc[this.fieldKey];
         if (!(data instanceof ImageField)) {
-            return (null);
+            return null;
         }
         const primary = data.url.href;
         if (primary.includes(window.location.origin)) {
-            return (null);
+            return null;
         }
         return (
             <img
-                id={"upload-icon"} draggable={false}
-                style={{ transformOrigin: "bottom right" }}
+                id={'upload-icon'}
+                draggable={false}
+                style={{ transformOrigin: 'bottom right' }}
                 src={`/assets/${this._uploadIcon}`}
                 onClick={async () => {
                     const { dataDoc } = this;
                     const { success, failure, idle, loading } = uploadIcons;
-                    runInAction(() => this._uploadIcon = loading);
-                    const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] });
-                    dataDoc[this.props.fieldKey + "-originalUrl"] = primary;
+                    runInAction(() => (this._uploadIcon = loading));
+                    const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [primary] });
+                    dataDoc[this.props.fieldKey + '-originalUrl'] = primary;
                     let succeeded = true;
                     let data: ImageField | undefined;
                     try {
@@ -276,13 +275,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     } catch {
                         succeeded = false;
                     }
-                    runInAction(() => this._uploadIcon = succeeded ? success : failure);
-                    setTimeout(action(() => {
-                        this._uploadIcon = idle;
-                        if (data) {
-                            dataDoc[this.fieldKey] = data;
-                        }
-                    }), 2000);
+                    runInAction(() => (this._uploadIcon = succeeded ? success : failure));
+                    setTimeout(
+                        action(() => {
+                            this._uploadIcon = idle;
+                            if (data) {
+                                dataDoc[this.fieldKey] = data;
+                            }
+                        }),
+                        2000
+                    );
                 }}
             />
         );
@@ -290,18 +292,21 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @computed get nativeSize() {
         TraceMobx();
-        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"], 500));
-        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"], 1));
-        const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + "-nativeOrientation"], 1);
+        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth'], 500));
+        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight'], 1));
+        const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '-nativeOrientation'], 1);
         return { nativeWidth, nativeHeight, nativeOrientation };
     }
 
     @computed get paths() {
         const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
-        const alts = DocListCast(this.dataDoc[this.fieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
-        const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+        const alts = DocListCast(this.dataDoc[this.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
+        const altpaths = alts
+            .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
+            .filter(url => url)
+            .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
-        return paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+        return paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
     }
 
     @computed get content() {
@@ -310,39 +315,35 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const srcpath = this.paths[0];
         const fadepath = this.paths[Math.min(1, this.paths.length - 1)];
         const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
-        const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]);
+        const rotation = NumCast(this.dataDoc[this.fieldKey + '-rotation']);
         const aspect = rotation % 180 ? nativeHeight / nativeWidth : 1;
-        let transformOrigin = "center center";
+        let transformOrigin = 'center center';
         let transform = `translate(0%, 0%) rotate(${rotation}deg) scale(${aspect})`;
         if (rotation === 90 || rotation === -270) {
-            transformOrigin = "top left";
+            transformOrigin = 'top left';
             transform = `translate(100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
         } else if (rotation === 180) {
             transform = `rotate(${rotation}deg) scale(${aspect})`;
         } else if (rotation === 270 || rotation === -90) {
-            transformOrigin = "right top";
+            transformOrigin = 'right top';
             transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
         }
 
-        return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
-            <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? "auto" : undefined }} >
-                <img key="paths"
-                    src={srcpath}
-                    style={{ transform, transformOrigin }}
-                    draggable={false}
-                    width={nativeWidth} />
-                {fadepath === srcpath ? (null) :
-                    <div className={`imageBox-fadeBlocker${this.props.isHovering?.() ? "-hover" : ""}`}>
-                        <img className="imageBox-fadeaway" key={"fadeaway"}
-                            src={fadepath}
-                            style={{ transform, transformOrigin }} draggable={false}
-                            width={nativeWidth} />
-                    </div>}
+        return (
+            <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
+                <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? 'auto' : undefined }}>
+                    <img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
+                    {fadepath === srcpath ? null : (
+                        <div className={`imageBox-fadeBlocker${this.props.isHovering?.() ? '-hover' : ''}`}>
+                            <img className="imageBox-fadeaway" key={'fadeaway'} src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
+                        </div>
+                    )}
+                </div>
+                {this.considerDownloadIcon}
+                {this.considerGooglePhotosLink()}
+                <FaceRectangles document={this.dataDoc} color={'#0000FF'} backgroundColor={'#0000FF'} />
             </div>
-            {this.considerDownloadIcon}
-            {this.considerGooglePhotosLink()}
-            <FaceRectangles document={this.dataDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
-        </div>;
+        );
     }
 
     screenToLocalTransform = this.props.ScreenToLocalTransform;
@@ -357,66 +358,79 @@ 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.ActiveTool)) {
-            setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                this._marqueeing = [e.clientX, e.clientY];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        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(Doc.ActiveTool)) {
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
         }
-    }
+    };
     @action
     finishMarquee = () => {
         this._getAnchor = AnchorMenu.Instance?.GetAnchor;
         this._marqueeing = undefined;
         this.props.select(false);
-    }
+    };
     savedAnnotations = () => this._savedAnnotations;
 
     render() {
         TraceMobx();
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
-        const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
-        return (<div className="imageBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
-            style={{
-                width: this.props.PanelWidth() ? undefined : `100%`,
-                height: this.props.PanelWidth() ? undefined : `100%`,
-                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
-                borderRadius
-            }} >
-            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                renderDepth={this.props.renderDepth + 1}
-                fieldKey={this.annotationKey}
-                CollectionView={undefined}
-                isAnnotationOverlay={true}
-                annotationLayerHostsContent={true}
-                PanelWidth={this.props.PanelWidth}
-                PanelHeight={this.props.PanelHeight}
-                ScreenToLocalTransform={this.screenToLocalTransform}
-                select={emptyFunction}
-                scaling={returnOne}
-                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                removeDocument={this.removeDocument}
-                moveDocument={this.moveDocument}
-                addDocument={this.addDocument}>
-                {this.contentFunc}
-            </CollectionFreeFormView>
-            {this.annotationLayer}
-            {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                <MarqueeAnnotator
-                    rootDoc={this.rootDoc}
-                    scrollTop={0}
-                    down={this._marqueeing}
-                    scaling={this.props.scaling}
-                    docView={this.props.docViewPath().slice(-1)[0]}
-                    addDocument={this.addDocument}
-                    finishMarquee={this.finishMarquee}
-                    savedAnnotations={this.savedAnnotations}
-                    annotationLayer={this._annotationLayer.current}
-                    mainCont={this._mainCont.current}
-                    anchorMenuCrop={this.crop}
-                />}
-        </div >);
+        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
+        return (
+            <div
+                className="imageBox"
+                onContextMenu={this.specificContextMenu}
+                ref={this._mainCont}
+                style={{
+                    width: this.props.PanelWidth() ? undefined : `100%`,
+                    height: this.props.PanelWidth() ? undefined : `100%`,
+                    pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
+                    borderRadius,
+                }}>
+                <CollectionFreeFormView
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                    renderDepth={this.props.renderDepth + 1}
+                    fieldKey={this.annotationKey}
+                    CollectionView={undefined}
+                    isAnnotationOverlay={true}
+                    annotationLayerHostsContent={true}
+                    PanelWidth={this.props.PanelWidth}
+                    PanelHeight={this.props.PanelHeight}
+                    ScreenToLocalTransform={this.screenToLocalTransform}
+                    select={emptyFunction}
+                    scaling={returnOne}
+                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                    removeDocument={this.removeDocument}
+                    moveDocument={this.moveDocument}
+                    addDocument={this.addDocument}>
+                    {this.contentFunc}
+                </CollectionFreeFormView>
+                {this.annotationLayer}
+                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                    <MarqueeAnnotator
+                        rootDoc={this.rootDoc}
+                        scrollTop={0}
+                        down={this._marqueeing}
+                        scaling={this.props.scaling}
+                        docView={this.props.docViewPath().slice(-1)[0]}
+                        addDocument={this.addDocument}
+                        finishMarquee={this.finishMarquee}
+                        savedAnnotations={this.savedAnnotations}
+                        annotationLayer={this._annotationLayer.current}
+                        mainCont={this._mainCont.current}
+                        anchorMenuCrop={this.crop}
+                    />
+                )}
+            </div>
+        );
     }
-
 }
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 7fd289a97..85a8622ec 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,30 +1,31 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc } from "../../../fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../fields/Doc';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
-import { DragManager } from "../../util/DragManager";
-import { LinkManager } from "../../util/LinkManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { LinkEditor } from "../linking/LinkEditor";
-import { StyleProp } from "../StyleProvider";
-import { FieldView, FieldViewProps } from "./FieldView";
-import "./LinkAnchorBox.scss";
-import { LinkDocPreview } from "./LinkDocPreview";
-import React = require("react");
-const higflyout = require("@hig/flyout");
+import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
+import { SelectionManager } from '../../util/SelectionManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { LinkEditor } from '../linking/LinkEditor';
+import { StyleProp } from '../StyleProvider';
+import { FieldView, FieldViewProps } from './FieldView';
+import './LinkAnchorBox.scss';
+import { LinkDocPreview } from './LinkDocPreview';
+import React = require('react');
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
-
 @observer
 export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(LinkAnchorBox, fieldKey);
+    }
     _doubleTap = false;
     _lastTap: number = 0;
     _ref = React.createRef<HTMLDivElement>();
@@ -38,7 +39,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     onPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, false);
-    }
+    };
     onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
         const cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
         if (!this._isOpen && cdiv) {
@@ -47,20 +48,20 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
             if (separation > 100) {
                 const dragData = new DragManager.DocumentDragData([this.rootDoc]);
-                dragData.dropAction = "alias";
-                dragData.removeDropProperties = ["anchor1_x", "anchor1_y", "anchor2_x", "anchor2_y", "isLinkButton"];
+                dragData.dropAction = 'alias';
+                dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'isLinkButton'];
                 DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
                 return true;
             } else {
-                this.rootDoc[this.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
-                this.rootDoc[this.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
+                this.rootDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
+                this.rootDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
             }
         }
         return false;
     });
     @action
     onClick = (e: React.MouseEvent) => {
-        if ((e.button === 2 || e.ctrlKey || !this.layoutDoc.isLinkButton)) {
+        if (e.button === 2 || e.ctrlKey || !this.layoutDoc.isLinkButton) {
             this.props.select(false);
         }
         if (!this._doubleTap && !e.ctrlKey && e.button < 2) {
@@ -68,10 +69,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this._editing = true;
             anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
             if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
-                this._timeout = setTimeout(action(() => {
-                    LinkManager.FollowLink(this.rootDoc, anchorContainerDoc, this.props, false);
-                    this._editing = false;
-                }), 300 - (Date.now() - this._lastTap));
+                this._timeout = setTimeout(
+                    action(() => {
+                        LinkFollower.FollowLink(this.rootDoc, anchorContainerDoc, this.props, false);
+                        this._editing = false;
+                    }),
+                    300 - (Date.now() - this._lastTap)
+                );
                 e.stopPropagation();
             }
         } else {
@@ -81,17 +85,17 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.openLinkEditor(e);
             e.stopPropagation();
         }
-    }
+    };
 
     openLinkDocOnRight = (e: React.MouseEvent) => {
-        this.props.addDocTab(this.rootDoc, "add:right");
-    }
+        this.props.addDocTab(this.rootDoc, 'add:right');
+    };
     openLinkTargetOnRight = (e: React.MouseEvent) => {
         const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
         alias._isLinkButton = undefined;
-        alias.layoutKey = "layout";
-        this.props.addDocTab(alias, "add:right");
-    }
+        alias.layoutKey = 'layout';
+        this.props.addDocTab(alias, 'add:right');
+    };
     @action
     openLinkEditor = action((e: React.MouseEvent) => {
         SelectionManager.DeselectAll();
@@ -100,56 +104,67 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: "Open Link Target on Right", event: () => this.openLinkTargetOnRight(e), icon: "eye" });
-        funcs.push({ description: "Open Link on Right", event: () => this.openLinkDocOnRight(e), icon: "eye" });
-        funcs.push({ description: "Open Link Editor", event: () => this.openLinkEditor(e), icon: "eye" });
-        funcs.push({ description: "Toggle Always Show Link", event: () => this.props.Document.linkDisplay = !this.props.Document.linkDisplay, icon: "eye" });
+        funcs.push({ description: 'Open Link Target on Right', event: () => this.openLinkTargetOnRight(e), icon: 'eye' });
+        funcs.push({ description: 'Open Link on Right', event: () => this.openLinkDocOnRight(e), icon: 'eye' });
+        funcs.push({ description: 'Open Link Editor', event: () => this.openLinkEditor(e), icon: 'eye' });
+        funcs.push({ description: 'Toggle Always Show Link', event: () => (this.props.Document.linkDisplay = !this.props.Document.linkDisplay), icon: 'eye' });
 
-        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
-    }
+        ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
+    };
 
     render() {
         TraceMobx();
         const small = this.props.PanelWidth() <= 1; // this happens when rendered in a treeView
-        const x = NumCast(this.rootDoc[this.fieldKey + "_x"], 100);
-        const y = NumCast(this.rootDoc[this.fieldKey + "_y"], 100);
+        const x = NumCast(this.rootDoc[this.fieldKey + '_x'], 100);
+        const y = NumCast(this.rootDoc[this.fieldKey + '_y'], 100);
         const linkSource = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
-        const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ":anchor");
-        const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1";
-        const anchorScale = !this.dataDoc[this.fieldKey + "-useLinkSmallAnchor"] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .25;
+        const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor');
+        const anchor = this.fieldKey === 'anchor1' ? 'anchor2' : 'anchor1';
+        const anchorScale = !this.dataDoc[this.fieldKey + '-useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
 
         const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
         const flyout = (
             <div className="linkAnchorBoxBox-flyout" title=" " onPointerOver={() => Doc.UnBrushDoc(this.rootDoc)}>
-                <LinkEditor sourceDoc={Cast(this.dataDoc[this.fieldKey], Doc, null)} hideback={true} linkDoc={this.rootDoc} showLinks={action(() => { })} />
-                {!this._forceOpen ? (null) : <div className="linkAnchorBox-linkCloser" onPointerDown={action(() => this._isOpen = this._editing = this._forceOpen = false)}>
-                    <FontAwesomeIcon color="dimgray" icon={"times"} size={"sm"} />
-                </div>}
+                <LinkEditor sourceDoc={Cast(this.dataDoc[this.fieldKey], Doc, null)} hideback={true} linkDoc={this.rootDoc} showLinks={action(() => {})} />
+                {!this._forceOpen ? null : (
+                    <div className="linkAnchorBox-linkCloser" onPointerDown={action(() => (this._isOpen = this._editing = this._forceOpen = false))}>
+                        <FontAwesomeIcon color="dimgray" icon={'times'} size={'sm'} />
+                    </div>
+                )}
+            </div>
+        );
+        return (
+            <div
+                className={`linkAnchorBox-cont${small ? '-small' : ''}`}
+                onPointerLeave={LinkDocPreview.Clear}
+                onPointerEnter={e =>
+                    LinkDocPreview.SetLinkInfo({
+                        docProps: this.props,
+                        linkSrc: linkSource,
+                        linkDoc: this.rootDoc,
+                        showHeader: true,
+                        location: [e.clientX, e.clientY + 20],
+                    })
+                }
+                onPointerDown={this.onPointerDown}
+                onClick={this.onClick}
+                title={targetTitle}
+                onContextMenu={this.specificContextMenu}
+                ref={this._ref}
+                style={{
+                    background,
+                    left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
+                    top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
+                    transform: `scale(${anchorScale})`,
+                }}>
+                {!this._editing && !this._forceOpen ? null : (
+                    <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} open={this._forceOpen ? true : undefined} onOpen={() => (this._isOpen = true)} onClose={action(() => (this._isOpen = this._forceOpen = this._editing = false))}>
+                        <span className="linkAnchorBox-button">
+                            <FontAwesomeIcon icon={'eye'} size={'lg'} />
+                        </span>
+                    </Flyout>
+                )}
             </div>
         );
-        return <div className={`linkAnchorBox-cont${small ? "-small" : ""}`}
-            onPointerLeave={LinkDocPreview.Clear}
-            onPointerEnter={e => LinkDocPreview.SetLinkInfo({
-                docProps: this.props,
-                linkSrc: linkSource,
-                linkDoc: this.rootDoc,
-                showHeader: true,
-                location: [e.clientX, e.clientY + 20]
-            })}
-            onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
-            ref={this._ref}
-            style={{
-                background,
-                left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
-                top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
-                transform: `scale(${anchorScale})`
-            }} >
-            {!this._editing && !this._forceOpen ? (null) :
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} open={this._forceOpen ? true : undefined} onOpen={() => this._isOpen = true} onClose={action(() => this._isOpen = this._forceOpen = this._editing = false)}>
-                    <span className="linkAnchorBox-button" >
-                        <FontAwesomeIcon icon={"eye"} size={"lg"} />
-                    </span>
-                </Flyout>}
-        </div>;
     }
 }
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 47d5c662e..c2fb5d4ec 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -16,6 +16,7 @@ import './LinkDocPreview.scss';
 import React = require('react');
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
 
 interface LinkDocPreviewProps {
     linkDoc?: Doc;
@@ -145,7 +146,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     followLink = (e: React.PointerEvent) => {
         if (this._linkDoc && this._linkSrc) {
             LinkDocPreview.Clear();
-            LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
+            LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
         } else if (this.props.hrefs?.length) {
             this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), 'add:right');
         }
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 18a6b5453..bf4c029b2 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -10,7 +10,6 @@ import { NumCast, StrCast } from '../../../../fields/Types';
 import { TraceMobx } from '../../../../fields/util';
 import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
@@ -491,7 +490,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.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -591,7 +590,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     moveDocument={this.moveDocument}
         //     addDocument={this.sidebarAddDocument}
         //     childPointerEvents={true}
-        //     pointerEvents={CurrentUserUtils.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
+        //     pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="mapBox" ref={this._ref}>
                 {/*console.log(apiKey)*/}
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index bdf0f9d64..72569135b 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -1,19 +1,17 @@
 import { InfoWindow } from '@react-google-maps/api';
 import { action, computed } from 'mobx';
-import { observer } from "mobx-react";
-import * as React from "react";
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { emptyFunction, OmitKeys, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { CollectionStackingView } from '../../collections/CollectionStackingView';
-import { CollectionViewType } from '../../collections/CollectionView';
 import { ViewBoxAnnotatableProps } from '../../DocComponent';
 import { FieldViewProps } from '../FieldView';
 import { FormattedTextBox } from '../formattedText/FormattedTextBox';
-import "./MapBox.scss";
-
+import './MapBox.scss';
 
 interface MapBoxInfoWindowProps {
     place: Doc;
@@ -22,69 +20,74 @@ interface MapBoxInfoWindowProps {
     isAnyChildContentActive: () => boolean;
 }
 @observer
-export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps>{
-
+export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
     @action
     private handleInfoWindowClose = () => {
         if (this.props.place.infoWindowOpen) {
             this.props.place.infoWindowOpen = false;
         }
         this.props.place.infoWindowOpen = false;
-    }
+    };
 
     addNoteClick = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
-            const newBox = Docs.Create.TextDocument("Note", { _autoHeight: true });
-            FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
-            Doc.AddDocToList(this.props.place, "data", newBox);
+            const newBox = Docs.Create.TextDocument('Note', { _autoHeight: true });
+            FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+            Doc.AddDocToList(this.props.place, 'data', newBox);
             this._stack?.scrollToBottom();
             e.stopPropagation();
             e.preventDefault();
         });
-    }
-
+    };
 
     _stack: CollectionStackingView | null | undefined;
-    childFitWidth = (doc:Doc) => doc.type === DocumentType.RTF;
-    addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, "data", d), true as boolean);
-    removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, "data", d), true as boolean);
+    childFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
+    addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
+    removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
     render() {
-        return <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose} >
-            <div className="mapbox-infowindow">
-                <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
-                    <CollectionStackingView
-                        ref={r => this._stack = r}
-                        {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                        Document={this.props.place}
-                        DataDoc={undefined}
-                        fieldKey="data"
-                        CollectionView={undefined}
-                        NativeWidth={returnZero}
-                        NativeHeight={returnZero}
-                        docFilters={returnEmptyFilter}
-                        setHeight={emptyFunction}
-                        isAnnotationOverlay={false}
-                        select={emptyFunction}
-                        scaling={returnOne}
-                        isContentActive={returnTrue}
-                        chromeHidden={true}
-                        rootSelected={returnFalse}
-                        childHideResizeHandles={returnTrue}
-                        childHideDecorationTitle={returnTrue}
-                        childFitWidth={this.childFitWidth}
-                        // childDocumentsActive={returnFalse}
-                        removeDocument={this.removeDoc}
-                        addDocument={this.addDoc}
-                        renderDepth={this.props.renderDepth + 1}
-                        viewType={CollectionViewType.Stacking}
-                        pointerEvents={returnAll}
-                    />
-                </div>
-                <hr />
-                <div onPointerDown={this.addNoteClick} onClick={e => { e.stopPropagation(); e.preventDefault(); }} >
-                    Add Note
+        return (
+            <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose}>
+                <div className="mapbox-infowindow">
+                    <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
+                        <CollectionStackingView
+                            ref={r => (this._stack = r)}
+                            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                            Document={this.props.place}
+                            DataDoc={undefined}
+                            fieldKey="data"
+                            CollectionView={undefined}
+                            NativeWidth={returnZero}
+                            NativeHeight={returnZero}
+                            docFilters={returnEmptyFilter}
+                            setHeight={emptyFunction}
+                            isAnnotationOverlay={false}
+                            select={emptyFunction}
+                            scaling={returnOne}
+                            isContentActive={returnTrue}
+                            chromeHidden={true}
+                            rootSelected={returnFalse}
+                            childHideResizeHandles={returnTrue}
+                            childHideDecorationTitle={returnTrue}
+                            childFitWidth={this.childFitWidth}
+                            // childDocumentsActive={returnFalse}
+                            removeDocument={this.removeDoc}
+                            addDocument={this.addDoc}
+                            renderDepth={this.props.renderDepth + 1}
+                            viewType={CollectionViewType.Stacking}
+                            pointerEvents={returnAll}
+                        />
+                    </div>
+                    <hr />
+                    <div
+                        onPointerDown={this.addNoteClick}
+                        onClick={e => {
+                            e.stopPropagation();
+                            e.preventDefault();
+                        }}>
+                        Add Note
+                    </div>
                 </div>
-            </div>
-        </InfoWindow>;
+            </InfoWindow>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 0c7d7dc31..942072524 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,32 +1,31 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 // import { Canvas } from '@react-three/fiber';
-import { computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
 // import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
-import { DateField } from "../../../fields/DateField";
-import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, NumCast } from "../../../fields/Types";
-import { AudioField, VideoField } from "../../../fields/URLField";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, OmitKeys, returnFalse, returnOne } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { Networking } from "../../Network";
-import { CaptureManager } from "../../util/CaptureManager";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline";
-import { ContextMenu } from "../ContextMenu";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
+import { DateField } from '../../../fields/DateField';
+import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { ComputedField } from '../../../fields/ScriptField';
+import { Cast, NumCast } from '../../../fields/Types';
+import { AudioField, VideoField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, OmitKeys, returnFalse, returnOne } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { Networking } from '../../Network';
+import { CaptureManager } from '../../util/CaptureManager';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline';
+import { ContextMenu } from '../ContextMenu';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { FieldView, FieldViewProps } from './FieldView';
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
-import "./ScreenshotBox.scss";
-import { VideoBox } from "./VideoBox";
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import './ScreenshotBox.scss';
+import { VideoBox } from './VideoBox';
 declare class MediaRecorder {
-      constructor(e: any, options?: any);  // whatever MediaRecorder has
+    constructor(e: any, options?: any); // whatever MediaRecorder has
 }
 
 // interface VideoTileProps {
@@ -107,226 +106,238 @@ declare class MediaRecorder {
 
 @observer
 export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-      public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
-      private _audioRec: any;
-      private _videoRec: any;
-      @observable private _videoRef: HTMLVideoElement | null = null;
-      @observable _screenCapture = false;
-      @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(ScreenshotBox, fieldKey);
+    }
+    private _audioRec: any;
+    private _videoRec: any;
+    @observable private _videoRef: HTMLVideoElement | null = null;
+    @observable _screenCapture = false;
+    @computed get recordingStart() {
+        return Cast(this.dataDoc[this.props.fieldKey + '-recordingStart'], DateField)?.date.getTime();
+    }
 
-      constructor(props: any) {
-            super(props);
-            if (this.rootDoc.videoWall) {
-                  this.rootDoc.nativeWidth = undefined;
-                  this.rootDoc.nativeHeight = undefined;
-                  this.layoutDoc.popOff = 0;
-                  this.layoutDoc.popOut = 1;
-            } else {
-                  this.setupDictation();
-            }
-      }
-      getAnchor = () => {
-            const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
-            return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow", "_timecodeToHide",
-                  startTime, startTime === undefined ? undefined : startTime + 3)
-                  || this.rootDoc;
-      }
+    constructor(props: any) {
+        super(props);
+        if (this.rootDoc.videoWall) {
+            this.rootDoc.nativeWidth = undefined;
+            this.rootDoc.nativeHeight = undefined;
+            this.layoutDoc.popOff = 0;
+            this.layoutDoc.popOut = 1;
+        } else {
+            this.setupDictation();
+        }
+    }
+    getAnchor = () => {
+        const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3) || this.rootDoc;
+    };
 
-      videoLoad = () => {
-            const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight;
-            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
-            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
-            if (!nativeWidth || !nativeHeight) {
-                  if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
-                  Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
-                  this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
-            }
-      }
+    videoLoad = () => {
+        const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight;
+        const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+        const nativeHeight = Doc.NativeHeight(this.layoutDoc);
+        if (!nativeWidth || !nativeHeight) {
+            if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
+            Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
+            this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
+        }
+    };
 
-      componentDidMount() {
-            this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
-            this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
-            // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
-            //     ({ width, height }) => {
-            //         if (this._camera) {
-            //             const angle = -Math.abs(1 - width / height);
-            //             const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
-            //             this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
-            //             this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
-            //             (this._camera as any).updateProjectionMatrix();
-            //         }
-            //     });
-      }
-      componentWillUnmount() {
-            const ind = DocUtils.ActiveRecordings.indexOf(this);
-            ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
-      }
-
-      specificContextMenu = (e: React.MouseEvent): void => {
-            const subitems = [{ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" as any }];
-            ContextMenu.Instance.addItem({ description: "Options...", subitems, icon: "video" });
-      }
+    componentDidMount() {
+        this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
+        this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+        // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
+        //     ({ width, height }) => {
+        //         if (this._camera) {
+        //             const angle = -Math.abs(1 - width / height);
+        //             const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
+        //             this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
+        //             this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+        //             (this._camera as any).updateProjectionMatrix();
+        //         }
+        //     });
+    }
+    componentWillUnmount() {
+        const ind = DocUtils.ActiveRecordings.indexOf(this);
+        ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1);
+    }
 
-      @computed get content() {
-            if (this.rootDoc.videoWall) return (null);
-            return <video className={"videoBox-content"} key="video"
-                  ref={r => {
-                        this._videoRef = r;
-                        setTimeout(() => {
-                              if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) {
-                                    this.toggleRecording();
-                              }
-                        }, 100);
-                  }}
-                  autoPlay={this._screenCapture}
-                  style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
-                  onCanPlay={this.videoLoad}
-                  controls={true}
-                  onClick={e => e.preventDefault()}>
-                  <source type="video/mp4" />
-                  Not supported.
-            </video>;
-      }
+    specificContextMenu = (e: React.MouseEvent): void => {
+        const subitems = [{ description: 'Screen Capture', event: this.toggleRecording, icon: 'expand-arrows-alt' as any }];
+        ContextMenu.Instance.addItem({ description: 'Options...', subitems, icon: 'video' });
+    };
 
-      // _numScreens = 5;
-      // _camera: Camera | undefined;
-      // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
-      // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
-      @computed get threed() {
-            // if (this.rootDoc.videoWall) {
-            //     const screens: any[] = [];
-            //     const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
-            //     let count = 0;
-            //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
-            //         <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
-            //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
-            //         this._camera = props.camera;
-            //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
-            //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
-            //     }}>
-            //         {/* <ambientLight />*/}
-            //         <pointLight position={[10, 10, 10]} intensity={1} />
-            //         {screens}
-            //     </ Canvas>;
-            // }
-            return (null);
-      }
-      toggleRecording = async () => {
-            if (!this._screenCapture) {
-                  this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
-                  const aud_chunks: any = [];
-                  this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
-                  this._audioRec.onstop = async (e: any) => {
-                        const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
-                        if (!(result instanceof Error)) {
-                              this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client);
+    @computed get content() {
+        if (this.rootDoc.videoWall) return null;
+        return (
+            <video
+                className={'videoBox-content'}
+                key="video"
+                ref={r => {
+                    this._videoRef = r;
+                    setTimeout(() => {
+                        if (this.rootDoc.mediaState === 'pendingRecording' && this._videoRef) {
+                            this.toggleRecording();
                         }
-                  };
-                  this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
-                  this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
-                  const vid_chunks: any = [];
-                  this._videoRec.onstart = () => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date());
-                  this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
-                  this._videoRec.onstop = async (e: any) => {
-                        console.log("screenshotbox: upload")
-                        const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
-                        const [{ result }] = await Networking.UploadFilesToServer(file);
-                        this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000;
-                        if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
-                              this.dataDoc.type = DocumentType.VID;
-                              this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
-                              this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
-                              this.layoutDoc._fitWidth = undefined;
-                              this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
-                        } else alert("video conversion failed");
-                  };
-                  this._audioRec.start();
-                  this._videoRec.start();
-                  runInAction(() => {
-                        this._screenCapture = true;
-                        this.dataDoc.mediaState = "recording";
-                  });
-                  DocUtils.ActiveRecordings.push(this);
-            } else {
-                  this._audioRec?.stop();
-                  this._videoRec?.stop();
-                  runInAction(() => {
-                        this._screenCapture = false;
-                        this.dataDoc.mediaState = "paused";
-                  });
-                  const ind = DocUtils.ActiveRecordings.indexOf(this);
-                  ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
+                    }, 100);
+                }}
+                autoPlay={this._screenCapture}
+                style={{ width: this._screenCapture ? '100%' : undefined, height: this._screenCapture ? '100%' : undefined }}
+                onCanPlay={this.videoLoad}
+                controls={true}
+                onClick={e => e.preventDefault()}>
+                <source type="video/mp4" />
+                Not supported.
+            </video>
+        );
+    }
+
+    // _numScreens = 5;
+    // _camera: Camera | undefined;
+    // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
+    // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
+    @computed get threed() {
+        // if (this.rootDoc.videoWall) {
+        //     const screens: any[] = [];
+        //     const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
+        //     let count = 0;
+        //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
+        //         <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
+        //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
+        //         this._camera = props.camera;
+        //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
+        //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+        //     }}>
+        //         {/* <ambientLight />*/}
+        //         <pointLight position={[10, 10, 10]} intensity={1} />
+        //         {screens}
+        //     </ Canvas>;
+        // }
+        return null;
+    }
+    toggleRecording = async () => {
+        if (!this._screenCapture) {
+            this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
+            const aud_chunks: any = [];
+            this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
+            this._audioRec.onstop = async (e: any) => {
+                const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
+                if (!(result instanceof Error)) {
+                    this.dataDoc[this.props.fieldKey + '-audio'] = new AudioField(result.accessPaths.agnostic.client);
+                }
+            };
+            this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
+            this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
+            const vid_chunks: any = [];
+            this._videoRec.onstart = () => (this.dataDoc[this.props.fieldKey + '-recordingStart'] = new DateField(new Date()));
+            this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
+            this._videoRec.onstop = async (e: any) => {
+                console.log('screenshotbox: upload');
+                const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
+                const [{ result }] = await Networking.UploadFilesToServer(file);
+                this.dataDoc[this.fieldKey + '-duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
+                if (!(result instanceof Error)) {
+                    // convert this screenshotBox into normal videoBox
+                    this.dataDoc.type = DocumentType.VID;
+                    this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
+                    this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
+                    this.layoutDoc._fitWidth = undefined;
+                    this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
+                } else alert('video conversion failed');
+            };
+            this._audioRec.start();
+            this._videoRec.start();
+            runInAction(() => {
+                this._screenCapture = true;
+                this.dataDoc.mediaState = 'recording';
+            });
+            DocUtils.ActiveRecordings.push(this);
+        } else {
+            this._audioRec?.stop();
+            this._videoRec?.stop();
+            runInAction(() => {
+                this._screenCapture = false;
+                this.dataDoc.mediaState = 'paused';
+            });
+            const ind = DocUtils.ActiveRecordings.indexOf(this);
+            ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1);
 
-                  CaptureManager.Instance.open(this.rootDoc);
-            }
-      }
+            CaptureManager.Instance.open(this.rootDoc);
+        }
+    };
 
-      setupDictation = () => {
-            if (this.dataDoc[this.fieldKey + "-dictation"]) return;
-            const dictationText = CurrentUserUtils.GetNewTextDoc("dictation",
-                  NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10,
-                  NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-            dictationText._autoHeight = false;
-            const dictationTextProto = Doc.GetProto(dictationText);
-            dictationTextProto.recordingSource = this.dataDoc;
-            dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
-            dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-            this.dataDoc[this.fieldKey + "-dictation"] = dictationText;
-      }
-      contentFunc = () => [this.threed, this.content];
-      videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], this.layoutDoc[WidthSym]()) * this.props.PanelWidth();
-      formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
-      render() {
-            TraceMobx();
-            return <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: "100%", height: "100%" }} >
-                  <div className="videoBox-viewer" >
-                        <div style={{ position: "relative", height: this.videoPanelHeight() }}>
-                              <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                                    PanelHeight={this.videoPanelHeight}
-                                    PanelWidth={this.props.PanelWidth}
-                                    focus={this.props.focus}
-                                    isSelected={this.props.isSelected}
-                                    isAnnotationOverlay={true}
-                                    select={emptyFunction}
-                                    isContentActive={returnFalse}
-                                    scaling={returnOne}
-                                    whenChildContentsActiveChanged={emptyFunction}
-                                    removeDocument={returnFalse}
-                                    moveDocument={returnFalse}
-                                    addDocument={returnFalse}
-                                    CollectionView={undefined}
-                                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                                    renderDepth={this.props.renderDepth + 1}
-                                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                                    {this.contentFunc}
-                              </CollectionFreeFormView></div>
-                        <div style={{ position: "relative", height: this.formattedPanelHeight() }}>
-                              {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
-                                    <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                                          Document={this.dataDoc[this.fieldKey + "-dictation"]}
-                                          fieldKey={"text"}
-                                          PanelHeight={this.formattedPanelHeight}
-                                          isAnnotationOverlay={true}
-                                          select={emptyFunction}
-                                          isContentActive={emptyFunction}
-                                          scaling={returnOne}
-                                          xPadding={25}
-                                          yPadding={10}
-                                          whenChildContentsActiveChanged={emptyFunction}
-                                          removeDocument={returnFalse}
-                                          moveDocument={returnFalse}
-                                          addDocument={returnFalse}
-                                          CollectionView={undefined}
-                                          renderDepth={this.props.renderDepth + 1}
-                                          ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                                    </FormattedTextBox>}
-                        </div>
-                  </div>
-                  {!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons">
-                        <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} >
-                              <FontAwesomeIcon icon="file" size="lg" />
+    setupDictation = () => {
+        if (this.dataDoc[this.fieldKey + '-dictation']) return;
+        const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+        dictationText._autoHeight = false;
+        const dictationTextProto = Doc.GetProto(dictationText);
+        dictationTextProto.recordingSource = this.dataDoc;
+        dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
+        dictationTextProto.mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState');
+        this.dataDoc[this.fieldKey + '-dictation'] = dictationText;
+    };
+    contentFunc = () => [this.threed, this.content];
+    videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth();
+    formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
+    render() {
+        TraceMobx();
+        return (
+            <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: '100%', height: '100%' }}>
+                <div className="videoBox-viewer">
+                    <div style={{ position: 'relative', height: this.videoPanelHeight() }}>
+                        <CollectionFreeFormView
+                            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+                            PanelHeight={this.videoPanelHeight}
+                            PanelWidth={this.props.PanelWidth}
+                            focus={this.props.focus}
+                            isSelected={this.props.isSelected}
+                            isAnnotationOverlay={true}
+                            select={emptyFunction}
+                            isContentActive={returnFalse}
+                            scaling={returnOne}
+                            whenChildContentsActiveChanged={emptyFunction}
+                            removeDocument={returnFalse}
+                            moveDocument={returnFalse}
+                            addDocument={returnFalse}
+                            CollectionView={undefined}
+                            ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                            renderDepth={this.props.renderDepth + 1}
+                            ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                            {this.contentFunc}
+                        </CollectionFreeFormView>
+                    </div>
+                    <div style={{ position: 'relative', height: this.formattedPanelHeight() }}>
+                        {!(this.dataDoc[this.fieldKey + '-dictation'] instanceof Doc) ? null : (
+                            <FormattedTextBox
+                                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+                                Document={this.dataDoc[this.fieldKey + '-dictation']}
+                                fieldKey={'text'}
+                                PanelHeight={this.formattedPanelHeight}
+                                isAnnotationOverlay={true}
+                                select={emptyFunction}
+                                isContentActive={emptyFunction}
+                                scaling={returnOne}
+                                xPadding={25}
+                                yPadding={10}
+                                whenChildContentsActiveChanged={emptyFunction}
+                                removeDocument={returnFalse}
+                                moveDocument={returnFalse}
+                                addDocument={returnFalse}
+                                CollectionView={undefined}
+                                renderDepth={this.props.renderDepth + 1}
+                                ContainingCollectionDoc={this.props.ContainingCollectionDoc}></FormattedTextBox>
+                        )}
+                    </div>
+                </div>
+                {!this.props.isSelected() ? null : (
+                    <div className="screenshotBox-uiButtons">
+                        <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording}>
+                            <FontAwesomeIcon icon="file" size="lg" />
                         </div>
-                  </div>}
-            </div >;
-      }
-}
\ No newline at end of file
+                    </div>
+                )}
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e833c7e30..b1f7d8023 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,44 +1,42 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from "mobx";
-import { observer } from "mobx-react";
-import { basename } from "path";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
+import { observer } from 'mobx-react';
+import { basename } from 'path';
 import * as rp from 'request-promise';
-import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc";
-import { InkTool } from "../../../fields/InkField";
-import { List } from "../../../fields/List";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { AudioField, ImageField, VideoField } from "../../../fields/URLField";
-import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
-import { Docs, DocUtils } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { Networking } from "../../Network";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentManager } from "../../util/DocumentManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { CollectionStackedTimeline, TrimScope } from "../collections/CollectionStackedTimeline";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
-import { DocumentDecorations } from "../DocumentDecorations";
-import { MarqueeAnnotator } from "../MarqueeAnnotator";
-import { AnchorMenu } from "../pdf/AnchorMenu";
-import { StyleProp } from "../StyleProvider";
+import { Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
+import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { AudioField, ImageField, VideoField } from '../../../fields/URLField';
+import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { Networking } from '../../Network';
+import { DocumentManager } from '../../util/DocumentManager';
+import { ReplayMovements } from '../../util/ReplayMovements';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { undoBatch } from '../../util/UndoManager';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { DocumentDecorations } from '../DocumentDecorations';
+import { MarqueeAnnotator } from '../MarqueeAnnotator';
+import { AnchorMenu } from '../pdf/AnchorMenu';
+import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
-import "./VideoBox.scss";
-import { Presentation } from "../../util/TrackMovements";
-import { RecordingBox } from "./RecordingBox";
-import { ReplayMovements } from "../../util/ReplayMovements";
+import { RecordingBox } from './RecordingBox';
+import './VideoBox.scss';
 const path = require('path');
 
 /**
  * VideoBox
  * Main component: VideoBox.tsx
  * Supporting Components: CollectionStackedTimeline
- * 
+ *
  * VideoBox is a node that supports the playback of video files in Dash.
  * When a video file or YouTube video is importeed into Dash, it is immediately rendered as a VideoBox document.
  * CollectionStackedTimline handles AudioBox and VideoBox shared behavior, but VideoBox handles playing, pausing, etc because it contains <video> element
@@ -48,7 +46,9 @@ const path = require('path');
 
 @observer
 export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(VideoBox, fieldKey);
+    }
     /**
      * Uploads an image buffer to the server and stores with specified filename.  by default the image
      * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
@@ -58,20 +58,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
      */
     public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
         try {
-            const posting = Utils.prepend("/uploadURI");
+            const posting = Utils.prepend('/uploadURI');
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
                     name: returnedFilename,
                     nosuffix,
-                    replaceRootFilename
+                    replaceRootFilename,
                 },
                 json: true,
             });
             return returnedUri;
-
         } catch (e) {
-            console.log("VideoBox :" + e);
+            console.log('VideoBox :' + e);
         }
     }
 
@@ -103,27 +102,29 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @observable _finished: boolean = false; // has playback reached end of clip
     @observable _volume: number = 1;
     @observable _muted: boolean = false;
-    @observable _controlsTransform?: { X: number, Y: number };
+    @observable _controlsTransform?: { X: number; Y: number };
     @observable _controlsVisible: boolean = true;
     @observable _scrubbing: boolean = false;
 
-    @computed get links() { return DocListCast(this.dataDoc.links); }
-    @computed get heightPercent() { return NumCast(this.layoutDoc._timelineHeightPercent, 100); } // current percent of video relative to VideoBox height
+    @computed get links() {
+        return DocListCast(this.dataDoc.links);
+    }
+    @computed get heightPercent() {
+        return NumCast(this.layoutDoc._timelineHeightPercent, 100);
+    } // current percent of video relative to VideoBox height
     // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
     @observable rawDuration: number = 0;
 
-
     @computed get youtubeVideoId() {
         const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
-        return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
+        return field && field.url.href.indexOf('youtube') !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split('/')) : '';
     }
 
-
     // returns the path of the audio file
     @computed get audiopath() {
         const field = Cast(this.props.Document[this.props.fieldKey + '-audio'], AudioField, null);
         const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
-        return field?.url.href ?? vfield?.url.href ?? "";
+        return field?.url.href ?? vfield?.url.href ?? '';
     }
 
     // returns the presentation data if it exists, null otherwise
@@ -132,10 +133,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return data ? JSON.parse(data) : null;
     }
 
-    @computed private get timeline() { return this._stackedTimeline; }
-    private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } // css transition for hiding/showing timeline
-    public get player(): HTMLVideoElement | null { return this._videoRef; }
-
+    @computed private get timeline() {
+        return this._stackedTimeline;
+    }
+    private get transition() {
+        return this._clicking ? 'left 0.5s, width 0.5s, height 0.5s' : '';
+    } // css transition for hiding/showing timeline
+    public get player(): HTMLVideoElement | null {
+        return this._videoRef;
+    }
 
     componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
@@ -150,7 +156,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             }
         }
         this.player && this.setPlayheadTime(0);
-        document.addEventListener("keydown", this.keyEvents, true);
+        document.addEventListener('keydown', this.keyEvents, true);
 
         if (this.presentation) {
             ReplayMovements.Instance.setVideoBox(this);
@@ -161,9 +167,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.removeCurrentlyPlaying();
         this.Pause();
         Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
-        document.removeEventListener("keydown", this.keyEvents, true);
+        document.removeEventListener('keydown', this.keyEvents, true);
 
-         if (this.presentation) {
+        if (this.presentation) {
             ReplayMovements.Instance.removeVideoBox();
         }
     }
@@ -173,20 +179,23 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     keyEvents = (e: KeyboardEvent) => {
         if (
             // need to include range inputs because after dragging time slider it becomes target element
-            !(e.target instanceof HTMLInputElement && !(e.target.type === "range")) &&
+            !(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) &&
             this.props.isSelected(true)
         ) {
             switch (e.key) {
-                case "ArrowLeft":
-                case "ArrowRight":
+                case 'ArrowLeft':
+                case 'ArrowRight':
                     clearTimeout(this._controlsFadeTimer);
                     this._scrubbing = true;
-                    this._controlsFadeTimer = setTimeout(action(() => this._scrubbing = false), 500);
+                    this._controlsFadeTimer = setTimeout(
+                        action(() => (this._scrubbing = false)),
+                        500
+                    );
                     e.stopPropagation();
                     break;
             }
         }
-    }
+    };
 
     // plays video
     @action public Play = (update: boolean = true) => {
@@ -210,18 +219,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 update && this._youtubePlayer?.playVideo();
                 this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
             } catch (e) {
-                console.log("Video Play Exception:", e);
+                console.log('Video Play Exception:', e);
             }
         }
         this.updateTimecode();
-    }
+    };
 
     // goes to time
     @action public Seek(time: number) {
         try {
             this._youtubePlayer?.seekTo(Math.round(time), true);
         } catch (e) {
-            console.log("Video Seek Exception:", e);
+            console.log('Video Seek Exception:', e);
         }
         this.player && (this.player.currentTime = time);
         this._audioPlayer && (this._audioPlayer.currentTime = time);
@@ -242,7 +251,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
             this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
         } catch (e) {
-            console.log("Video Pause Exception:", e);
+            console.log('Video Pause Exception:', e);
         }
         this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
         this._playTimer = undefined;
@@ -251,24 +260,23 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             clearTimeout(this._playRegionTimer); // if paused in the middle of playback, prevents restart on next play
         }
         this._playRegionTimer = undefined;
-    }
+    };
 
     // toggles video full screen
     @action public FullScreen = () => {
         if (document.fullscreenElement === this._contentRef) {
             this._fullScreen = false;
             this.player && this._contentRef && document.exitFullscreen();
-        }
-        else {
+        } else {
             this._fullScreen = true;
             this.player && this._contentRef && this._contentRef.requestFullscreen();
         }
         try {
-            this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
+            this._youtubePlayer && this.props.addDocTab(this.rootDoc, 'add');
         } catch (e) {
-            console.log("Video FullScreen Exception:", e);
+            console.log('Video FullScreen Exception:', e);
         }
-    }
+    };
 
     // fades out controls in fullscreen after mouse stops moving
     @action controlsFade = (e: PointerEvent) => {
@@ -276,10 +284,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (!this._scrubbing) {
             clearTimeout(this._controlsFadeTimer);
             this._controlsVisible = true;
-            this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000);
+            this._controlsFadeTimer = setTimeout(
+                action(() => (this._controlsVisible = false)),
+                3000
+            );
         }
-    }
-
+    };
 
     // drag controls around window in fulls screen
     @action controlsDrag = (e: React.PointerEvent) => {
@@ -288,7 +298,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const eleStyle = getComputedStyle(e.target as Element);
         this._controlsTransform = { X: parseInt(eleStyle.left), Y: parseInt(eleStyle.top) };
 
-        setupMoveUpEvents(e.target,
+        setupMoveUpEvents(
+            e.target,
             e,
             action((e, down, delta) => {
                 if (this._controlsTransform) {
@@ -298,32 +309,35 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 return false;
             }),
             emptyFunction,
-            emptyFunction)
-    }
-
+            emptyFunction
+        );
+    };
 
     // creates and links snapshot photo of current video frame
     @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
         const width = NumCast(this.layoutDoc._width);
         const canvas = document.createElement('canvas');
         canvas.width = 640;
-        canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
-        const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
+        canvas.height = (640 * Doc.NativeHeight(this.layoutDoc)) / (Doc.NativeWidth(this.layoutDoc) || 1);
+        const ctx = canvas.getContext('2d'); //draw image to canvas. scale to target dimensions
         if (ctx) {
             this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
         }
 
         if (!this._videoRef) {
             const b = Docs.Create.LabelDocument({
-                x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
-                _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
-                _isLinkButton: true
+                x: NumCast(this.layoutDoc.x) + width,
+                y: NumCast(this.layoutDoc.y, 1),
+                _width: 150,
+                _height: 50,
+                title: (this.layoutDoc._currentTimecode || 0).toString(),
+                _isLinkButton: true,
             });
             this.props.addDocument?.(b);
-            DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
-            Networking.PostToServer("/youtubeScreenshot", {
+            DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, 'video snapshot');
+            Networking.PostToServer('/youtubeScreenshot', {
                 id: this.youtubeVideoId,
-                timecode: this.layoutDoc._currentTimecode
+                timecode: this.layoutDoc._currentTimecode,
             }).then(response => {
                 const resolved = response?.accessPaths?.agnostic?.client;
                 if (resolved) {
@@ -335,49 +349,50 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             //convert to desired file format
             const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
             // if you want to preview the captured image,
-            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
-            const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
+            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
+            const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, '_'));
             const filename = basename(encodedFilename);
-            VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
-                returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
+            VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
         }
-    }
+    };
 
     updateIcon = () => {
         const makeIcon = (returnedfilename: string) => {
             this.dataDoc.icon = new ImageField(returnedfilename);
-            this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
-            this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
+            this.dataDoc['icon-nativeWidth'] = this.layoutDoc[WidthSym]();
+            this.dataDoc['icon-nativeHeight'] = this.layoutDoc[HeightSym]();
         };
         this.Snapshot(undefined, undefined, makeIcon);
-    }
+    };
 
     // creates link for snapshot
     createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
-        const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
+        const url = !imagePath.startsWith('/') ? Utils.CorsProxy(imagePath) : imagePath;
         const width = NumCast(this.layoutDoc._width) || 1;
         const height = NumCast(this.layoutDoc._height);
         const imageSummary = Docs.Create.ImageDocument(url, {
-            _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
-            x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
-            _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
+            _nativeWidth: Doc.NativeWidth(this.layoutDoc),
+            _nativeHeight: Doc.NativeHeight(this.layoutDoc),
+            x: NumCast(this.layoutDoc.x) + width,
+            y: NumCast(this.layoutDoc.y),
+            _isLinkButton: true,
+            _width: 150,
+            _height: (height / width) * 150,
+            title: '--snapshot' + NumCast(this.layoutDoc._currentTimecode) + ' image-',
         });
         Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
         Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
         this.props.addDocument?.(imageSummary);
-        const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
+        const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, 'video snapshot');
         link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
-        setTimeout(() =>
-            (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
-    }
-
+        setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true));
+    };
 
     getAnchor = () => {
-        const timecode = Cast(this.layoutDoc._currentTimecode, "number", null);
+        const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null);
         const marquee = AnchorMenu.Instance.GetAnchor?.();
-        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
-    }
-
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
+    };
 
     // sets video info on load
     videoLoad = action(() => {
@@ -387,10 +402,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
         if (Number.isFinite(this.player!.duration)) {
             this.rawDuration = this.player!.duration;
-        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + "-duration"]);
+        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + '-duration']);
     });
 
-
     // updates video time
     @action
     updateTimecode = () => {
@@ -398,10 +412,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         try {
             this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
         } catch (e) {
-            console.log("Video Timecode Exception:", e);
+            console.log('Video Timecode Exception:', e);
         }
-    }
-
+    };
 
     // extracts video thumbnails and saves them as field of doc
     getVideoThumbnails = () => {
@@ -419,23 +432,23 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const ctx = canvas.getContext('2d');
             ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
             const imgUrl = canvas.toDataURL();
-            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
-            const encodedFilename = encodeURIComponent("thumbnail" + retitled + "_" + video.currentTime.toString().replace(/\./, "_"));
+            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
+            const encodedFilename = encodeURIComponent('thumbnail' + retitled + '_' + video.currentTime.toString().replace(/\./, '_'));
             const filename = basename(encodedFilename);
             thumbnailPromises.push(VideoBox.convertDataUri(imgUrl, filename));
             const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
             if (newTime < video.duration) {
                 video.currentTime = newTime;
+            } else {
+                Promise.all(thumbnailPromises).then(thumbnails => {
+                    this.dataDoc.thumbnails = new List<string>(thumbnails);
+                });
             }
-            else {
-                Promise.all(thumbnailPromises).then(thumbnails => { this.dataDoc.thumbnails = new List<string>(thumbnails); });
-            }
-        }
+        };
 
         const field = Cast(this.dataDoc[this.fieldKey], VideoField);
         field && (video.src = field.url.href);
-    }
-
+    };
 
     // sets video element ref
     @action
@@ -446,33 +459,34 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             // @ts-ignore
             // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
             this._disposers.reactionDisposer?.();
-            this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
-                time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
+            this._disposers.reactionDisposer = reaction(
+                () => NumCast(this.layoutDoc._currentTimecode),
+                time => !this._playing && (vref.currentTime = time),
+                { fireImmediately: true }
+            );
 
             (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails();
         }
-    }
+    };
 
     // set ref for div that wraps video and controls for fullscreen
     @action
     setContentRef = (cref: HTMLDivElement | null) => {
         this._contentRef = cref;
         if (cref) {
-            cref.onfullscreenchange = action((e) => {
-                this._fullScreen = (document.fullscreenElement === cref);
+            cref.onfullscreenchange = action(e => {
+                this._fullScreen = document.fullscreenElement === cref;
                 this._controlsVisible = true;
                 this._scrubbing = false;
                 clearTimeout(this._controlsFadeTimer);
                 if (this._fullScreen) {
                     document.addEventListener('pointermove', this.controlsFade);
-                }
-                else {
+                } else {
                     document.removeEventListener('pointermove', this.controlsFade);
                 }
             });
         }
-    }
-
+    };
 
     // context menu
     specificContextMenu = (e: React.MouseEvent): void => {
@@ -480,143 +494,185 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (field) {
             const url = field.url.href;
             const subitems: ContextMenuProps[] = [];
-            subitems.push({ description: "Full Screen", event: this.FullScreen, icon: "expand" });
-            subitems.push({ description: "Take Snapshot", event: this.Snapshot, icon: "expand-arrows-alt" });
-            this.rootDoc.type === DocumentType.SCREENSHOT && subitems.push({
-                description: "Screen Capture", event: (async () => {
-                    runInAction(() => this._screenCapture = !this._screenCapture);
-                    this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
-                }), icon: "expand-arrows-alt"
+            subitems.push({ description: 'Full Screen', event: this.FullScreen, icon: 'expand' });
+            subitems.push({ description: 'Take Snapshot', event: this.Snapshot, icon: 'expand-arrows-alt' });
+            this.rootDoc.type === DocumentType.SCREENSHOT &&
+                subitems.push({
+                    description: 'Screen Capture',
+                    event: async () => {
+                        runInAction(() => (this._screenCapture = !this._screenCapture));
+                        this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
+                    },
+                    icon: 'expand-arrows-alt',
+                });
+            subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? '' : "Don't") + ' follow links when encountered', event: () => (this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks), icon: 'expand-arrows-alt' });
+            subitems.push({
+                description: (this.layoutDoc.dontAutoPlayFollowedLinks ? '' : "Don't") + ' play when link is selected',
+                event: () => (this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks),
+                icon: 'expand-arrows-alt',
             });
-            subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? "" : "Don't") + " follow links when encountered", event: () => this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks, icon: "expand-arrows-alt" });
-            subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" });
-            subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" });
+            subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : 'Auto play') + ' anchors onClick', event: () => (this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors), icon: 'expand-arrows-alt' });
             // subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" });
             // subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" });
             // subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" });
             // subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" });
-            subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
+            subitems.push({
+                description: 'Copy path',
+                event: () => {
+                    Utils.CopyText(url);
+                },
+                icon: 'expand-arrows-alt',
+            });
             // if the videobox was turned from a recording box
-            if (this.dataDoc[this.fieldKey + "-recorded"] === true) {
+            if (this.dataDoc[this.fieldKey + '-recorded'] === true) {
                 subitems.push({
-                      description: "Recreate recording", event: () => {
-                            this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey);
-                            // delete assoicated video data
-                            this.dataDoc[this.props.fieldKey] = "";
-                            this.dataDoc[this.fieldKey + "-duration"] = "";
-                            // delete assoicated presentation data
-                            this.dataDoc[this.fieldKey + "-presentation"] = "";
-                      }, icon: "expand-arrows-alt"
+                    description: 'Recreate recording',
+                    event: () => {
+                        this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey);
+                        // delete assoicated video data
+                        this.dataDoc[this.props.fieldKey] = '';
+                        this.dataDoc[this.fieldKey + '-duration'] = '';
+                        // delete assoicated presentation data
+                        this.dataDoc[this.fieldKey + '-presentation'] = '';
+                    },
+                    icon: 'expand-arrows-alt',
                 });
             }
-            ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
+            ContextMenu.Instance.addItem({ description: 'Options...', subitems: subitems, icon: 'video' });
         }
-    }
-
+    };
 
     // ref for updating time
-    setAudioRef = (e: HTMLAudioElement | null) => this._audioPlayer = e;
+    setAudioRef = (e: HTMLAudioElement | null) => (this._audioPlayer = e);
 
     // renders the video and audio
     @computed get content() {
         const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-        const interactive = CurrentUserUtils.ActiveTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
-        const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
-        const opacity = this._scrubbing ? 0.3 : (this._controlsVisible ? 1 : 0);
-        return !field ? <div key="loading">Loading</div> :
-            <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply", cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
-                <div className={classname} ref={this.setContentRef} onPointerDown={(e) => this._fullScreen && e.stopPropagation()}>
-                    {this._fullScreen && <div className="videoBox-ui" onPointerDown={this.controlsDrag}
-                        style={{ left: this._controlsTransform && this._controlsTransform.X, top: this._controlsTransform && this._controlsTransform.Y, visibility: this._controlsVisible || this._scrubbing ? 'visible' : 'hidden', opacity: opacity }}>
-                        {this.UIButtons}
-                    </div>}
-                    <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={this._fullScreen ? this.fullScreenSize() : {}}
+        const interactive = Doc.ActiveTool !== InkTool.None || !this.props.isSelected() ? '' : '-interactive';
+        const classname = 'videoBox-content' + (this._fullScreen ? '-fullScreen' : '') + interactive;
+        const opacity = this._scrubbing ? 0.3 : this._controlsVisible ? 1 : 0;
+        return !field ? (
+            <div key="loading">Loading</div>
+        ) : (
+            <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: 'multiply', cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
+                <div className={classname} ref={this.setContentRef} onPointerDown={e => this._fullScreen && e.stopPropagation()}>
+                    {this._fullScreen && (
+                        <div
+                            className="videoBox-ui"
+                            onPointerDown={this.controlsDrag}
+                            style={{
+                                left: this._controlsTransform && this._controlsTransform.X,
+                                top: this._controlsTransform && this._controlsTransform.Y,
+                                visibility: this._controlsVisible || this._scrubbing ? 'visible' : 'hidden',
+                                opacity: opacity,
+                            }}>
+                            {this.UIButtons}
+                        </div>
+                    )}
+                    <video
+                        key="video"
+                        autoPlay={this._screenCapture}
+                        ref={this.setVideoRef}
+                        style={this._fullScreen ? this.fullScreenSize() : {}}
                         onCanPlay={this.videoLoad}
                         controls={VideoBox._nativeControls}
                         onPlay={() => this.Play()}
                         onSeeked={this.updateTimecode}
                         onPause={() => this.Pause()}
-                        onClick={this._fullScreen ? () => this.playing() ? this.Pause() : this.Play() : e => e.preventDefault()}>
+                        onClick={this._fullScreen ? () => (this.playing() ? this.Pause() : this.Play()) : e => e.preventDefault()}>
                         <source src={field.url.href} type="video/mp4" />
                         Not supported.
                     </video>
-                    {!this.audiopath || this.audiopath === field.url.href ? (null) :
-                        <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}>
+                    {!this.audiopath || this.audiopath === field.url.href ? null : (
+                        <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}>
                             <source src={this.audiopath} type="audio/mpeg" />
                             Not supported.
-                        </audio>}
+                        </audio>
+                    )}
                 </div>
-            </div>;
+            </div>
+        );
     }
 
-
     @action youtubeIframeLoaded = (e: any) => {
         if (!this._youtubeContentCreated) {
             this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
             return;
-        }
-        else this._youtubeContentCreated = false;
+        } else this._youtubeContentCreated = false;
 
         this.loadYouTube(e.target);
-    }
+    };
 
     loadYouTube = (iframe: any) => {
         let started = true;
-        const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
-            if (started && event.data === YT.PlayerState.PLAYING) {
-                started = false;
-                this._youtubePlayer?.unMute();
-                //this.Pause();
-                return;
-            }
-            if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
-            if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
-        });
+        const onYoutubePlayerStateChange = (event: any) =>
+            runInAction(() => {
+                if (started && event.data === YT.PlayerState.PLAYING) {
+                    started = false;
+                    this._youtubePlayer?.unMute();
+                    //this.Pause();
+                    return;
+                }
+                if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
+                if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
+            });
         const onYoutubePlayerReady = (event: any) => {
             this._disposers.reactionDisposer?.();
             this._disposers.youtubeReactionDisposer?.();
-            this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
+            this._disposers.reactionDisposer = reaction(
+                () => this.layoutDoc._currentTimecode,
+                () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode))
+            );
             this._disposers.youtubeReactionDisposer = reaction(
-                () => CurrentUserUtils.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
-                (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
+                () => Doc.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);
+        if (typeof YT === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
         else {
             (YT as any)?.ready(() => {
                 this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
                     events: {
-                        'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
-                        'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
-                    }
+                        onReady: this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
+                        onStateChange: this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
+                    },
                 });
             });
         }
-    }
-
+    };
 
     // for play button
-    onPlayDown = () => this._playing ? this.Pause() : this.Play();
+    onPlayDown = () => (this._playing ? this.Pause() : this.Play());
 
     // for fullscreen button
     onFullDown = (e: React.PointerEvent) => {
         this.FullScreen();
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     // for snapshot button
     onSnapshotDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e) => {
-            this.Snapshot(e.clientX, e.clientY);
-            return true;
-        }, emptyFunction, () => this.Snapshot());
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            e => {
+                this.Snapshot(e.clientX, e.clientY);
+                return true;
+            },
+            emptyFunction,
+            () => this.Snapshot()
+        );
+    };
 
     // for show/hide timeline button, transitions between show/hide
     @action
     onTimelineHdlDown = (e: React.PointerEvent) => {
         this._clicking = true;
-        setupMoveUpEvents(this, e,
+        setupMoveUpEvents(
+            this,
+            e,
             action(encodeURIComponent => {
                 this._clicking = false;
                 if (this.props.isContentActive()) {
@@ -626,13 +682,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     this.layoutDoc._timelineHeightPercent = 80;
                 }
                 return false;
-            }), emptyFunction,
+            }),
+            emptyFunction,
             () => {
                 this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
-                setTimeout(action(() => this._clicking = false), 500);
-            }, this.props.isContentActive(), this.props.isContentActive());
-    }
-
+                setTimeout(
+                    action(() => (this._clicking = false)),
+                    500
+                );
+            },
+            this.props.isContentActive(),
+            this.props.isContentActive()
+        );
+    };
 
     // removes video from currently playing display
     @action
@@ -641,7 +703,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
             index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
         }
-    }
+    };
 
     // adds video to currently playing display
     @action
@@ -652,31 +714,36 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
             CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
         }
-    }
-
+    };
 
     @computed get youtubeContent() {
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
-        const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
+        const classname = 'videoBox-content-YouTube' + (this._fullScreen ? '-fullScreen' : '');
         const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
-        return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
-            onPointerLeave={this.updateTimecode}
-            onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
-            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`} />;
+        return (
+            <iframe
+                key={this._youtubeIframeId}
+                id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
+                onPointerLeave={this.updateTimecode}
+                onLoad={this.youtubeIframeLoaded}
+                className={classname}
+                width={Doc.NativeWidth(this.layoutDoc) || 640}
+                height={Doc.NativeHeight(this.layoutDoc) || 390}
+                src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`}
+            />
+        );
     }
 
-
     // for annotating, adds doc with time info
     @action.bound
     addDocWithTimecode(doc: Doc | Doc[]): boolean {
         const docs = doc instanceof Doc ? [doc] : doc;
         const curTime = NumCast(this.layoutDoc._currentTimecode);
-        docs.forEach(doc => doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1);
+        docs.forEach(doc => (doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1));
         return this.addDocument(doc);
     }
 
-
     // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range
     @action
     playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => {
@@ -684,8 +751,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._playRegionTimer = undefined;
         if (Number.isNaN(this.player?.duration)) {
             setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
-        }
-        else if (this.player) {
+        } else if (this.player) {
             // trimBounds override requested playback bounds
             const end = Math.min(this.timeline?.trimEnd ?? this.rawDuration, endTime ?? this.timeline?.trimEnd ?? this.rawDuration);
             const start = Math.max(this.timeline?.trimStart ?? 0, seekTimeInSeconds);
@@ -698,20 +764,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this._audioPlayer?.play();
                 this._playing = true;
                 this.addCurrentlyPlaying();
-                this._playRegionTimer = setTimeout(
-                    () => {
-                        // need to keep track of if end of clip is reached so on next play, clip restarts
-                        if (fullPlay) this._finished = true;
-                        // removes from currently playing if playback has reached end of range marker
-                        else this.removeCurrentlyPlaying();
-                        this.Pause();
-                    }, playRegionDuration * 1000);
+                this._playRegionTimer = setTimeout(() => {
+                    // need to keep track of if end of clip is reached so on next play, clip restarts
+                    if (fullPlay) this._finished = true;
+                    // removes from currently playing if playback has reached end of range marker
+                    else this.removeCurrentlyPlaying();
+                    this.Pause();
+                }, playRegionDuration * 1000);
             } else {
                 this.Pause();
             }
         }
-    }
-
+    };
 
     // ends trim, hides trim controls and displays new clip
     @undoBatch
@@ -725,22 +789,28 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     startTrim = (scope: TrimScope) => {
         this.Pause();
         this.timeline?.StartTrimming(scope);
-    }
+    };
 
     // for trim button, double click displays full clip, single displays curr trim bounds
     onClipPointerDown = (e: React.PointerEvent) => {
         // if timeline isn't shown, show first then trim
         this.heightPercent >= 100 && this.onTimelineHdlDown(e);
-        this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
-            if (doubleTap) {
-                this.startTrim(TrimScope.All);
-            } else if (this.timeline) {
-                this.Pause();
-                this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
-            }
-        }));
-    }
-
+        this.timeline &&
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                returnFalse,
+                action((e: PointerEvent, doubleTap?: boolean) => {
+                    if (doubleTap) {
+                        this.startTrim(TrimScope.All);
+                    } else if (this.timeline) {
+                        this.Pause();
+                        this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
+                    }
+                })
+            );
+    };
 
     // for volume slider sets volume
     @action
@@ -752,7 +822,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.toggleMute();
             }
         }
-    }
+    };
 
     // toggles video mute
     @action
@@ -761,62 +831,68 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._muted = !this._muted;
             this.player.muted = this._muted;
         }
-    }
-
+    };
 
     // stretches vertically or horizontally depending on video orientation so video fits full screen
     fullScreenSize() {
         if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
-            return { height: "100%" };
-        }
-        else {
-            return { width: "100%" };
+            return { height: '100%' };
+        } else {
+            return { width: '100%' };
         }
     }
 
-
     // for zoom slider, sets timeline waveform zoom
     zoom = (zoom: number) => {
         this.timeline?.setZoom(zoom);
-    }
-
+    };
 
     // plays link
     playLink = (doc: Doc) => {
-        const startTime = Math.max(0, (this._stackedTimeline?.anchorStart(doc) || 0));
+        const startTime = Math.max(0, this._stackedTimeline?.anchorStart(doc) || 0);
         const endTime = this.timeline?.anchorEnd(doc);
         if (startTime !== undefined) {
             if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
             else this.Seek(startTime);
         }
-    }
-
+    };
 
     // 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.ActiveTool)) {
-            setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                this._marqueeing = [e.clientX, e.clientY];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
         }
-    }
+    };
 
     // ends marquee selection
     @action
     finishMarquee = () => {
         this._marqueeing = undefined;
         this.props.select(true);
-    }
+    };
 
-    timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
+    timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
 
-    timelineScreenToLocal = () => this.props.ScreenToLocalTransform().scale(this.scaling()).translate(0, -this.heightPercent / 100 * this.props.PanelHeight());
+    timelineScreenToLocal = () =>
+        this.props
+            .ScreenToLocalTransform()
+            .scale(this.scaling())
+            .translate(0, (-this.heightPercent / 100) * this.props.PanelHeight());
 
-    setPlayheadTime = (time: number) => this.player!.currentTime = this.layoutDoc._currentTimecode = time;
+    setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._currentTimecode = time);
 
-    timelineHeight = () => this.props.PanelHeight() * (100 - this.heightPercent) / 100;
+    timelineHeight = () => (this.props.PanelHeight() * (100 - this.heightPercent)) / 100;
 
     playing = () => this._playing;
 
@@ -824,20 +900,22 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     scaling = () => this.props.scaling?.() || 1;
 
-    panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100;
-    panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100;
+    panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100;
+    panelHeight = () => (this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
 
     screenToLocalTransform = () => {
         const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
-        return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
-    }
+        return this.props
+            .ScreenToLocalTransform()
+            .translate(-offset, 0)
+            .scale(100 / this.heightPercent);
+    };
 
-    marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100;
-    marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0];
+    marqueeFitScaling = () => ((this.props.scaling?.() || 1) * this.heightPercent) / 100;
+    marqueeOffset = () => [((this.panelWidth() / 2) * (1 - this.heightPercent / 100)) / (this.heightPercent / 100), 0];
 
     timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
 
-
     // renders video controls
     componentUI = (boundsLeft: number, boundsTop: number) => {
         const bounds = this.props.docViewPath().lastElement().getBounds();
@@ -848,130 +926,157 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const width = Math.max(right - left, 100);
         const uiHeight = Math.max(25, Math.min(50, height / 10));
         const uiMargin = Math.min(10, height / 20);
-        const vidHeight = height * this.heightPercent / 100;
+        const vidHeight = (height * this.heightPercent) / 100;
         const yPos = top + vidHeight - uiHeight - uiMargin;
         const xPos = uiHeight / vidHeight > 0.4 ? right + 10 : left + 10;
-        const opacity = this._scrubbing ? 0.3 : (this._controlsVisible ? 1 : 0);
-        return this._fullScreen || (right - left) < 50 ? null : <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
-            <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? "top 0.5s" : "", opacity: opacity}}>
-                {this.UIButtons}
+        const opacity = this._scrubbing ? 0.3 : this._controlsVisible ? 1 : 0;
+        return this._fullScreen || right - left < 50 ? null : (
+            <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
+                <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? 'top 0.5s' : '', opacity: opacity }}>
+                    {this.UIButtons}
+                </div>
             </div>
-        </div>
-    }
+        );
+    };
 
     @computed get UIButtons() {
         const bounds = this.props.docViewPath().lastElement().getBounds();
         const width = (bounds?.right || 0) - (bounds?.left || 0);
         const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
-        return <>
-            <div className="videobox-button"
-                title={this._playing ? "play" : "pause"}
-                onPointerDown={this.onPlayDown}>
-                <FontAwesomeIcon icon={this._playing ? "pause" : "play"} />
-            </div>
-
-            {this.timeline && width > 150 && <div className="timecode-controls">
-                <div className="timecode-current">
-                    {formatTime(curTime)}
+        return (
+            <>
+                <div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}>
+                    <FontAwesomeIcon icon={this._playing ? 'pause' : 'play'} />
                 </div>
 
-                {this._fullScreen || (this.heightPercent === 100 && width > 200) ?
-                    <div className="timeline-slider">
-                        <input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
-                            className="toolbar-slider time-progress"
-                            onPointerDown={action((e: React.PointerEvent) => { e.stopPropagation(); this._scrubbing = true;})}
-                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
-                            onPointerUp={action((e: React.PointerEvent) => {e.stopPropagation(); this._scrubbing = false;})}
-                        />
+                {this.timeline && width > 150 && (
+                    <div className="timecode-controls">
+                        <div className="timecode-current">{formatTime(curTime)}</div>
+
+                        {this._fullScreen || (this.heightPercent === 100 && width > 200) ? (
+                            <div className="timeline-slider">
+                                <input
+                                    type="range"
+                                    step="0.1"
+                                    min={this.timeline.clipStart}
+                                    max={this.timeline.clipEnd}
+                                    value={curTime}
+                                    className="toolbar-slider time-progress"
+                                    onPointerDown={action((e: React.PointerEvent) => {
+                                        e.stopPropagation();
+                                        this._scrubbing = true;
+                                    })}
+                                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
+                                    onPointerUp={action((e: React.PointerEvent) => {
+                                        e.stopPropagation();
+                                        this._scrubbing = false;
+                                    })}
+                                />
+                            </div>
+                        ) : (
+                            <div>/</div>
+                        )}
+
+                        <div className="timecode-end">{formatTime(this.timeline.clipDuration)}</div>
                     </div>
-                    :
-                    <div>/</div>}
-
-                <div className="timecode-end">
-                    {formatTime(this.timeline.clipDuration)}
-                </div>
-            </div>
-            }
-
-            <div className="videobox-button"
-                title={"full screen"}
-                onPointerDown={this.onFullDown}>
-                <FontAwesomeIcon icon="expand" />
-            </div>
-
-            {
-                !this._fullScreen && width > 300 && <div className="videobox-button"
-                    title={"show timeline"}
-                    onPointerDown={this.onTimelineHdlDown}>
-                    <FontAwesomeIcon icon="eye" />
-                </div>
-            }
+                )}
 
-            {
-                !this._fullScreen && width > 300 && <div className="videobox-button"
-                    title={this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
-                    onPointerDown={this.onClipPointerDown}>
-                    <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
+                <div className="videobox-button" title={'full screen'} onPointerDown={this.onFullDown}>
+                    <FontAwesomeIcon icon="expand" />
                 </div>
-            }
 
-            <div className="videobox-button"
-                title={this._muted ? "unmute" : "mute"}
-                onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
-                <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
-            </div>
-            {
-                width > 300 && <input type="range" style={{ width: `min(25%, 50px)` }} step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
-                    className="toolbar-slider volume"
-                    onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
-                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
-                />
-            }
+                {!this._fullScreen && width > 300 && (
+                    <div className="videobox-button" title={'show timeline'} onPointerDown={this.onTimelineHdlDown}>
+                        <FontAwesomeIcon icon="eye" />
+                    </div>
+                )}
 
-            {
-                !this._fullScreen && this.heightPercent !== 100 && width > 300 &&
-                <>
-                    <div className="videobox-button" title="zoom">
-                        <FontAwesomeIcon icon="search-plus" />
+                {!this._fullScreen && width > 300 && (
+                    <div className="videobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish trimming' : 'start trim'} onPointerDown={this.onClipPointerDown}>
+                        <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} />
                     </div>
-                    <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
-                        className="toolbar-slider zoom"
-                        onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
+                )}
+
+                <div
+                    className="videobox-button"
+                    title={this._muted ? 'unmute' : 'mute'}
+                    onPointerDown={e => {
+                        e.stopPropagation();
+                        this.toggleMute();
+                    }}>
+                    <FontAwesomeIcon icon={this._muted ? 'volume-mute' : 'volume-up'} />
+                </div>
+                {width > 300 && (
+                    <input
+                        type="range"
+                        style={{ width: `min(25%, 50px)` }}
+                        step="0.1"
+                        min="0"
+                        max="1"
+                        value={this._muted ? 0 : this._volume}
+                        className="toolbar-slider volume"
+                        onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
                     />
-                </>
-            }
-        </>
+                )}
+
+                {!this._fullScreen && this.heightPercent !== 100 && width > 300 && (
+                    <>
+                        <div className="videobox-button" title="zoom">
+                            <FontAwesomeIcon icon="search-plus" />
+                        </div>
+                        <input
+                            type="range"
+                            step="0.1"
+                            min="1"
+                            max="5"
+                            value={this.timeline?._zoomFactor}
+                            className="toolbar-slider zoom"
+                            onPointerDown={(e: React.PointerEvent) => {
+                                e.stopPropagation();
+                            }}
+                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                                this.zoom(Number(e.target.value));
+                            }}
+                        />
+                    </>
+                )}
+            </>
+        );
     }
 
     // renders CollectionStackedTimeline
     @computed get renderTimeline() {
-        return <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
-            <CollectionStackedTimeline ref={action((r: any) => this._stackedTimeline = r)} {...this.props}
-                fieldKey={this.annotationKey}
-                dictationKey={this.fieldKey + "-dictation"}
-                mediaPath={this.audiopath}
-                renderDepth={this.props.renderDepth + 1}
-                startTag={"_timecodeToShow" /* videoStart */}
-                endTag={"_timecodeToHide" /* videoEnd */}
-                bringToFront={emptyFunction}
-                CollectionView={undefined}
-                playFrom={this.playFrom}
-                setTime={this.setPlayheadTime}
-                playing={this.playing}
-                isAnyChildContentActive={this.isAnyChildContentActive}
-                whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
-                moveDocument={this.moveDocument}
-                addDocument={this.addDocument}
-                removeDocument={this.removeDocument}
-                ScreenToLocalTransform={this.timelineScreenToLocal}
-                Play={this.Play}
-                Pause={this.Pause}
-                playLink={this.playLink}
-                PanelHeight={this.timelineHeight}
-                rawDuration={this.rawDuration}
-            />
-        </div>;
+        return (
+            <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
+                <CollectionStackedTimeline
+                    ref={action((r: any) => (this._stackedTimeline = r))}
+                    {...this.props}
+                    fieldKey={this.annotationKey}
+                    dictationKey={this.fieldKey + '-dictation'}
+                    mediaPath={this.audiopath}
+                    renderDepth={this.props.renderDepth + 1}
+                    startTag={'_timecodeToShow' /* videoStart */}
+                    endTag={'_timecodeToHide' /* videoEnd */}
+                    bringToFront={emptyFunction}
+                    CollectionView={undefined}
+                    playFrom={this.playFrom}
+                    setTime={this.setPlayheadTime}
+                    playing={this.playing}
+                    isAnyChildContentActive={this.isAnyChildContentActive}
+                    whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
+                    moveDocument={this.moveDocument}
+                    addDocument={this.addDocument}
+                    removeDocument={this.removeDocument}
+                    ScreenToLocalTransform={this.timelineScreenToLocal}
+                    Play={this.Play}
+                    Pause={this.Pause}
+                    playLink={this.playLink}
+                    PanelHeight={this.timelineHeight}
+                    rawDuration={this.rawDuration}
+                />
+            </div>
+        );
     }
 
     // renders annotation layer
@@ -982,59 +1087,72 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     savedAnnotations = () => this._savedAnnotations;
     render() {
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
-        const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
-        return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
-            style={{
-                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
-                borderRadius,
-                overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
-            }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
-            <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
-                <div style={{
-                    position: "absolute", transition: this.transition,
-                    width: this.panelWidth(),
-                    height: this.panelHeight(),
-                    top: 0,
-                    left: (this.props.PanelWidth() - this.panelWidth()) / 2
+        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / this.scaling()}px` : borderRad;
+        return (
+            <div
+                className="videoBox"
+                onContextMenu={this.specificContextMenu}
+                ref={this._mainCont}
+                style={{
+                    pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
+                    borderRadius,
+                    overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? 'auto' : undefined,
+                }}
+                onWheel={e => {
+                    e.stopPropagation();
+                    e.preventDefault();
                 }}>
-                    <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                        renderDepth={this.props.renderDepth + 1}
-                        fieldKey={this.annotationKey}
-                        CollectionView={undefined}
-                        isAnnotationOverlay={true}
-                        annotationLayerHostsContent={true}
-                        PanelWidth={this.panelWidth}
-                        PanelHeight={this.panelHeight}
-                        ScreenToLocalTransform={this.screenToLocalTransform}
-                        docFilters={this.timelineDocFilter}
-                        select={emptyFunction}
-                        scaling={returnOne}
-                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                        removeDocument={this.removeDocument}
-                        moveDocument={this.moveDocument}
-                        addDocument={this.addDocWithTimecode}>
-                        {this.contentFunc}
-                    </CollectionFreeFormView>
+                <div className="videoBox-viewer" onPointerDown={this.marqueeDown}>
+                    <div
+                        style={{
+                            position: 'absolute',
+                            transition: this.transition,
+                            width: this.panelWidth(),
+                            height: this.panelHeight(),
+                            top: 0,
+                            left: (this.props.PanelWidth() - this.panelWidth()) / 2,
+                        }}>
+                        <CollectionFreeFormView
+                            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                            renderDepth={this.props.renderDepth + 1}
+                            fieldKey={this.annotationKey}
+                            CollectionView={undefined}
+                            isAnnotationOverlay={true}
+                            annotationLayerHostsContent={true}
+                            PanelWidth={this.panelWidth}
+                            PanelHeight={this.panelHeight}
+                            ScreenToLocalTransform={this.screenToLocalTransform}
+                            docFilters={this.timelineDocFilter}
+                            select={emptyFunction}
+                            scaling={returnOne}
+                            whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                            removeDocument={this.removeDocument}
+                            moveDocument={this.moveDocument}
+                            addDocument={this.addDocWithTimecode}>
+                            {this.contentFunc}
+                        </CollectionFreeFormView>
+                    </div>
+                    {this.annotationLayer}
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <MarqueeAnnotator
+                            rootDoc={this.rootDoc}
+                            scrollTop={0}
+                            down={this._marqueeing}
+                            scaling={this.marqueeFitScaling}
+                            docView={this.props.docViewPath().slice(-1)[0]}
+                            containerOffset={this.marqueeOffset}
+                            addDocument={this.addDocWithTimecode}
+                            finishMarquee={this.finishMarquee}
+                            savedAnnotations={this.savedAnnotations}
+                            annotationLayer={this._annotationLayer.current}
+                            mainCont={this._mainCont.current}
+                        />
+                    )}
+                    {this.renderTimeline}
                 </div>
-                {this.annotationLayer}
-                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                    <MarqueeAnnotator
-                        rootDoc={this.rootDoc}
-                        scrollTop={0}
-                        down={this._marqueeing}
-                        scaling={this.marqueeFitScaling}
-                        docView={this.props.docViewPath().slice(-1)[0]}
-                        containerOffset={this.marqueeOffset}
-                        addDocument={this.addDocWithTimecode}
-                        finishMarquee={this.finishMarquee}
-                        savedAnnotations={this.savedAnnotations}
-                        annotationLayer={this._annotationLayer.current}
-                        mainCont={this._mainCont.current}
-                    />}
-                {this.renderTimeline}
             </div>
-        </div >);
+        );
     }
 }
 
-VideoBox._nativeControls = false;
\ No newline at end of file
+VideoBox._nativeControls = false;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index d14af49ea..d97277c2b 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,48 +1,48 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
 import * as WebRequest from 'web-request';
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { HtmlField } from "../../../fields/HtmlField";
-import { InkTool } from "../../../fields/InkField";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, ImageCast, NumCast, StrCast } from "../../../fields/Types";
-import { ImageField, WebField } from "../../../fields/URLField";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SnappingManager } from "../../util/SnappingManager";
-import { undoBatch } from "../../util/UndoManager";
-import { MarqueeOptionsMenu } from "../collections/collectionFreeForm";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
-import { DocumentDecorations } from "../DocumentDecorations";
-import { Colors } from "../global/globalEnums";
-import { LightboxView } from "../LightboxView";
-import { MarqueeAnnotator } from "../MarqueeAnnotator";
-import { AnchorMenu } from "../pdf/AnchorMenu";
-import { Annotation } from "../pdf/Annotation";
-import { SidebarAnnos } from "../SidebarAnnos";
-import { StyleProp } from "../StyleProvider";
-import { DocumentViewProps } from "./DocumentView";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { HtmlField } from '../../../fields/HtmlField';
+import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { ImageField, WebField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SnappingManager } from '../../util/SnappingManager';
+import { undoBatch } from '../../util/UndoManager';
+import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { DocumentDecorations } from '../DocumentDecorations';
+import { Colors } from '../global/globalEnums';
+import { LightboxView } from '../LightboxView';
+import { MarqueeAnnotator } from '../MarqueeAnnotator';
+import { AnchorMenu } from '../pdf/AnchorMenu';
+import { Annotation } from '../pdf/Annotation';
+import { SidebarAnnos } from '../SidebarAnnos';
+import { StyleProp } from '../StyleProvider';
+import { DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
-import { LinkDocPreview } from "./LinkDocPreview";
-import { VideoBox } from "./VideoBox";
-import "./WebBox.scss";
-import React = require("react");
-const { CreateImage } = require("./WebBoxRenderer");
-const _global = (window /* browser */ || global /* node */) as any;
-const htmlToText = require("html-to-text");
+import { LinkDocPreview } from './LinkDocPreview';
+import { VideoBox } from './VideoBox';
+import './WebBox.scss';
+import React = require('react');
+const { CreateImage } = require('./WebBoxRenderer');
+const _global = (window /* browser */ || global) /* node */ as any;
+const htmlToText = require('html-to-text');
 @observer
 export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(WebBox, fieldKey);
+    }
     public static openSidebarWidth = 250;
     public static sidebarResizerWidth = 5;
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
@@ -55,9 +55,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _searchRef = React.createRef<HTMLInputElement>();
-    private _searchString = "";
-    @observable private _webUrl = "";  // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render.  
-    @observable private _hackHide = false;  // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe.  so thisforces the ifrmae to be rebuilt when allowScripts is toggled
+    private _searchString = '';
+    @observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render.
+    @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe.  so thisforces the ifrmae to be rebuilt when allowScripts is toggled
     @observable private _searching: boolean = false;
     @observable private _showSidebar = false;
     @observable private _scrollTimer: any;
@@ -69,22 +69,41 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight);
-    @computed get _url() { return this.webField?.toString() || ""; }
-    @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; }
-    @computed get scrollHeight() { return Math.max(this.layoutDoc[HeightSym](), this._scrollHeight); }
-    @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
-    @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
-    @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
+    @computed get _url() {
+        return this.webField?.toString() || '';
+    }
+    @computed get _urlHash() {
+        return this._url ? WebBox.urlHash(this._url) + '' : '';
+    }
+    @computed get scrollHeight() {
+        return Math.max(this.layoutDoc[HeightSym](), this._scrollHeight);
+    }
+    @computed get allAnnotations() {
+        return DocListCast(this.dataDoc[this.annotationKey]);
+    }
+    @computed get inlineTextAnnotations() {
+        return this.allAnnotations.filter(a => a.textInlineAnnotations);
+    }
+    @computed get webField() {
+        return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url;
+    }
     @computed get webThumb() {
-        return this.props.thumbShown?.() &&
-            ImageCast(this.layoutDoc["thumb-frozen"],
-                ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) &&
-                    this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight) ? this.layoutDoc.thumb : undefined))?.url;
+        return (
+            this.props.thumbShown?.() &&
+            ImageCast(
+                this.layoutDoc['thumb-frozen'],
+                ImageCast(
+                    this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) && this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight)
+                        ? this.layoutDoc.thumb
+                        : undefined
+                )
+            )?.url
+        );
     }
 
     constructor(props: any) {
         super(props);
-        runInAction(() => this._webUrl = this._url);  // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
+        runInAction(() => (this._webUrl = this._url)); // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
     }
 
     @action
@@ -104,7 +123,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true);
         }
         return true;
-    }
+    };
     @action
     setScrollPos = (pos: number) => {
         if (!this._outerRef.current || this._outerRef.current.scrollHeight < pos) {
@@ -113,14 +132,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._outerRef.current.scrollTop = pos;
             this._initialScroll = undefined;
         }
-    }
+    };
 
     lockout = false;
     updateThumb = async () => {
-        const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
+        const imageBitmap = ImageCast(this.layoutDoc['thumb-frozen'])?.url.href;
         const scrollTop = NumCast(this.layoutDoc._scrollTop);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
-        const nativeHeight = nativeWidth * this.props.PanelHeight() / this.props.PanelWidth();
+        const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
         if (!this.lockout && this._iframe && !imageBitmap && (scrollTop !== this.layoutDoc.thumbScrollTop || nativeWidth !== this.layoutDoc.thumbNativeWidth || nativeHeight !== this.layoutDoc.thumbNativeHeight)) {
             var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
             if (!htmlString) {
@@ -128,60 +147,65 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
             this.layoutDoc.thumb = undefined;
             this.lockout = true; // lock to prevent multiple thumb updates.
-            CreateImage(
-                this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl,
-                this._iframe.contentDocument?.styleSheets ?? [],
-                htmlString,
-                nativeWidth,
-                nativeHeight,
-                scrollTop
-            ).then
-                ((data_url: any) => {
-                    VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
-                        returnedfilename => setTimeout(action(() => {
-                            this.lockout = false;
-                            this.layoutDoc.thumb = new ImageField(returnedfilename);
-                            this.layoutDoc.thumbScrollTop = scrollTop;
-                            this.layoutDoc.thumbNativeWidth = nativeWidth;
-                            this.layoutDoc.thumbNativeHeight = nativeHeight;
-                        }), 500));
+            CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop)
+                .then((data_url: any) => {
+                    VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename =>
+                        setTimeout(
+                            action(() => {
+                                this.lockout = false;
+                                this.layoutDoc.thumb = new ImageField(returnedfilename);
+                                this.layoutDoc.thumbScrollTop = scrollTop;
+                                this.layoutDoc.thumbNativeWidth = nativeWidth;
+                                this.layoutDoc.thumbNativeHeight = nativeHeight;
+                            }),
+                            500
+                        )
+                    );
                 })
                 .catch(function (error: any) {
                     console.error('oops, something went wrong!', error);
                 });
         }
-    }
+    };
     async componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
         runInAction(() => {
-            this._annotationKeySuffix = () => this._urlHash + "-annotations";
-            const reqdFuncs:{[key:string]: string} = {};
+            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)
-            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);
+            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"`;
+            DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
         });
-        reaction(() => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
-            async (selected) => {
+        reaction(
+            () => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
+            async selected => {
                 if (selected) {
                     this._webPageHasBeenRendered = true;
-                } 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)
+                } 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.
+                    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(true) || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegreeUnmemoized(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,
+        this._disposers.autoHeight = reaction(
+            () => this.layoutDoc._autoHeight,
             autoHeight => {
                 if (autoHeight) {
-                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.scaling?.() || 1));
                 }
-            });
+            }
+        );
 
-        if (this.webField?.href.indexOf("youtube") !== -1) {
+        if (this.webField?.href.indexOf('youtube') !== -1) {
             const youtubeaspect = 400 / 315;
             const nativeWidth = Doc.NativeWidth(this.layoutDoc);
             const nativeHeight = Doc.NativeHeight(this.layoutDoc);
@@ -199,8 +223,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
 
-        this._disposers.scrollReaction = reaction(() => NumCast(this.layoutDoc._scrollTop),
-            (scrollTop) => {
+        this._disposers.scrollReaction = reaction(
+            () => NumCast(this.layoutDoc._scrollTop),
+            scrollTop => {
                 const viewTrans = StrCast(this.Document._viewTransition);
                 const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
                 const durationSecStr = viewTrans.match(/([0-9.]*)s/);
@@ -223,13 +248,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
                 if (rect && rect.width !== this._mainCont.current.clientWidth) {
-                    const annoBox = document.createElement("div");
-                    annoBox.className = "marqueeAnnotator-annotationBox";
+                    const annoBox = document.createElement('div');
+                    annoBox.className = 'marqueeAnnotator-annotationBox';
                     // transforms the positions from screen onto the pdf div
                     annoBox.style.top = (rect.top + this._mainCont.current.scrollTop).toString();
-                    annoBox.style.left = (rect.left).toString();
-                    annoBox.style.width = (rect.width).toString();
-                    annoBox.style.height = (rect.height).toString();
+                    annoBox.style.left = rect.left.toString();
+                    annoBox.style.width = rect.width.toString();
+                    annoBox.style.height = rect.height.toString();
                     this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, 1);
                 }
             }
@@ -237,23 +262,22 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //this._selectionText = selRange.cloneContents().textContent || "";
 
         // clear selection
-        if (sel.empty) sel.empty();// Chrome
-        else if (sel.removeAllRanges) sel.removeAllRanges();  // Firefox
+        if (sel.empty) sel.empty(); // Chrome
+        else if (sel.removeAllRanges) sel.removeAllRanges(); // Firefox
         return this._savedAnnotations;
-    }
+    };
 
     menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
         if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth);
-        if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
         }
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         if (doc !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1,
-                Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
+            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
             if (scrollTo !== undefined && this._initialScroll === undefined) {
                 const focusSpeed = smooth ? 500 : 0;
                 this.goTo(scrollTo, focusSpeed);
@@ -263,22 +287,22 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
         return undefined;
-    }
+    };
 
     getAnchor = () => {
         const anchor =
             this._getAnchor(this._savedAnnotations) ??
             Docs.Create.WebanchorDocument(this._url, {
-                title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
+                title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
                 y: NumCast(this.layoutDoc._scrollTop),
-                unrendered: true
+                unrendered: true,
             });
         this.addDocumentWrapper(anchor);
         return anchor;
-    }
+    };
 
     _textAnnotationCreator: (() => ObservableMap<number, HTMLDivElement[]>) | undefined;
-    savedAnnotationsCreator: (() => ObservableMap<number, HTMLDivElement[]>) = () => this._textAnnotationCreator?.() || this._savedAnnotations;
+    savedAnnotationsCreator: () => ObservableMap<number, HTMLDivElement[]> = () => this._textAnnotationCreator?.() || this._savedAnnotations;
 
     @action
     iframeUp = (e: PointerEvent) => {
@@ -290,11 +314,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
-                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX,
-                    e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
+                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
             }
         }
-    }
+    };
     @action
     iframeDown = (e: PointerEvent) => {
         const sel = this._iframe?.contentWindow?.getSelection?.();
@@ -303,10 +326,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
         this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-        this._marqueeing = [e.clientX * scale + mainContBounds.translateX,
-        e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
-        if (word || (e.target as any || "").className.includes("rangeslider") || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
-            setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+        this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
+        if (word || ((e.target as any) || '').className.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
+            setTimeout(
+                action(() => (this._marqueeing = undefined)),
+                100
+            ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
         } else {
             this._iframeClick = this._iframe ?? undefined;
             this._isAnnotating = true;
@@ -322,13 +347,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             ContextMenu.Instance.closeMenu();
             ContextMenu.Instance.setIgnoreEvents(true);
         }
-    }
+    };
 
     getScrollHeight = () => this._scrollHeight;
 
     isFirefox = () => {
-        return "InstallTrigger" in window; // navigator.userAgent.indexOf("Chrome") !== -1;
-    }
+        return 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
+    };
     iframeClick = () => this._iframeClick;
     iframeScaling = () => 1 / this.props.ScreenToLocalTransform().Scale;
 
@@ -338,7 +363,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (this._initialScroll !== undefined) {
             this.setScrollPos(this._initialScroll);
         }
-        let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend("") + "/corsProxy/", "") ?? this._url.toString());
+        let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend('') + '/corsProxy/', '') ?? this._url.toString());
         if (requrlraw !== this._url.toString()) {
             if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) {
                 const matches = requrlraw.match(/[^a-zA-z]q=[^&]*/g);
@@ -346,63 +371,79 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (matches) {
                     requrlraw = requrlraw.substring(0, requrlraw.indexOf(newsearch));
                     for (let i = 1; i < Array.from(matches)?.length; i++) {
-                        requrlraw = requrlraw.replace(matches[i], "");
+                        requrlraw = requrlraw.replace(matches[i], '');
                     }
                 }
-                requrlraw = requrlraw.replace(/q=[^&]*/, newsearch.substring(1)).replace("search&", "search?").replace("?gbv=1", "");
+                requrlraw = requrlraw
+                    .replace(/q=[^&]*/, newsearch.substring(1))
+                    .replace('search&', 'search?')
+                    .replace('?gbv=1', '');
             }
             this.submitURL(requrlraw, undefined, true);
         }
         if (iframe?.contentDocument) {
-            iframe.contentDocument.addEventListener("pointerup", this.iframeUp);
-            iframe.contentDocument.addEventListener("pointerdown", this.iframeDown);
+            iframe.contentDocument.addEventListener('pointerup', this.iframeUp);
+            iframe.contentDocument.addEventListener('pointerdown', this.iframeDown);
             this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument.body.scrollHeight);
-            setTimeout(action(() => this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000);
-            iframe.setAttribute("enable-annotation", "true");
-            iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => {
-                let href = "";
-                for (let ele = e.target as any; ele; ele = ele.parentElement) {
-                    href = (typeof (ele.href) === "string" ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href;
-                }
-                const origin = this.webField?.origin;
-                if (href && origin) {
-                    e.stopPropagation();
-                    setTimeout(() => this.submitURL(href.replace(Utils.prepend(""), origin)));
-                    if (this._outerRef.current) {
-                        this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
-                        this._outerRef.current.scrollLeft = 0;
-                    }
-                }
-            })));
+            setTimeout(
+                action(() => (this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0))),
+                5000
+            );
+            iframe.setAttribute('enable-annotation', 'true');
+            iframe.contentDocument.addEventListener(
+                'click',
+                undoBatch(
+                    action((e: MouseEvent) => {
+                        let href = '';
+                        for (let ele = e.target as any; ele; ele = ele.parentElement) {
+                            href = (typeof ele.href === 'string' ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href;
+                        }
+                        const origin = this.webField?.origin;
+                        if (href && origin) {
+                            e.stopPropagation();
+                            setTimeout(() => this.submitURL(href.replace(Utils.prepend(''), origin)));
+                            if (this._outerRef.current) {
+                                this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
+                                this._outerRef.current.scrollLeft = 0;
+                            }
+                        }
+                    })
+                )
+            );
             iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false);
             //iframe.contentDocument.addEventListener('scroll', () => !this.active() && this._iframe && (this._iframe.scrollTop = NumCast(this.layoutDoc._scrollTop), false));
         }
-    }
+    };
 
     @action
     iframeWheel = (e: any) => {
         if (!this._scrollTimer) {
-            this._scrollTimer = setTimeout(action(() => this._scrollTimer = undefined), 250); // this turns events off on the iframe which allows scrolling to change direction smoothly
+            this._scrollTimer = setTimeout(
+                action(() => (this._scrollTimer = undefined)),
+                250
+            ); // this turns events off on the iframe which allows scrolling to change direction smoothly
         }
-    }
+    };
 
     @action
     setDashScrollTop = (scrollTop: number, timeout: number = 250) => {
         const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight());
         this._scrollTimer && clearTimeout(this._scrollTimer);
-        this._scrollTimer = setTimeout(action(() => {
-            this._scrollTimer = undefined;
-            const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
-            if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop &&
-                (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
-                this.layoutDoc.thumb = undefined;
-                this.layoutDoc.thumbScrollTop = undefined;
-                this.layoutDoc.thumbNativeWidth = undefined;
-                this.layoutDoc.thumbNativeHeight = undefined;
-                this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
-            } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
-        }), timeout);
-    }
+        this._scrollTimer = setTimeout(
+            action(() => {
+                this._scrollTimer = undefined;
+                const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
+                if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
+                    this.layoutDoc.thumb = undefined;
+                    this.layoutDoc.thumbScrollTop = undefined;
+                    this.layoutDoc.thumbNativeWidth = undefined;
+                    this.layoutDoc.thumbNativeHeight = undefined;
+                    this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
+                } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
+            }),
+            timeout
+        );
+    };
 
     goTo = (scrollTop: number, duration: number) => {
         if (this._outerRef.current) {
@@ -414,20 +455,20 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.setDashScrollTop(scrollTop);
             }
         } else this._initialScroll = scrollTop;
-    }
+    };
 
     forward = (checkAvailable?: boolean) => {
-        const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []);
-        const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
+        const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'), []);
+        const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []);
         if (checkAvailable) return future.length;
         runInAction(() => {
             if (future.length) {
                 const curUrl = this._url;
-                this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
+                this.dataDoc[this.fieldKey + '-history'] = new List<string>([...history, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
                 if (this._webUrl === this._url) {
                     this._webUrl = curUrl;
-                    setTimeout(action(() => this._webUrl = this._url));
+                    setTimeout(action(() => (this._webUrl = this._url)));
                 } else {
                     this._webUrl = this._url;
                 }
@@ -435,21 +476,21 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         });
         return false;
-    }
+    };
 
     back = (checkAvailable?: boolean) => {
-        const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"));
-        const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
+        const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'));
+        const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []);
         if (checkAvailable) return history.length;
         runInAction(() => {
             if (history.length) {
                 const curUrl = this._url;
-                if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
-                else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
+                if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List<string>([this._url]);
+                else this.dataDoc[this.fieldKey + '-future'] = new List<string>([...future, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
                 if (this._webUrl === this._url) {
                     this._webUrl = curUrl;
-                    setTimeout(action(() => this._webUrl = this._url));
+                    setTimeout(action(() => (this._webUrl = this._url)));
                 } else {
                     this._webUrl = this._url;
                 }
@@ -457,21 +498,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         });
         return false;
-    }
+    };
 
     static urlHash = (s: string) => {
-        return Math.abs(s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0));
-    }
+        return Math.abs(
+            s.split('').reduce((a: any, b: any) => {
+                a = (a << 5) - a + b.charCodeAt(0);
+                return a & a;
+            }, 0)
+        );
+    };
     @action
     submitURL = (newUrl?: string, preview?: boolean, dontUpdateIframe?: boolean) => {
         if (!newUrl) return;
-        if (!newUrl.startsWith("http")) newUrl = "http://" + newUrl;
+        if (!newUrl.startsWith('http')) newUrl = 'http://' + newUrl;
         try {
-            const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"));
-            const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"));
+            const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'));
+            const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'));
             const url = this.webField?.toString();
             if (url && !preview) {
-                this.dataDoc[this.fieldKey + "-history"] = new List<string>([...(history || []), url]);
+                this.dataDoc[this.fieldKey + '-history'] = new List<string>([...(history || []), url]);
                 this.layoutDoc._scrollTop = 0;
                 if (this._webPageHasBeenRendered) {
                     this.layoutDoc.thumb = undefined;
@@ -486,47 +532,54 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 !dontUpdateIframe && (this._webUrl = this._url);
             }
         } catch (e) {
-            console.log("WebBox URL error:" + this._url);
+            console.log('WebBox URL error:' + this._url);
         }
         return true;
-    }
+    };
 
     onWebUrlDrop = (e: React.DragEvent) => {
         const { dataTransfer } = e;
-        const html = dataTransfer.getData("text/html");
-        const uri = dataTransfer.getData("text/uri-list");
-        const url = uri || html || this._url || "";
-        const newurl = url.startsWith(window.location.origin) ?
-            url.replace(window.location.origin, this._url?.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url;
+        const html = dataTransfer.getData('text/html');
+        const uri = dataTransfer.getData('text/uri-list');
+        const url = uri || html || this._url || '';
+        const newurl = url.startsWith(window.location.origin) ? url.replace(window.location.origin, this._url?.match(/http[s]?:\/\/[^\/]*/)?.[0] || '') : url;
         this.submitURL(newurl);
         e.stopPropagation();
-    }
+    };
     onWebUrlValueKeyDown = (e: React.KeyboardEvent) => {
-        e.key === "Enter" && this.submitURL(this._keyInput.current!.value);
+        e.key === 'Enter' && this.submitURL(this._keyInput.current!.value);
         e.stopPropagation();
-    }
+    };
 
     @computed get urlEditor() {
         return (
-            <div className="collectionMenu-webUrlButtons" onDrop={this.onWebUrlDrop} onDragOver={e => e.preventDefault()} >
-                <input className="collectionMenu-urlInput" key={this._url}
+            <div className="collectionMenu-webUrlButtons" onDrop={this.onWebUrlDrop} onDragOver={e => e.preventDefault()}>
+                <input
+                    className="collectionMenu-urlInput"
+                    key={this._url}
                     placeholder="ENTER URL"
                     defaultValue={this._url}
                     onDrop={this.onWebUrlDrop}
                     onDragOver={e => e.preventDefault()}
                     onKeyDown={this.onWebUrlValueKeyDown}
-                    onClick={(e) => {
+                    onClick={e => {
                         this._keyInput.current!.select();
                         e.stopPropagation();
                     }}
                     ref={this._keyInput}
                 />
-                <div style={{ display: "flex", flexDirection: "row", justifyContent: "space-between", maxWidth: "250px", }}>
+                <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', maxWidth: '250px' }}>
                     <button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={e => e.stopPropagation()} onDrop={this.onWebUrlDrop}>
                         GO
                     </button>
-                    <button className="submitUrl" onClick={() => this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button>
-                    <button className="submitUrl" onClick={() => this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" />  </button>
+                    <button className="submitUrl" onClick={() => this.back}>
+                        {' '}
+                        <FontAwesomeIcon icon="caret-left" size="lg" />{' '}
+                    </button>
+                    <button className="submitUrl" onClick={() => this.forward}>
+                        {' '}
+                        <FontAwesomeIcon icon="caret-right" size="lg" />{' '}
+                    </button>
                 </div>
             </div>
         );
@@ -535,48 +588,59 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     specificContextMenu = (e: React.MouseEvent | PointerEvent): void => {
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
-        if (!cm.findByDescription("Options...")) {
-            !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
+        if (!cm.findByDescription('Options...')) {
+            !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: () => {
+                description: (this.layoutDoc.allowScripts ? 'Prevent' : 'Allow') + ' Scripts',
+                event: () => {
                     this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts;
                     if (this._iframe) {
-                        runInAction(() => this._hackHide = true);
-                        setTimeout(action(() => this._hackHide = false));
+                        runInAction(() => (this._hackHide = true));
+                        setTimeout(action(() => (this._hackHide = false)));
                     }
-                }, icon: "snowflake"
+                },
+                icon: 'snowflake',
             });
             funcs.push({
-                description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => {
+                description: (!this.layoutDoc.forceReflow ? 'Force' : 'Prevent') + ' Reflow',
+                event: () => {
                     const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
                     this.layoutDoc.forceReflow = !nw;
                     if (nw) {
-                        Doc.SetInPlace(this.layoutDoc, this.fieldKey + "-nativeWidth", nw, true);
+                        Doc.SetInPlace(this.layoutDoc, this.fieldKey + '-nativeWidth', nw, true);
                     }
-                }, icon: "snowflake"
+                },
+                icon: 'snowflake',
             });
-            cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+            cm.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
         }
-    }
+    };
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        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];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
         }
-    }
+    };
     @action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
         this._getAnchor = AnchorMenu.Instance?.GetAnchor;
         this._marqueeing = undefined;
         this._isAnnotating = false;
         this._iframeClick = undefined;
         const sel = this._iframe?.contentDocument?.getSelection();
-        if (sel?.empty) sel.empty();// Chrome
-        else if (sel?.removeAllRanges) sel.removeAllRanges();  // Firefox
+        if (sel?.empty) sel.empty(); // Chrome
+        else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         if (x !== undefined && y !== undefined) {
             this._setPreviewCursor?.(x, y, false, false);
             ContextMenu.Instance.closeMenu();
@@ -586,10 +650,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
             }
         }
-    }
+    };
 
     @computed get urlContent() {
-        if (this._hackHide || (this.webThumb && (!this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc))) return (null);
+        if (this._hackHide || (this.webThumb && !this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc)) return null;
         this.props.thumbShown?.();
         const field = this.dataDoc[this.props.fieldKey];
         let view;
@@ -597,149 +661,194 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             view = <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
         } else if (field instanceof WebField) {
             const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl;
-            view = <iframe className="webBox-iframe" enable-annotation={"true"}
-                style={{ pointerEvents: this._scrollTimer ? "none" : undefined }}
-                ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded}
-                // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
-                // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
-                sandbox={`${this.layoutDoc.allowScripts ? "allow-scripts" : ""} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`} />;
+            view = (
+                <iframe
+                    className="webBox-iframe"
+                    enable-annotation={'true'}
+                    style={{ pointerEvents: this._scrollTimer ? 'none' : undefined }}
+                    ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
+                    src={url}
+                    onLoad={this.iframeLoaded}
+                    // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
+                    // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
+                    sandbox={`${this.layoutDoc.allowScripts ? 'allow-scripts' : ''} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`}
+                />
+            );
         } else {
-            view = <iframe className="webBox-iframe" enable-annotation={"true"}
-                style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
-                ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
+            view = (
+                <iframe
+                    className="webBox-iframe"
+                    enable-annotation={'true'}
+                    style={{ pointerEvents: this._scrollTimer ? 'none' : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
+                    ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
+                    src={'https://crossorigin.me/https://cs.brown.edu'}
+                />
+            );
         }
-        setTimeout(action(() => {
-            this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0);
-            if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
-                this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
-            }
-            this._webPageHasBeenRendered = true;
-        }));
+        setTimeout(
+            action(() => {
+                this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0);
+                if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
+                    this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+                }
+                this._webPageHasBeenRendered = true;
+            })
+        );
         return view;
     }
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => doc.webUrl = this._url);
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.webUrl = this._url));
         return this.addDocument(doc, annotationKey);
-    }
+    };
 
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         return this.addDocumentWrapper(doc, sidebarKey);
-    }
+    };
     @observable _draggingSidebar = false;
-    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
-        setupMoveUpEvents(this, e, action((e, down, delta) => {
-            this._draggingSidebar = true;
-            const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
-            const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-            const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"]);
-            const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-            const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
-            if (ratio >= 1) {
-                this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
-                onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
-                this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
-            }
-            return false;
-        }), action(() => this._draggingSidebar = false), () => this.toggleSidebar());
-    }
+    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => {
+        // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
+        setupMoveUpEvents(
+            this,
+            e,
+            action((e, down, delta) => {
+                this._draggingSidebar = true;
+                const localDelta = this.props
+                    .ScreenToLocalTransform()
+                    .scale(this.props.scaling?.() || 1)
+                    .transformDirection(delta[0], delta[1]);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+                const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight']);
+                const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
+                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / nativeWidth;
+                if (ratio >= 1) {
+                    this.layoutDoc.nativeWidth = nativeWidth * ratio;
+                    this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
+                    onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
+                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                }
+                return false;
+            }),
+            action(() => (this._draggingSidebar = false)),
+            () => this.toggleSidebar()
+        );
+    };
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
-        var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
+        var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
         if (!nativeWidth) {
             const defaultNativeWidth = this.dataDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym]();
             Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth);
-            Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * defaultNativeWidth);
-            nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
+            Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth);
+            nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
         }
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
         const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
         if (preview) {
             this._previewNativeWidth = nativeWidth * sideratio;
-            this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
+            this._previewWidth = (this.layoutDoc[WidthSym]() * nativeWidth * sideratio) / curNativeWidth;
             this._showSidebar = true;
-        }
-        else {
+        } else {
             this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar;
-            this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
+            this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
             if (!this.layoutDoc._showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) {
-                this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + "-nativeWidth"] = undefined;
+                this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '-nativeWidth'] = undefined;
             } else {
                 this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
             }
         }
     });
-    sidebarWidth = () => !this.SidebarShown ? 0 :
-        WebBox.sidebarResizerWidth + (this._previewWidth ? WebBox.openSidebarWidth :
-            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
+    sidebarWidth = () =>
+        !this.SidebarShown ? 0 : WebBox.sidebarResizerWidth + (this._previewWidth ? WebBox.openSidebarWidth : ((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.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%", }}>
-            {this.urlContent}
-        </div>;
+        const interactive =
+            !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.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%' }}>
+                {this.urlContent}
+            </div>
+        );
     }
 
     @computed get annotationLayer() {
         TraceMobx();
-        return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
-            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)}
-        </div>;
-
+        return (
+            <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+                {this.inlineTextAnnotations
+                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+                    .map(anno => (
+                        <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+                    ))}
+            </div>
+        );
+    }
+    @computed get SidebarShown() {
+        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
     }
-    @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
 
     @computed get searchUI() {
-        return <div className="webBox-ui"
-            onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
-            <div className="webBox-overlayCont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
-                <button className="webBox-overlayButton" title={"search"} />
-                <input className="webBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged}
-                    onKeyDown={e => { e.key === "Enter" && this.search(this._searchString, e.shiftKey); e.stopPropagation(); }} />
-                <button className="webBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}>
-                    <FontAwesomeIcon icon="search" size="sm" />
+        return (
+            <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
+                <div className="webBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
+                    <button className="webBox-overlayButton" title={'search'} />
+                    <input
+                        className="webBox-searchBar"
+                        placeholder="Search"
+                        ref={this._searchRef}
+                        onChange={this.searchStringChanged}
+                        onKeyDown={e => {
+                            e.key === 'Enter' && this.search(this._searchString, e.shiftKey);
+                            e.stopPropagation();
+                        }}
+                    />
+                    <button className="webBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}>
+                        <FontAwesomeIcon icon="search" size="sm" />
+                    </button>
+                </div>
+                <button
+                    className="webBox-overlayButton"
+                    title={'search'}
+                    onClick={action(() => {
+                        this._searching = !this._searching;
+                        this.search('', false, true);
+                    })}>
+                    <div className="webBox-overlayButton-arrow" onPointerDown={e => e.stopPropagation()} />
+                    <div className="webBox-overlayButton-iconCont" onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon icon={this._searching ? 'times' : 'search'} size="lg" />
+                    </div>
                 </button>
             </div>
-            <button className="webBox-overlayButton" title={"search"}
-                onClick={action(() => { this._searching = !this._searching; this.search("", false, true); })} >
-                <div className="webBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()} />
-                <div className="webBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
-                    <FontAwesomeIcon icon={this._searching ? "times" : "search"} size="lg" />
-                </div>
-            </button>
-        </div>;
+        );
     }
-    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
-    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
+    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
+    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
-    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")];
+    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
-    childStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+    childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
-            if (doc.textInlineAnnotations) return "none";
+            if (doc.textInlineAnnotations) return 'none';
         }
         return this.props.styleProvider?.(doc, props, property);
-    }
-    pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
-    annotationPointerEvents = () => this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none";
+    };
+    pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
+    annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() ? 'all' : 'none');
     render() {
-        const pointerEvents = this.layoutDoc._lockedPosition ? "none" : this.props.pointerEvents?.() as any;
+        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
-        const renderAnnotations = (docFilters?: () => string[]) =>
-            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+        const renderAnnotations = (docFilters?: () => string[]) => (
+            <CollectionFreeFormView
+                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
                 fieldKey={this.annotationKey}
@@ -749,7 +858,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 PanelHeight={this.panelHeight}
                 ScreenToLocalTransform={this.scrollXf}
                 scaling={returnOne}
-                dropAction={"alias"}
+                dropAction={'alias'}
                 docFilters={docFilters || this.basicFilter}
                 dontRenderDocuments={docFilters ? false : true}
                 select={emptyFunction}
@@ -760,61 +869,76 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 moveDocument={this.moveDocument}
                 addDocument={this.addDocument}
                 styleProvider={this.childStyleProvider}
-                childPointerEvents={this.props.isContentActive() ? "all" : undefined}
-                pointerEvents={this.annotationPointerEvents} />;
+                childPointerEvents={this.props.isContentActive() ? 'all' : undefined}
+                pointerEvents={this.annotationPointerEvents}
+            />
+        );
         return (
-            <div className="webBox" ref={this._mainCont}
-                style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? "none" : undefined }} >
+            <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? 'none' : undefined }}>
                 <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor) }} />
-                <div className="webBox-container" style={{
-                    width: `calc(${100 / scale}% - ${!this.SidebarShown ? 0 : (this.sidebarWidth() - WebBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
-                    transform: `scale(${scale})`,
-                    pointerEvents
-                }} onContextMenu={this.specificContextMenu}>
-                    <div className={"webBox-outerContent"} ref={this._outerRef}
+                <div
+                    className="webBox-container"
+                    style={{
+                        width: `calc(${100 / scale}% - ${!this.SidebarShown ? 0 : ((this.sidebarWidth() - WebBox.sidebarResizerWidth) / scale) * (this._previewWidth ? scale : 1)}px)`,
+                        transform: `scale(${scale})`,
+                        pointerEvents,
+                    }}
+                    onContextMenu={this.specificContextMenu}>
+                    <div
+                        className={'webBox-outerContent'}
+                        ref={this._outerRef}
                         style={{
                             height: `${100 / scale}%`,
-                            pointerEvents
+                            pointerEvents,
                         }}
-                        onWheel={e => { e.stopPropagation(); e.preventDefault(); }} // block wheel events from propagating since they're handled by the iframe
+                        onWheel={e => {
+                            e.stopPropagation();
+                            e.preventDefault();
+                        }} // block wheel events from propagating since they're handled by the iframe
                         onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
-                        onPointerDown={this.onMarqueeDown}
-                    >
-                        <div className={"webBox-innerContent"} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : "100%", pointerEvents }}>
+                        onPointerDown={this.onMarqueeDown}>
+                        <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : '100%', pointerEvents }}>
                             {this.content}
-                            <div style={{ mixBlendMode: "multiply" }}>
-                                {renderAnnotations(this.transparentFilter)}
-                            </div>
+                            <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                             {renderAnnotations(this.opaqueFilter)}
-                            {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
+                            {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                             {this.annotationLayer}
                         </div>
                     </div>
-                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                        <div style={{ transformOrigin: "top left", transform: `scale(${1 / scale})` }}>
-                            <MarqueeAnnotator rootDoc={this.rootDoc}
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <div style={{ transformOrigin: 'top left', transform: `scale(${1 / scale})` }}>
+                            <MarqueeAnnotator
+                                rootDoc={this.rootDoc}
                                 iframe={this.isFirefox() ? this.iframeClick : undefined}
                                 iframeScaling={this.isFirefox() ? this.iframeScaling : undefined}
                                 anchorMenuClick={this.anchorMenuClick}
                                 scrollTop={0}
-                                down={this._marqueeing} scaling={returnOne}
+                                down={this._marqueeing}
+                                scaling={returnOne}
                                 addDocument={this.addDocumentWrapper}
                                 docView={this.props.docViewPath().lastElement()}
                                 finishMarquee={this.finishMarquee}
                                 savedAnnotations={this.savedAnnotationsCreator}
                                 annotationLayer={this._annotationLayer.current}
-                                mainCont={this._mainCont.current} /> </div>}
-                </div >
-                <div className="webBox-sideResizer" style={{
-                    display: this.SidebarShown ? undefined : "none",
-                    width: WebBox.sidebarResizerWidth,
-                    left: `calc(100% - ${this.sidebarWidth() - WebBox.sidebarResizerWidth}px)`
-                }}
-                    onPointerDown={e => this.sidebarBtnDown(e, false)} />
-                <SidebarAnnos ref={this._sidebarRef}
+                                mainCont={this._mainCont.current}
+                            />{' '}
+                        </div>
+                    )}
+                </div>
+                <div
+                    className="webBox-sideResizer"
+                    style={{
+                        display: this.SidebarShown ? undefined : 'none',
+                        width: WebBox.sidebarResizerWidth,
+                        left: `calc(100% - ${this.sidebarWidth() - WebBox.sidebarResizerWidth}px)`,
+                    }}
+                    onPointerDown={e => this.sidebarBtnDown(e, false)}
+                />
+                <SidebarAnnos
+                    ref={this._sidebarRef}
                     {...this.props}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    fieldKey={this.fieldKey + "-" + this._urlHash}
+                    fieldKey={this.fieldKey + '-' + this._urlHash}
                     rootDoc={this.rootDoc}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
@@ -825,17 +949,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
                 />
-                <div className="webBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
+                <div
+                    className="webBox-overlayButton-sidebar"
+                    key="sidebar"
+                    title="Toggle Sidebar"
                     style={{
-                        display: !this.props.isContentActive() ? "none" : undefined,
-                        top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
-                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
+                        display: !this.props.isContentActive() ? 'none' : undefined,
+                        top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                     }}
-                    onPointerDown={e => this.sidebarBtnDown(e, true)} >
-                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                    onPointerDown={e => this.sidebarBtnDown(e, true)}>
+                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
                 </div>
-                {!this.props.isContentActive() ? (null) : this.searchUI}
-            </div>);
+                {!this.props.isContentActive() ? null : this.searchUI}
+            </div>
+        );
     }
 }
-ScriptingGlobals.add(function urlHash(url: string) { return WebBox.urlHash(url); });
\ No newline at end of file
+ScriptingGlobals.add(function urlHash(url: string) {
+    return WebBox.urlHash(url);
+});
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 539fb5c99..1ce97979e 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,7 +1,6 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -12,13 +11,11 @@ import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { WebField } from '../../../../fields/URLField';
 import { aggregateBounds, Utils } from '../../../../Utils';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
-import { CollectionViewType } from '../../collections/CollectionView';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
 import { EditableView } from '../../EditableView';
@@ -733,7 +730,7 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 
 export function checkInksToGroup() {
     // console.log("getting here to inks group");
-    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
+    if (Doc.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
@@ -746,7 +743,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.ActiveTool === InkTool.Write) {
+    if (Doc.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;
@@ -819,30 +816,30 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
 ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) {
     InkTranscription.Instance?.createInkGroup();
     if (checkResult) {
-        return (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool ? Colors.MEDIUM_BLUE : 'transparent';
+        return (Doc.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool ? Colors.MEDIUM_BLUE : 'transparent';
     }
     if (['circle', 'square', 'line'].includes(tool)) {
         if (GestureOverlay.Instance.InkShape === tool) {
-            CurrentUserUtils.ActiveTool = InkTool.None;
+            Doc.ActiveTool = InkTool.None;
             GestureOverlay.Instance.InkShape = InkTool.None;
         } else {
-            CurrentUserUtils.ActiveTool = InkTool.Pen;
+            Doc.ActiveTool = InkTool.Pen;
             GestureOverlay.Instance.InkShape = tool;
         }
     } else if (tool) {
         // pen or eraser
-        if (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance.InkShape) {
-            CurrentUserUtils.ActiveTool = InkTool.None;
+        if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape) {
+            Doc.ActiveTool = InkTool.None;
         } else if (tool == InkTool.Write) {
             // console.log("write mode selected - create groupDoc here!", tool)
-            CurrentUserUtils.ActiveTool = tool;
+            Doc.ActiveTool = tool;
             GestureOverlay.Instance.InkShape = '';
         } else {
-            CurrentUserUtils.ActiveTool = tool as any;
+            Doc.ActiveTool = tool as any;
             GestureOverlay.Instance.InkShape = '';
         }
     } else {
-        CurrentUserUtils.ActiveTool = InkTool.None;
+        Doc.ActiveTool = InkTool.None;
     }
 });
 
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 9d203b6cc..08f255cab 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,18 +1,17 @@
-import { IReactionDisposer, reaction, observable, action } from 'mobx';
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
 import { NodeSelection } from 'prosemirror-state';
+import * as ReactDOM from 'react-dom';
 import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
-import { Cast, StrCast, NumCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, Utils, returnTransparent } from '../../../../Utils';
+import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { ColorScheme } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
 import { DocumentView } from '../DocumentView';
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require('react');
-import * as ReactDOM from 'react-dom';
-import { observer } from 'mobx-react';
-import { ColorScheme } from '../../../util/SettingsManager';
 
 export class DashDocView {
     dom: HTMLSpanElement; // container for label and value
@@ -21,7 +20,7 @@ export class DashDocView {
         this.dom = document.createElement('span');
         this.dom.style.position = 'relative';
         this.dom.style.textIndent = '0';
-        this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'lightGray');
+        this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'lightGray');
         this.dom.style.width = node.attrs.width;
         this.dom.style.height = node.attrs.height;
         this.dom.style.display = node.attrs.hidden ? 'none' : 'inline-block';
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 940ed6386..8c8b74560 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -8,7 +8,6 @@ import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { Cast, StrCast } from '../../../../fields/Types';
 import { DocServer } from '../../../DocServer';
-import { CollectionViewType } from '../../collections/CollectionView';
 import './DashFieldView.scss';
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require('react');
@@ -16,6 +15,7 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 import { Tooltip } from '@material-ui/core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { CollectionViewType } from '../../../documents/DocumentTypes';
 
 export class DashFieldView {
     dom: HTMLDivElement; // container for label and value
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 61940ac09..cfaa428f9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -25,7 +25,6 @@ import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/G
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 import { DictationManager } from '../../../util/DictationManager';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
@@ -259,7 +258,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             e.preventDefault();
             e.stopPropagation();
             const targetCreator = (annotationOn?: Doc) => {
-                const target = CurrentUserUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn);
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn);
                 FormattedTextBox.SelectOnLoad = target[Id];
                 return target;
             };
@@ -386,7 +385,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             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(CurrentUserUtils.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
+            DocListCast(Doc.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);
@@ -411,7 +410,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             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(CurrentUserUtils.MyPublishedDocs, undefined, this.rootDoc);
+                    Doc.AddDocToList(Doc.MyPublishedDocs, undefined, this.rootDoc);
                 }
             }
         }
@@ -1774,7 +1773,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             );
         };
         return (
-            <div className={'formattedTextBox-sidebar' + (CurrentUserUtils.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+            <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                 {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
             </div>
         );
@@ -1785,7 +1784,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         const selected = active;
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
-        const interactive = (CurrentUserUtils.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
+        const interactive = (Doc.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/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 91f8d1efc..292c187e4 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -8,19 +8,19 @@ import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal
 import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
-import { PrefetchProxy } from '../../../../fields/Proxy';
 import { listSpec } from '../../../../fields/Schema';
 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';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
+import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
+import { SettingsManager } from '../../../util/SettingsManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
 import { MarqueeViewBounds } from '../../collections/collectionFreeForm';
-import { CollectionView, CollectionViewType } from '../../collections/CollectionView';
+import { CollectionView } from '../../collections/CollectionView';
 import { TabDocView } from '../../collections/TabDocView';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
@@ -29,8 +29,6 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import './PresBox.scss';
 import { PresEffect, PresMovement, PresStatus } from './PresEnums';
-import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
-import { PresElementBox } from '.';
 
 export interface PinProps {
     audioRange?: boolean;
@@ -155,7 +153,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     constructor(props: any) {
         super(props);
-        if ((CurrentUserUtils.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
+        if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
         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() {
@@ -195,7 +193,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.layoutDoc._gridGap = 0;
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
-        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, 'data', this.rootDoc));
+        DocListCastAsync(Doc.MyTrails.data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.MyTrails, 'data', this.rootDoc));
         this._disposers.selection = reaction(
             () => SelectionManager.Views(),
             views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation()
@@ -204,8 +202,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
-        if (pres) CurrentUserUtils.ActivePresentation = pres;
-        else CurrentUserUtils.ActivePresentation = this.rootDoc;
+        if (pres) Doc.ActivePresentation = pres;
+        else Doc.ActivePresentation = this.rootDoc;
         document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
         document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = true;
@@ -648,9 +646,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     updateMinimize = async () => {
-        if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+        if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
-            Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
+            Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, 'right');
         } else {
             this.layoutDoc.presStatus = PresStatus.Edit;
@@ -660,7 +658,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.rootDoc.y = pt[1] + 10;
             this.rootDoc._height = 30;
             this.rootDoc._width = 248;
-            Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
+            Doc.AddDocToList(Doc.MyOverlayDocs, undefined, this.rootDoc);
             this.props.removeDocument?.(this.layoutDoc);
         }
     };
@@ -768,7 +766,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
     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;
+        Doc.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
 
     /**
      * For sorting the array so that the order is maintained when it is dropped.
@@ -918,7 +916,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
                 break;
             case 'Escape':
-                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+                if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
                     this.updateMinimize();
                 } else if (this.layoutDoc.presStatus === 'edit') {
                     this._selectedArray.clear();
@@ -2714,19 +2712,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     toggleProperties = () => {
-        if (CurrentUserUtils.propertiesWidth > 0) {
-            CurrentUserUtils.propertiesWidth = 0;
+        if (SettingsManager.propertiesWidth > 0) {
+            SettingsManager.propertiesWidth = 0;
         } else {
-            CurrentUserUtils.propertiesWidth = 250;
+            SettingsManager.propertiesWidth = 250;
         }
     };
 
     @computed get toolbar() {
-        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
-        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
+        const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
+        const propTitle = SettingsManager.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 === CurrentUserUtils.ActivePresentation;
+        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.ActivePresentation;
         const activeColor = Colors.LIGHT_BLUE;
         const inactiveColor = Colors.WHITE;
         return mode === CollectionViewType.Carousel3D ? null : (
@@ -2776,7 +2774,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </>
                             }>
                             <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
-                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} />
+                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: SettingsManager.propertiesWidth > 0 ? activeColor : inactiveColor }} />
                             </div>
                         </Tooltip>
                     </>
@@ -3093,10 +3091,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 === CurrentUserUtils.ActivePresentation;
+        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.ActivePresentation;
         const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
         const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
-        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ? (
+        return DocListCast(Doc.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
@@ -3151,7 +3149,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div>
         ) : (
-            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}>
+            <div className="presBox-cont" style={{ minWidth: DocListCast(Doc.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 cc7bd5189..1a2054676 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -1,52 +1,66 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { DragManager } from "../../../util/DragManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { CollectionViewType } from "../../collections/CollectionView";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+import { Docs, DocUtils } from '../../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { DragManager } from '../../../util/DragManager';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
-import { EditableView } from "../../EditableView";
-import { Colors } from "../../global/globalEnums";
-import { DocumentView, DocumentViewProps } from "../../nodes/DocumentView";
+import { EditableView } from '../../EditableView';
+import { Colors } from '../../global/globalEnums';
+import { DocumentView, DocumentViewProps } from '../../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
-import { StyleProp } from "../../StyleProvider";
-import { PresBox } from "./PresBox";
-import "./PresElementBox.scss";
-import { PresMovement } from "./PresEnums";
-import React = require("react");
-import { List } from "../../../../fields/List";
+import { StyleProp } from '../../StyleProvider';
+import { PresBox } from './PresBox';
+import './PresElementBox.scss';
+import { PresMovement } from './PresEnums';
+import React = require('react');
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
  */
 @observer
 export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(PresElementBox, fieldKey);
+    }
     _heightDisposer: IReactionDisposer | undefined;
 
     @observable _dragging = false;
     // Idea: this boolean will determine whether to automatically show the video when this preselement is selected.
     // @observable static showVideo: boolean = false;
-    @computed get indexInPres() { return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, "data")]).indexOf(this.rootDoc); }  // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
-    @computed get collapsedHeight() { return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(this.presBox._viewType as any) ? 35 : 31; } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
-    @computed get presStatus() { return this.presBox.presStatus; }
-    @computed get presBox() { return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!; }
-    @computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; }
+    @computed get indexInPres() {
+        return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc);
+    } // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
+    @computed get collapsedHeight() {
+        return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(this.presBox._viewType as any) ? 35 : 31;
+    } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
+    @computed get presStatus() {
+        return this.presBox.presStatus;
+    }
+    @computed get presBox() {
+        return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!;
+    }
+    @computed get targetDoc() {
+        return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc;
+    }
 
     componentDidMount() {
         this.layoutDoc.hideLinkButton = true;
-        this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.collapsedHeight],
-            params => this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true });
+        this._heightDisposer = reaction(
+            () => [this.rootDoc.presExpandInlineButton, this.collapsedHeight],
+            params => (this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0)),
+            { fireImmediately: true }
+        );
     }
     componentWillUnmount() {
         this._heightDisposer?.();
@@ -60,26 +74,26 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     presExpandDocumentClick = () => {
         this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton;
-    }
+    };
 
     embedHeight = (): number => 97;
     // embedWidth = () => this.props.PanelWidth();
     // embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight);
     embedWidth = (): number => this.props.PanelWidth() - 35;
-    styleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+    styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (property === StyleProp.Opacity) return 1;
         return this.props.styleProvider?.(doc, props, property);
-    }
+    };
     /**
      * The function that is responsible for rendering a preview or not for this
      * presentation element.
      */
     @computed get renderEmbeddedInline() {
-        return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? (null) :
+        return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? null : (
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: this.embedWidth() }}>
                 <DocumentView
                     Document={this.rootDoc}
-                    DataDoc={undefined}//this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
+                    DataDoc={undefined} //this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     styleProvider={this.styleProvider}
                     docViewPath={returnEmptyDoclist}
                     rootSelected={returnTrue}
@@ -105,22 +119,23 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     hideLinkButton={true}
                 />
                 <div className="presItem-embeddedMask" />
-            </div>;
+            </div>
+        );
     }
 
     @computed get renderGroupSlides() {
         const childDocs = DocListCast(this.targetDoc.data);
-        const groupSlides = childDocs.map((doc: Doc, ind: number) =>
-            <div className="presItem-groupSlide" onClick={(e) => {
-                console.log("Clicked on slide with index: ", ind);
-                e.stopPropagation();
-                e.preventDefault();
-                PresBox.Instance.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
-                this.presExpandDocumentClick();
-            }}>
-                <div className="presItem-groupNum">
-                    {`${ind + 1}.`}
-                </div>
+        const groupSlides = childDocs.map((doc: Doc, ind: number) => (
+            <div
+                className="presItem-groupSlide"
+                onClick={e => {
+                    console.log('Clicked on slide with index: ', ind);
+                    e.stopPropagation();
+                    e.preventDefault();
+                    PresBox.Instance.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+                    this.presExpandDocumentClick();
+                }}>
+                <div className="presItem-groupNum">{`${ind + 1}.`}</div>
                 {/* style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }} */}
                 <div className="presItem-name">
                     <EditableView
@@ -130,37 +145,36 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         overflow={'ellipsis'}
                         GetValue={() => StrCast(doc.title)}
                         SetValue={(value: string) => {
-                            doc.title = !value.trim().length ? "-untitled-" : value;
+                            doc.title = !value.trim().length ? '-untitled-' : value;
                             return true;
                         }}
                     />
                 </div>
             </div>
-        );
-        return (
-            groupSlides
-        );
+        ));
+        return groupSlides;
     }
     @computed get duration() {
         let durationInS: number;
-        if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10; }
-        else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
+        if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) {
+            durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime);
+            durationInS = Math.round(durationInS * 10) / 10;
+        } else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
         else durationInS = 2;
-        return "D: " + durationInS + "s";
+        return 'D: ' + durationInS + 's';
     }
 
     @computed get transition() {
         let transitionInS: number;
         if (this.rootDoc.presTransition) transitionInS = NumCast(this.rootDoc.presTransition) / 1000;
         else transitionInS = 0.5;
-        return this.rootDoc.presMovement === PresMovement.Jump || this.rootDoc.presMovement === PresMovement.None ? (null) : "M: " + transitionInS + "s";
+        return this.rootDoc.presMovement === PresMovement.Jump || this.rootDoc.presMovement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
     }
 
     private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _titleRef: React.RefObject<EditableView> = React.createRef();
 
-
     @action
     headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
         const element = e.target as any;
@@ -171,18 +185,24 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
                 setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
             } else {
-                setupMoveUpEvents(this, e, ((e: PointerEvent) => {
-                    PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
-                    return this.startDrag(e);
-                }), emptyFunction, emptyFunction);
+                setupMoveUpEvents(
+                    this,
+                    e,
+                    (e: PointerEvent) => {
+                        PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
+                        return this.startDrag(e);
+                    },
+                    emptyFunction,
+                    emptyFunction
+                );
             }
         }
-    }
+    };
 
     headerUp = (e: React.PointerEvent<HTMLDivElement>) => {
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     /**
      * Function to drag and drop the pres element to a diferent location
@@ -193,37 +213,43 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const dragArray = PresBox.Instance._dragArray;
         const dragData = new DragManager.DocumentDragData(PresBox.Instance.sortArray());
         if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
-        dragData.dropAction = "move";
+        dragData.dropAction = 'move';
         dragData.treeViewDoc = this.props.docViewPath().lastElement()?.props.treeViewDoc;
         dragData.moveDocument = this.props.docViewPath().lastElement()?.props.moveDocument;
         const dragItem: HTMLElement[] = [];
         if (dragArray.length === 1) {
             const doc = this._itemRef.current || dragArray[0];
-            doc.className = miniView ? "presItem-miniSlide" : "presItem-slide";
+            doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
             dragItem.push(doc);
         } else if (dragArray.length >= 1) {
             const doc = document.createElement('div');
-            doc.className = "presItem-multiDrag";
-            doc.innerText = "Move " + PresBox.Instance._selectedArray.size + " slides";
+            doc.className = 'presItem-multiDrag';
+            doc.innerText = 'Move ' + PresBox.Instance._selectedArray.size + ' slides';
             doc.style.position = 'absolute';
-            doc.style.top = (e.clientY) + 'px';
-            doc.style.left = (e.clientX - 50) + 'px';
+            doc.style.top = e.clientY + 'px';
+            doc.style.left = e.clientX - 50 + 'px';
             dragItem.push(doc);
         }
 
         // const dropEvent = () => runInAction(() => this._dragging = false);
         if (activeItem) {
-            DragManager.StartDocumentDrag(dragItem.map(ele => ele), dragData, e.clientX, e.clientY, undefined);
+            DragManager.StartDocumentDrag(
+                dragItem.map(ele => ele),
+                dragData,
+                e.clientX,
+                e.clientY,
+                undefined
+            );
             // runInAction(() => this._dragging = true);
             return true;
         }
         return false;
-    }
+    };
 
     onPointerOver = (e: any) => {
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.addEventListener("pointermove", this.onPointerMove);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.addEventListener('pointermove', this.onPointerMove);
+    };
 
     onPointerMove = (e: PointerEvent) => {
         const slide = this._itemRef.current!;
@@ -233,32 +259,32 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (slide && dragIsPresItem) {
             const rect = slide.getBoundingClientRect();
-            const y = e.clientY - rect.top;  //y position within the element.
+            const y = e.clientY - rect.top; //y position within the element.
             const height = slide.clientHeight;
             const halfLine = height / 2;
             if (y <= halfLine) {
                 slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                slide.style.borderBottom = "0px";
+                slide.style.borderBottom = '0px';
             } else if (y > halfLine) {
-                slide.style.borderTop = "0px";
+                slide.style.borderTop = '0px';
                 slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
             }
         }
-        document.removeEventListener("pointermove", this.onPointerMove);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+    };
 
     onPointerLeave = (e: any) => {
-        this._itemRef.current!.style.borderTop = "0px";
-        this._itemRef.current!.style.borderBottom = "0px";
-        document.removeEventListener("pointermove", this.onPointerMove);
-    }
+        this._itemRef.current!.style.borderTop = '0px';
+        this._itemRef.current!.style.borderBottom = '0px';
+        document.removeEventListener('pointermove', this.onPointerMove);
+    };
 
     @action
     toggleProperties = () => {
-        if (CurrentUserUtils.propertiesWidth < 5) {
-            action(() => (CurrentUserUtils.propertiesWidth = 250));
+        if (SettingsManager.propertiesWidth < 5) {
+            action(() => (SettingsManager.propertiesWidth = 250));
         }
-    }
+    };
 
     @undoBatch
     removeItem = action((e: React.MouseEvent) => {
@@ -267,32 +293,35 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
             PresBox.Instance._selectedArray.delete(this.rootDoc);
         }
-        this.removeAllRecordingInOverlay()
+        this.removeAllRecordingInOverlay();
     });
 
     // set the value/title of the individual pres element
     @undoBatch
     @action
     onSetValue = (value: string) => {
-        this.rootDoc.title = !value.trim().length ? "-untitled-" : value;
+        this.rootDoc.title = !value.trim().length ? '-untitled-' : value;
         return true;
-    }
+    };
 
     /**
      * Method called for updating the view of the currently selected document
-     * 
-     * @param targetDoc 
-     * @param activeItem 
+     *
+     * @param targetDoc
+     * @param activeItem
      */
     @undoBatch
     @action
     updateView = (targetDoc: Doc, activeItem: Doc) => {
         switch (targetDoc.type) {
-            case DocumentType.PDF: case DocumentType.WEB: case DocumentType.RTF:
+            case DocumentType.PDF:
+            case DocumentType.WEB:
+            case DocumentType.RTF:
                 const scroll = targetDoc._scrollTop;
                 activeItem.presPinViewScroll = scroll;
                 break;
-            case DocumentType.VID: case DocumentType.AUDIO:
+            case DocumentType.VID:
+            case DocumentType.AUDIO:
                 activeItem.presStartTime = targetDoc._currentTimecode;
                 break;
             case DocumentType.COMPARISON:
@@ -307,53 +336,53 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 activeItem.presPinViewY = y;
                 activeItem.presPinViewScale = scale;
         }
-    }
+    };
 
     @computed get recordingIsInOverlay() {
-        let isInOverlay = false
-        DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
+        let isInOverlay = false;
+        DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
             if (doc.slides === this.rootDoc) {
-                isInOverlay = true
+                isInOverlay = true;
                 // return
             }
-        })
-        return isInOverlay
+        });
+        return isInOverlay;
     }
 
     // a previously recorded video will have timecode defined
     static videoIsRecorded = (activeItem: Doc) => {
         const casted = Cast(activeItem.recording, Doc, null);
         return casted && 'currentTimecode' in casted;
-    }
+    };
 
     removeAllRecordingInOverlay = () => {
-        DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
+        DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
             if (doc.slides === this.rootDoc) {
-                Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
+                Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc);
             }
-        })
-    }
+        });
+    };
 
     static removeEveryExistingRecordingInOverlay = () => {
         // Remove every recording that already exists in overlay view
-        DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
+        DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
             // if it's a recording video, don't remove from overlay (user can lose data)
-            if (!PresElementBox.videoIsRecorded(DocCast(doc.slides))) return
+            if (!PresElementBox.videoIsRecorded(DocCast(doc.slides))) return;
 
             if (doc.slides !== null) {
-                Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
+                Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc);
             }
-        })
-    }
+        });
+    };
 
     @undoBatch
     @action
     hideRecording = (e: React.MouseEvent, iconClick: boolean = false) => {
-        e.stopPropagation()
-        this.removeAllRecordingInOverlay()
+        e.stopPropagation();
+        this.removeAllRecordingInOverlay();
 
         // if (iconClick) PresElementBox.showVideo = false;
-    }
+    };
 
     @undoBatch
     @action
@@ -364,47 +393,46 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // remove the overlays on switch *IF* not opened from the specific icon
         if (!iconClick) PresElementBox.removeEveryExistingRecordingInOverlay();
 
-
         if (activeItem.recording) {
-            Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
+            Doc.AddDocToList(Doc.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
         }
-    }
+    };
 
     @undoBatch
     @action
     startRecording = (activeItem: Doc) => {
-        console.log('start recording', 'activeItem', activeItem)
+        console.log('start recording', 'activeItem', activeItem);
         if (PresElementBox.videoIsRecorded(activeItem)) {
             // if we already have an existing recording
             this.showRecording(activeItem, true);
             //   // if we already have an existing recording
-            //   Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
-
+            //   Doc.AddDocToList(Doc.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
         } else {
             // Remove every recording that already exists in overlay view
             // this is a design decision to clear to focus in on the recoding mode
             PresElementBox.removeEveryExistingRecordingInOverlay();
 
             // if we dont have any recording
-            const recording = Docs.Create.WebCamDocument("", {
-                _width: 384, _height: 216,
+            const recording = Docs.Create.WebCamDocument('', {
+                _width: 384,
+                _height: 216,
                 hideDocumentButtonBar: true,
                 hideDecorationTitle: true,
                 hideOpenButton: true,
                 // hideDeleteButton: true,
-                cloneFieldFilter: new List<string>(["system"])
+                cloneFieldFilter: new List<string>(['system']),
             });
 
             // attach the recording to the slide, and attach the slide to the recording
-            recording.slides = activeItem
-            activeItem.recording = recording
+            recording.slides = activeItem;
+            activeItem.recording = recording;
 
             // 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(CurrentUserUtils.MyOverlayDocs, undefined, recording);
+            Doc.AddDocToList(Doc.MyOverlayDocs, undefined, recording);
         }
-    }
+    };
 
     @computed
     get toolbarWidth(): number {
@@ -422,17 +450,18 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const miniView: boolean = this.toolbarWidth <= 110;
         const presBox: Doc = this.presBox; //presBox
         const presBoxColor: string = StrCast(presBox._backgroundColor);
-        const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
+        const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
         const targetDoc: Doc = this.targetDoc;
         const activeItem: Doc = this.rootDoc;
 
         return (
-            <div className={`presItem-container`}
+            <div
+                className={`presItem-container`}
                 key={this.props.Document[Id] + this.indexInPres}
                 ref={this._itemRef}
                 style={{
-                    backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent",
-                    opacity: this._dragging ? 0.3 : 1
+                    backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
+                    opacity: this._dragging ? 0.3 : 1,
                 }}
                 onClick={e => {
                     e.stopPropagation();
@@ -447,8 +476,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 onPointerOver={this.onPointerOver}
                 onPointerLeave={this.onPointerLeave}
                 onPointerDown={this.headerDown}
-                onPointerUp={this.headerUp}
-            >
+                onPointerUp={this.headerUp}>
                 {/* {miniView ?
                               // when width is LESS than 110 px 
                               <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
@@ -462,50 +490,63 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 {/* <div className="presItem-number">
                               {`${this.indexInPres + 1}.`}
                         </div> */}
-                {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
-                    style={{
-                        backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
-                        boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
-                    }}>
-                    <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
-                        <div>{`${this.indexInPres + 1}.  `}</div>
-                        <EditableView
-                            ref={this._titleRef}
-                            editing={!isSelected ? false : undefined}
-                            contents={activeItem.title}
-                            overflow={'ellipsis'}
-                            GetValue={() => StrCast(activeItem.title)}
-                            SetValue={this.onSetValue}
-                        />
-                    </div>
-                    {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
-                    {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
-                    <div className={"presItem-slideButtons"}>
-                        <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}>
-                            <div className="slideButton"
-                                onClick={() => this.updateView(targetDoc, activeItem)}
-                                style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
-                        </Tooltip>
-
-                        {this.recordingIsInOverlay ?
-                            <Tooltip title={<><div className="dash-tooltip">{"Hide Recording"}</div></>}>
-                                <div className="slideButton"
-                                    onClick={(e) => this.hideRecording(e, true)}
-                                    style={{ fontWeight: 700 }}>
-                                    <FontAwesomeIcon icon={"video-slash"} onPointerDown={e => e.stopPropagation()} />
-                                </div>
-                            </Tooltip> :
-                            <Tooltip title={<><div className="dash-tooltip">{`${PresElementBox.videoIsRecorded(activeItem) ? "Show" : "Start"} recording`}</div></>}>
-                                <div className="slideButton"
-                                    onClick={(e) => { e.stopPropagation(); this.startRecording(activeItem); }}
-                                    style={{ fontWeight: 700 }}>
-                                    <FontAwesomeIcon icon={"video"} onPointerDown={e => e.stopPropagation()} />
+                {miniView ? null : (
+                    <div
+                        ref={miniView ? null : this._dragRef}
+                        className={`presItem-slide ${isSelected ? 'active' : ''}`}
+                        style={{
+                            backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+                            boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isSelected ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
+                        }}>
+                        <div className="presItem-name" style={{ maxWidth: showMore ? toolbarWidth - 195 : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
+                            <div>{`${this.indexInPres + 1}.  `}</div>
+                            <EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
+                        </div>
+                        {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
+                        {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
+                        <div className={'presItem-slideButtons'}>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{'Update view'}</div>
+                                    </>
+                                }>
+                                <div className="slideButton" onClick={() => this.updateView(targetDoc, activeItem)} style={{ fontWeight: 700, display: activeItem.presPinView ? 'flex' : 'none' }}>
+                                    V
                                 </div>
                             </Tooltip>
-                        }
-
 
-                        {/* {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
+                            {this.recordingIsInOverlay ? (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Hide Recording'}</div>
+                                        </>
+                                    }>
+                                    <div className="slideButton" onClick={e => this.hideRecording(e, true)} style={{ fontWeight: 700 }}>
+                                        <FontAwesomeIcon icon={'video-slash'} onPointerDown={e => e.stopPropagation()} />
+                                    </div>
+                                </Tooltip>
+                            ) : (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{`${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        className="slideButton"
+                                        onClick={e => {
+                                            e.stopPropagation();
+                                            this.startRecording(activeItem);
+                                        }}
+                                        style={{ fontWeight: 700 }}>
+                                        <FontAwesomeIcon icon={'video'} onPointerDown={e => e.stopPropagation()} />
+                                    </div>
+                                </Tooltip>
+                            )}
+
+                            {/* {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
                                           <div className="slideButton"
                                                 onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
                                                 style={{
@@ -520,22 +561,41 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                                 </div>
                                           </div>
                                     </Tooltip>} */}
-                        <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
-                            <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
-                        </div></Tooltip>
-                        <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
-                            className={"slideButton"}
-                            onClick={this.removeItem}>
-                            <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
-                        </div></Tooltip>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? 'Minimize' : 'Expand'}</div>
+                                    </>
+                                }>
+                                <div
+                                    className={'slideButton'}
+                                    onClick={e => {
+                                        e.stopPropagation();
+                                        this.presExpandDocumentClick();
+                                    }}>
+                                    <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
+                                </div>
+                            </Tooltip>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{'Remove from presentation'}</div>
+                                    </>
+                                }>
+                                <div className={'slideButton'} onClick={this.removeItem}>
+                                    <FontAwesomeIcon icon={'trash'} onPointerDown={e => e.stopPropagation()} />
+                                </div>
+                            </Tooltip>
+                        </div>
+                        {/* <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> */}
+                        {this.renderEmbeddedInline}
                     </div>
-                    {/* <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> */}
-                    {this.renderEmbeddedInline}
-                </div>}
-            </div >);
+                )}
+            </div>
+        );
     }
 
     render() {
-        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem;
+        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 5bdce273d..44f815336 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,15 +1,15 @@
-import React = require("react");
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { LinkManager } from "../../util/LinkManager";
-import { undoBatch } from "../../util/UndoManager";
-import { FieldViewProps } from "../nodes/FieldView";
-import { AnchorMenu } from "./AnchorMenu";
-import "./Annotation.scss";
+import React = require('react');
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { LinkFollower } from '../../util/LinkFollower';
+import { undoBatch } from '../../util/UndoManager';
+import { FieldViewProps } from '../nodes/FieldView';
+import { AnchorMenu } from './AnchorMenu';
+import './Annotation.scss';
 
 interface IAnnotationProps extends FieldViewProps {
     anno: Doc;
@@ -19,14 +19,15 @@ interface IAnnotationProps extends FieldViewProps {
     pointerEvents?: () => Opt<string>;
 }
 @observer
-export
-    class Annotation extends React.Component<IAnnotationProps> {
+export class Annotation extends React.Component<IAnnotationProps> {
     render() {
-        return <div>
-            {DocListCast(this.props.anno.textInlineAnnotations).map(a =>
-                <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
-            )}
-        </div>;
+        return (
+            <div>
+                {DocListCast(this.props.anno.textInlineAnnotations).map(a => (
+                    <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
+                ))}
+            </div>
+        );
     }
 }
 
@@ -38,7 +39,9 @@ interface IRegionAnnotationProps extends IAnnotationProps {
 class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
 
-    @computed get annoTextRegion() { return Cast(this.props.document.annoTextRegion, Doc, null) || this.props.document; }
+    @computed get annoTextRegion() {
+        return Cast(this.props.document.annoTextRegion, Doc, null) || this.props.document;
+    }
 
     @undoBatch
     deleteAnnotation = () => {
@@ -46,20 +49,20 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         this.props.dataDoc[this.props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
         AnchorMenu.Instance.fadeOut(true);
         this.props.select(false);
-    }
+    };
 
     @undoBatch
-    pinToPres = () => this.props.pinToPres(this.annoTextRegion)
+    pinToPres = () => this.props.pinToPres(this.annoTextRegion);
 
     @undoBatch
-    makePushpin = () => this.annoTextRegion.isPushpin = !this.annoTextRegion.isPushpin
+    makePushpin = () => (this.annoTextRegion.isPushpin = !this.annoTextRegion.isPushpin);
 
     isPushpin = () => BoolCast(this.annoTextRegion.isPushpin);
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
         if (e.button === 2 || e.ctrlKey) {
-            AnchorMenu.Instance.Status = "annotation";
+            AnchorMenu.Instance.Status = 'annotation';
             AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
             AnchorMenu.Instance.Pinned = false;
             AnchorMenu.Instance.AddTag = this.addTag.bind(this);
@@ -68,42 +71,43 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             AnchorMenu.Instance.IsPushpin = this.isPushpin;
             AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
             e.stopPropagation();
-        }
-        else if (e.button === 0) {
+        } else if (e.button === 0) {
             e.stopPropagation();
-            LinkManager.FollowLink(undefined, this.annoTextRegion, this.props, false);
+            LinkFollower.FollowLink(undefined, this.annoTextRegion, this.props, false);
         }
-    }
+    };
 
     addTag = (key: string, value: string): boolean => {
         const valNum = parseInt(value);
         this.annoTextRegion[key] = isNaN(valNum) ? value : valNum;
         return true;
-    }
+    };
 
     render() {
         const brushed = this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion);
-        return (<div className="htmlAnnotation" ref={this._mainCont}
-            onPointerEnter={action(() => {
-                Doc.BrushDoc(this.props.anno);
-                this.props.showInfo(this.props.anno);
-            })}
-            onPointerLeave={action(() => {
-                Doc.UnBrushDoc(this.props.anno);
-                this.props.showInfo(undefined);
-            })}
-            onPointerDown={this.onPointerDown}
-            style={{
-                left: NumCast(this.props.document.x),
-                top: NumCast(this.props.document.y),
-                width: NumCast(this.props.document._width),
-                height: NumCast(this.props.document._height),
-                opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
-                pointerEvents: this.props.pointerEvents?.() as any,
-                outline: brushed === Doc.DocBrushStatus.linkHighlighted ? "solid 1px lightBlue" : undefined,
-                backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? "orange" :
-                    StrCast(this.props.document.backgroundColor),
-            }} >
-        </div>);
+        return (
+            <div
+                className="htmlAnnotation"
+                ref={this._mainCont}
+                onPointerEnter={action(() => {
+                    Doc.BrushDoc(this.props.anno);
+                    this.props.showInfo(this.props.anno);
+                })}
+                onPointerLeave={action(() => {
+                    Doc.UnBrushDoc(this.props.anno);
+                    this.props.showInfo(undefined);
+                })}
+                onPointerDown={this.onPointerDown}
+                style={{
+                    left: NumCast(this.props.document.x),
+                    top: NumCast(this.props.document.y),
+                    width: NumCast(this.props.document._width),
+                    height: NumCast(this.props.document._height),
+                    opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
+                    pointerEvents: this.props.pointerEvents?.() as any,
+                    outline: brushed === Doc.DocBrushStatus.linkHighlighted ? 'solid 1px lightBlue' : undefined,
+                    backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? 'orange' : StrCast(this.props.document.backgroundColor),
+                }}></div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 510c5c385..837734edf 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,36 +1,35 @@
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx";
-import { observer } from "mobx-react";
-import * as Pdfjs from "pdfjs-dist";
-import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, DocListCast, Field, HeightSym, Opt } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { InkTool } from "../../../fields/InkField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, returnFalse, smoothScroll, Utils } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SharingManager } from "../../util/SharingManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { MarqueeOptionsMenu } from "../collections/collectionFreeForm";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { MarqueeAnnotator } from "../MarqueeAnnotator";
-import { DocumentViewProps } from "../nodes/DocumentView";
-import { FieldViewProps } from "../nodes/FieldView";
-import { LinkDocPreview } from "../nodes/LinkDocPreview";
-import { StyleProp } from "../StyleProvider";
-import { AnchorMenu } from "./AnchorMenu";
-import { Annotation } from "./Annotation";
-import "./PDFViewer.scss";
-import React = require("react");
-const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
-const pdfjsLib = require("pdfjs-dist");
-const _global = (window /* browser */ || global /* node */) as any;
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as Pdfjs from 'pdfjs-dist';
+import 'pdfjs-dist/web/pdf_viewer.css';
+import { Doc, DocListCast, Field, HeightSym, Opt } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { InkTool } from '../../../fields/InkField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SharingManager } from '../../util/SharingManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { MarqueeAnnotator } from '../MarqueeAnnotator';
+import { DocumentViewProps } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
+import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { StyleProp } from '../StyleProvider';
+import { AnchorMenu } from './AnchorMenu';
+import { Annotation } from './Annotation';
+import './PDFViewer.scss';
+import React = require('react');
+const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer');
+const pdfjsLib = require('pdfjs-dist');
+const _global = (window /* browser */ || global) /* node */ as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
 // The workerSrc property shall be specified.
-pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.14.305/build/pdf.worker.js";
+pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.14.305/build/pdf.worker.js';
 
 interface IViewerProps extends FieldViewProps {
     Document: Doc;
@@ -53,7 +52,7 @@ interface IViewerProps extends FieldViewProps {
 @observer
 export class PDFViewer extends React.Component<IViewerProps> {
     static _annotationStyle: any = addStyleSheet();
-    @observable private _pageSizes: { width: number, height: number }[] = [];
+    @observable private _pageSizes: { width: number; height: number }[] = [];
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _marqueeing: number[] | undefined;
     @observable private _textSelecting = true;
@@ -70,7 +69,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
     public _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
-    private _selectionText: string = "";
+    private _selectionText: string = '';
     private _downX: number = 0;
     private _downY: number = 0;
     private _lastSearch = false;
@@ -82,69 +81,80 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @observable isAnnotating = false;
     // key where data is stored
     @computed get allAnnotations() {
-        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + "-annotations"]), this.props.docFilters(), this.props.docRangeFilters(), undefined);
+        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']), this.props.docFilters(), this.props.docRangeFilters(), undefined);
+    }
+    @computed get inlineTextAnnotations() {
+        return this.allAnnotations.filter(a => a.textInlineAnnotations);
     }
-    @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
 
     componentDidMount = async () => {
-        runInAction(() => this._showWaiting = true);
+        runInAction(() => (this._showWaiting = true));
         this.setupPdfJsViewer();
-        this._mainCont.current?.addEventListener("scroll", e => (e.target as any).scrollLeft = 0);
+        this._mainCont.current?.addEventListener('scroll', e => ((e.target as any).scrollLeft = 0));
 
-        this._disposers.autoHeight = reaction(() => this.props.layoutDoc._autoHeight,
+        this._disposers.autoHeight = reaction(
+            () => this.props.layoutDoc._autoHeight,
             autoHeight => {
                 if (autoHeight) {
-                    this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.scaling?.() || 1));
                 }
-            });
+            }
+        );
 
-        this._disposers.selected = reaction(() => this.props.isSelected(),
+        this._disposers.selected = reaction(
+            () => this.props.isSelected(),
             selected => {
                 // if (!selected) {
                 //     Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
                 //     Array.from(this._savedAnnotations.keys()).forEach(k => this._savedAnnotations.set(k, []));
                 // }
-                (SelectionManager.Views().length === 1) && this.setupPdfJsViewer();
+                SelectionManager.Views().length === 1 && this.setupPdfJsViewer();
             },
-            { fireImmediately: true });
-        this._disposers.curPage = reaction(() => Cast(this.props.Document._curPage, "number", null),
-            (page) => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
             { fireImmediately: true }
         );
-    }
+        this._disposers.curPage = reaction(
+            () => Cast(this.props.Document._curPage, 'number', null),
+            page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
+            { fireImmediately: true }
+        );
+    };
 
     componentWillUnmount = () => {
         Object.values(this._disposers).forEach(disposer => disposer?.());
-        document.removeEventListener("copy", this.copy);
-    }
+        document.removeEventListener('copy', this.copy);
+    };
 
     copy = (e: ClipboardEvent) => {
         if (this.props.isContentActive() && e.clipboardData) {
-            e.clipboardData.setData("text/plain", this._selectionText);
+            e.clipboardData.setData('text/plain', this._selectionText);
             e.preventDefault();
         }
-    }
+    };
 
     @action
     initialLoad = async () => {
         if (this._pageSizes.length === 0) {
-            this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
-            await Promise.all(this._pageSizes.map((val, i) =>
-                this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => {
-                    const page0or180 = page.rotate === 0 || page.rotate === 180;
-                    this._pageSizes.splice(i, 1, {
-                        width: (page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1]),
-                        height: (page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0])
-                    });
-                    if (i === this.props.pdf.numPages - 1) {
-                        this.props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1],
-                            page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this.props.pdf.numPages);
-                    }
-                }))));
-            this.props.Document.scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0) * 96 / 72;
+            this._pageSizes = Array<{ width: number; height: number }>(this.props.pdf.numPages);
+            await Promise.all(
+                this._pageSizes.map((val, i) =>
+                    this.props.pdf.getPage(i + 1).then(
+                        action((page: Pdfjs.PDFPageProxy) => {
+                            const page0or180 = page.rotate === 0 || page.rotate === 180;
+                            this._pageSizes.splice(i, 1, {
+                                width: page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1],
+                                height: page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0],
+                            });
+                            if (i === this.props.pdf.numPages - 1) {
+                                this.props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this.props.pdf.numPages);
+                            }
+                        })
+                    )
+                )
+            );
+            this.props.Document.scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72;
         }
-    }
+    };
 
     // scrolls to focus on a nested annotation document.  if this is part a link preview then it will jump to the scroll location,
     // otherwise it will scroll smoothly.
@@ -153,7 +163,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight, NumCast(this.props.Document.scrollHeight));
+            const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight));
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
                 focusSpeed = 500;
 
@@ -165,10 +175,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
             this._initialScroll = NumCast(this.props.layoutDoc._scrollTop);
         }
         return focusSpeed;
-    }
+    };
     crop = (region: Doc | undefined, addCrop?: boolean) => {
         return this.props.crop(region, addCrop);
-    }
+    };
 
     @action
     setupPdfJsViewer = async () => {
@@ -179,18 +189,18 @@ export class PDFViewer extends React.Component<IViewerProps> {
         await this.initialLoad();
 
         this.createPdfViewer();
-    }
+    };
 
     pagesinit = () => {
         if (this._pdfViewer._setDocumentViewerElement?.offsetParent) {
-            runInAction(() => this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1);
+            runInAction(() => (this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1));
             this.gotoPage(NumCast(this.props.Document._curPage, 1));
         }
-        document.removeEventListener("pagesinit", this.pagesinit);
-        var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : "";
+        document.removeEventListener('pagesinit', this.pagesinit);
+        var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : '';
         this._disposers.scroll = reaction(
             () => Math.abs(NumCast(this.props.Document._scrollTop)),
-            (pos) => {
+            pos => {
                 if (!this._ignoreScroll) {
                     this._showWaiting && this.setupPdfJsViewer();
                     const viewTrans = quickScroll ?? StrCast(this.props.Document._viewTransition);
@@ -199,10 +209,13 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
                     this._forcedScroll = true;
                     if (duration) {
-                        setTimeout(() => {
-                            this._mainCont.current && smoothScroll(duration, this._mainCont.current, pos);
-                            setTimeout(() => this._forcedScroll = false, duration);
-                        }, this._mainCont.current ? 0 : 250); // wait for mainCont and try again to scroll
+                        setTimeout(
+                            () => {
+                                this._mainCont.current && smoothScroll(duration, this._mainCont.current, pos);
+                                setTimeout(() => (this._forcedScroll = false), duration);
+                            },
+                            this._mainCont.current ? 0 : 250
+                        ); // wait for mainCont and try again to scroll
                     } else {
                         this._mainCont.current?.scrollTo({ top: pos });
                         this._forcedScroll = false;
@@ -216,23 +229,27 @@ export class PDFViewer extends React.Component<IViewerProps> {
             this._mainCont.current?.scrollTo({ top: Math.abs(this._initialScroll || 0) });
             this._initialScroll = undefined;
         }
-    }
+    };
 
     createPdfViewer() {
-        if (!this._mainCont.current) { // bcz: I don't think this is ever triggered or needed
-            console.log("PDFViewer- I guess we got here");
+        if (!this._mainCont.current) {
+            // bcz: I don't think this is ever triggered or needed
+            console.log('PDFViewer- I guess we got here');
             if (this._retries < 5) {
                 this._retries++;
-                console.log("PDFViewer- retry num:" + this._retries);
+                console.log('PDFViewer- retry num:' + this._retries);
                 setTimeout(() => this.createPdfViewer(), 1000);
             }
             return;
         }
-        document.removeEventListener("copy", this.copy);
-        document.addEventListener("copy", this.copy);
+        document.removeEventListener('copy', this.copy);
+        document.addEventListener('copy', this.copy);
         const eventBus = new PDFJSViewer.EventBus(true);
-        eventBus._on("pagesinit", this.pagesinit);
-        eventBus._on("pagerendered", action(() => this._showWaiting = false));
+        eventBus._on('pagesinit', this.pagesinit);
+        eventBus._on(
+            'pagerendered',
+            action(() => (this._showWaiting = false))
+        );
         const pdfLinkService = new PDFJSViewer.PDFLinkService({ eventBus });
         const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus });
         this._pdfViewer = new PDFJSViewer.PDFViewer({
@@ -240,31 +257,30 @@ export class PDFViewer extends React.Component<IViewerProps> {
             viewer: this._viewer.current,
             linkService: pdfLinkService,
             findController: pdfFindController,
-            renderer: "canvas",
-            eventBus
+            renderer: 'canvas',
+            eventBus,
         });
         pdfLinkService.setViewer(this._pdfViewer);
         pdfLinkService.setDocument(this.props.pdf, null);
         this._pdfViewer.setDocument(this.props.pdf);
     }
 
-
     @action
     prevAnnotation = () => {
         this.Index = Math.max(this.Index - 1, 0);
         this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]);
-    }
+    };
 
     @action
     nextAnnotation = () => {
         this.Index = Math.min(this.Index + 1, this.allAnnotations.length - 1);
         this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]);
-    }
+    };
 
     @action
     gotoPage = (p: number) => {
         this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
-    }
+    };
 
     @action
     scrollToAnnotation = (scrollToAnnotation: Doc) => {
@@ -272,7 +288,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             this.scrollFocus(scrollToAnnotation, true);
             Doc.linkFollowHighlight(scrollToAnnotation);
         }
-    }
+    };
 
     @observable private _scrollTimer: any;
 
@@ -289,7 +305,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 this._scrollTimer = undefined;
             }, 200);
         }
-    }
+    };
 
     // get the page index that the vertical offset passed in is on
     getPageFromScroll = (vOffset: number) => {
@@ -299,7 +315,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             currOffset -= this._pageSizes[index++].height;
         }
         return index;
-    }
+    };
 
     @action
     search = (searchString: string, bwd?: boolean, clear: boolean = false) => {
@@ -308,22 +324,21 @@ export class PDFViewer extends React.Component<IViewerProps> {
             findPrevious: bwd,
             highlightAll: true,
             phraseSearch: true,
-            query: searchString
+            query: searchString,
         };
         if (clear) {
-            this._pdfViewer?.findController.executeCommand('reset', { query: "" });
+            this._pdfViewer?.findController.executeCommand('reset', { query: '' });
         } else if (!searchString) {
             bwd ? this.prevAnnotation() : this.nextAnnotation();
         } else if (this._pdfViewer?.pageViewsReady) {
             this._pdfViewer.findController.executeCommand('findagain', findOpts);
-        }
-        else if (this._mainCont.current) {
+        } else if (this._mainCont.current) {
             const executeFind = () => this._pdfViewer.findController.executeCommand('find', findOpts);
-            this._mainCont.current.addEventListener("pagesloaded", executeFind);
-            this._mainCont.current.addEventListener("pagerendered", executeFind);
+            this._mainCont.current.addEventListener('pagesloaded', executeFind);
+            this._mainCont.current.addEventListener('pagerendered', executeFind);
         }
         return true;
-    }
+    };
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
@@ -340,24 +355,27 @@ 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.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             this.props.select(false);
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
             this.isAnnotating = true;
-            if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) {
+            if (e.target && ((e.target as any).className.includes('endOfContent') || (e.target as any).parentElement.className !== 'textLayer')) {
                 this._textSelecting = false;
-                document.addEventListener("pointermove", this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called
+                document.addEventListener('pointermove', this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called
             } else {
                 // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee.
-                setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
-
-                this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "htmlAnnotation", { "pointer-events": "none" });
-                document.addEventListener("pointerup", this.onSelectEnd);
-                document.addEventListener("pointermove", this.onSelectMove);
+                setTimeout(
+                    action(() => (this._marqueeing = undefined)),
+                    100
+                ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+
+                this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, 'htmlAnnotation', { 'pointer-events': 'none' });
+                document.addEventListener('pointerup', this.onSelectEnd);
+                document.addEventListener('pointermove', this.onSelectMove);
             }
         }
-    }
+    };
 
     @action
     finishMarquee = (x?: number, y?: number) => {
@@ -365,8 +383,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this.isAnnotating = false;
         this._marqueeing = undefined;
         this._textSelecting = true;
-        document.removeEventListener("pointermove", this.onSelectMove);
-    }
+        document.removeEventListener('pointermove', this.onSelectMove);
+    };
 
     onSelectMove = (e: PointerEvent) => e.stopPropagation();
 
@@ -375,15 +393,15 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this.isAnnotating = false;
         clearStyleSheetRules(PDFViewer._annotationStyle);
         this.props.select(false);
-        document.removeEventListener("pointermove", this.onSelectMove);
-        document.removeEventListener("pointerup", this.onSelectEnd);
+        document.removeEventListener('pointermove', this.onSelectMove);
+        document.removeEventListener('pointerup', this.onSelectEnd);
 
         const sel = window.getSelection();
-        if (sel?.type === "Range") {
+        if (sel?.type === 'Range') {
             this.createTextAnnotation(sel, sel.getRangeAt(0));
             AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
         }
-    }
+    };
 
     @action
     createTextAnnotation = (sel: Selection, selRange: Range) => {
@@ -395,41 +413,41 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 if (rect && rect.width !== this._mainCont.current.clientWidth && rect.width) {
                     const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
                     const pdfScale = NumCast(this.props.layoutDoc._viewScale, 1);
-                    const annoBox = document.createElement("div");
-                    annoBox.className = "marqueeAnnotator-annotationBox";
+                    const annoBox = document.createElement('div');
+                    annoBox.className = 'marqueeAnnotator-annotationBox';
                     // transforms the positions from screen onto the pdf div
-                    annoBox.style.top = ((rect.top - boundingRect.top) * scaleX / pdfScale + this._mainCont.current.scrollTop).toString();
-                    annoBox.style.left = ((rect.left - boundingRect.left) * scaleX / pdfScale).toString();
-                    annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width / pdfScale).toString();
-                    annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height / pdfScale).toString();
+                    annoBox.style.top = (((rect.top - boundingRect.top) * scaleX) / pdfScale + this._mainCont.current.scrollTop).toString();
+                    annoBox.style.left = (((rect.left - boundingRect.left) * scaleX) / pdfScale).toString();
+                    annoBox.style.width = ((rect.width * this._mainCont.current.offsetWidth) / boundingRect.width / pdfScale).toString();
+                    annoBox.style.height = ((rect.height * this._mainCont.current.offsetHeight) / boundingRect.height / pdfScale).toString();
                     this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, this.getPageFromScroll(rect.top));
                 }
             }
         }
-        this._selectionText = selRange.cloneContents().textContent || "";
+        this._selectionText = selRange.cloneContents().textContent || '';
 
         // clear selection
-        if (sel.empty) {  // Chrome
+        if (sel.empty) {
+            // Chrome
             sel.empty();
-        } else if (sel.removeAllRanges) {  // Firefox
+        } else if (sel.removeAllRanges) {
+            // Firefox
             sel.removeAllRanges();
         }
-    }
+    };
 
     scrollXf = () => {
         return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform();
-    }
+    };
 
     onClick = (e: React.MouseEvent) => {
-        if (this._setPreviewCursor && e.button === 0 &&
-            Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
-            Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+        if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             this._setPreviewCursor(e.clientX, e.clientY, false, false);
         }
         // e.stopPropagation();  // bcz: not sure why this was here.  We need to allow the DocumentView to get clicks to process doubleClicks
-    }
+    };
 
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
 
     @action
     onZoomWheel = (e: React.WheelEvent) => {
@@ -437,52 +455,59 @@ export class PDFViewer extends React.Component<IViewerProps> {
             e.stopPropagation();
             if (e.ctrlKey) {
                 const curScale = Number(this._pdfViewer.currentScaleValue);
-                this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000));
+                this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - (curScale * e.deltaY) / 1000));
                 this.props.layoutDoc._viewScale = Number(this._pdfViewer.currentScaleValue);
             }
         }
-    }
+    };
 
-    pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
+    pointerEvents = () => (this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
     @computed get annotationLayer() {
-        return <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }} ref={this._annotationLayer}>
-            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation {...this.props} fieldKey={this.props.fieldKey + "-annotations"} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)}
-        </div>;
+        return (
+            <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }} ref={this._annotationLayer}>
+                {this.inlineTextAnnotations
+                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+                    .map(anno => (
+                        <Annotation {...this.props} fieldKey={this.props.fieldKey + '-annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+                    ))}
+            </div>
+        );
     }
 
     @computed get overlayInfo() {
-        return !this._overlayAnnoInfo ? (null) :
+        return !this._overlayAnnoInfo ? null : (
             <div className="pdfViewerDash-overlayAnno" style={{ top: NumCast(this._overlayAnnoInfo.y), left: NumCast(this._overlayAnnoInfo.x) }}>
                 <div className="pdfViewerDash-overlayAnno" style={{ right: -50, background: SharingManager.Instance.users.find(users => users.user.email === this._overlayAnnoInfo!.author)?.userColor }}>
-                    {this._overlayAnnoInfo.author + " " + Field.toString(this._overlayAnnoInfo.creationDate as Field)}
+                    {this._overlayAnnoInfo.author + ' ' + Field.toString(this._overlayAnnoInfo.creationDate as Field)}
                 </div>
-            </div>;
+            </div>
+        );
     }
 
-    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
+    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
     overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1));
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
-    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")];
+    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
-    childStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+    childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
-            if (doc.textInlineAnnotations) return "none";
-            return "all";
+            if (doc.textInlineAnnotations) return 'none';
+            return 'all';
         }
         return this.props.styleProvider?.(doc, props, property);
-    }
+    };
 
-    renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) =>
-        <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+    renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) => (
+        <CollectionFreeFormView
+            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
             isAnnotationOverlay={true}
-            fieldKey={this.props.fieldKey + "-annotations"}
+            fieldKey={this.props.fieldKey + '-annotations'}
             setPreviewCursor={this.setPreviewCursor}
             PanelHeight={this.panelHeight}
             PanelWidth={this.panelWidth}
-            dropAction={"alias"}
+            dropAction={'alias'}
             select={emptyFunction}
             ContentScaling={this.contentZoom}
             bringToFront={emptyFunction}
@@ -493,70 +518,88 @@ export class PDFViewer extends React.Component<IViewerProps> {
             ScreenToLocalTransform={this.overlayTransform}
             renderDepth={this.props.renderDepth + 1}
         />
-    @computed get overlayTransparentAnnotations() { return this.renderAnnotations(this.transparentFilter, false); }
-    @computed get overlayOpaqueAnnotations() { return this.renderAnnotations(this.opaqueFilter, false); }
+    );
+    @computed get overlayTransparentAnnotations() {
+        return this.renderAnnotations(this.transparentFilter, false);
+    }
+    @computed get overlayOpaqueAnnotations() {
+        return this.renderAnnotations(this.opaqueFilter, false);
+    }
     @computed get overlayClickableAnnotations() {
-        return <div style={{ height: NumCast(this.props.rootDoc.scrollHeight) }}>
-            {this.renderAnnotations(undefined, true)}
-        </div>;
+        return <div style={{ height: NumCast(this.props.rootDoc.scrollHeight) }}>{this.renderAnnotations(undefined, true)}</div>;
     }
     @computed get overlayLayer() {
-        return <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none" }}>
-            <div className="pdfViewerDash-overlay"
-                style={{
-                    pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none",
-                    mixBlendMode: "multiply",
-                    transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`
-                }}>
-                {this.overlayTransparentAnnotations}
-            </div>
-            <div className="pdfViewerDash-overlay"
-                style={{
-                    pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none",
-                    mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined,
-                    transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`
-                }}>
-                {this.overlayOpaqueAnnotations}
-                {this.overlayClickableAnnotations}
+        return (
+            <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none' }}>
+                <div
+                    className="pdfViewerDash-overlay"
+                    style={{
+                        pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none',
+                        mixBlendMode: 'multiply',
+                        transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`,
+                    }}>
+                    {this.overlayTransparentAnnotations}
+                </div>
+                <div
+                    className="pdfViewerDash-overlay"
+                    style={{
+                        pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none',
+                        mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined,
+                        transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`,
+                    }}>
+                    {this.overlayOpaqueAnnotations}
+                    {this.overlayClickableAnnotations}
+                </div>
             </div>
-        </div>;
+        );
     }
     @computed get pdfViewerDiv() {
-        return <div className={"pdfViewerDash-text" + (this.props.pointerEvents?.() !== "none" && this._textSelecting && this.props.isContentActive() ? "-selected" : "")} ref={this._viewer} />;
+        return <div className={'pdfViewerDash-text' + (this.props.pointerEvents?.() !== 'none' && this._textSelecting && this.props.isContentActive() ? '-selected' : '')} ref={this._viewer} />;
+    }
+    @computed get contentScaling() {
+        return this.props.ContentScaling?.() || 1;
     }
-    @computed get contentScaling() { return this.props.ContentScaling?.() || 1; }
     contentZoom = () => NumCast(this.props.layoutDoc._viewScale, 1);
     savedAnnotations = () => this._savedAnnotations;
     render() {
         TraceMobx();
-        return <div className="pdfViewer-content">
-            <div className={`pdfViewerDash${this.props.isContentActive() && this.props.pointerEvents?.() !== "none" ? "-interactive" : ""}`} ref={this._mainCont}
-                onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}
-                style={{
-                    overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? "scroll" : undefined,
-                    height: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`,
-                    transform: `scale(${this.contentScaling})`
-                }}  >
-                {this.pdfViewerDiv}
-                {this.annotationLayer}
-                {this.overlayLayer}
-                {this.overlayInfo}
-                {this._showWaiting ? <img className="pdfViewerDash-waiting" src={"/assets/loading.gif"} /> : (null)}
-                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                    <MarqueeAnnotator rootDoc={this.props.rootDoc}
-                        getPageFromScroll={this.getPageFromScroll}
-                        anchorMenuClick={this.props.anchorMenuClick}
-                        scrollTop={0}
-                        down={this._marqueeing}
-                        addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)}
-                        docView={this.props.docViewPath().lastElement()}
-                        finishMarquee={this.finishMarquee}
-                        savedAnnotations={this.savedAnnotations}
-                        annotationLayer={this._annotationLayer.current}
-                        mainCont={this._mainCont.current}
-                        anchorMenuCrop={this._textSelecting ? undefined : this.crop}
-                    />}
+        return (
+            <div className="pdfViewer-content">
+                <div
+                    className={`pdfViewerDash${this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' ? '-interactive' : ''}`}
+                    ref={this._mainCont}
+                    onScroll={this.onScroll}
+                    onWheel={this.onZoomWheel}
+                    onPointerDown={this.onPointerDown}
+                    onClick={this.onClick}
+                    style={{
+                        overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? 'scroll' : undefined,
+                        height: !this.props.Document._fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`,
+                        transform: `scale(${this.contentScaling})`,
+                    }}>
+                    {this.pdfViewerDiv}
+                    {this.annotationLayer}
+                    {this.overlayLayer}
+                    {this.overlayInfo}
+                    {this._showWaiting ? <img className="pdfViewerDash-waiting" src={'/assets/loading.gif'} /> : null}
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <MarqueeAnnotator
+                            rootDoc={this.props.rootDoc}
+                            getPageFromScroll={this.getPageFromScroll}
+                            anchorMenuClick={this.props.anchorMenuClick}
+                            scrollTop={0}
+                            down={this._marqueeing}
+                            addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)}
+                            docView={this.props.docViewPath().lastElement()}
+                            finishMarquee={this.finishMarquee}
+                            savedAnnotations={this.savedAnnotations}
+                            annotationLayer={this._annotationLayer.current}
+                            mainCont={this._mainCont.current}
+                            anchorMenuCrop={this._textSelecting ? undefined : this.crop}
+                        />
+                    )}
+                </div>
             </div>
-        </div>;
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index b447bdc19..0bf1575fb 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -1,94 +1,106 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action } from "mobx";
-import { observer } from "mobx-react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action } from 'mobx';
+import { observer } from 'mobx-react';
 import * as React from 'react';
-import { AclAdmin, Doc, DocListCast } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
-import { Cast, StrCast } from '../../../fields/Types';
-import { GetEffectiveAcl } from "../../../fields/util";
+import { AclAdmin, Doc } from '../../../fields/Doc';
+import { 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 { CollectionDockingView } from "../collections/CollectionDockingView";
-import { ContextMenu } from "../ContextMenu";
-import { Borders, Colors } from "../global/globalEnums";
-import { MainView } from "../MainView";
-import "./TopBar.scss";
+import { DocumentManager } from '../../util/DocumentManager';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
+import { SharingManager } from '../../util/SharingManager';
+import { UndoManager } from '../../util/UndoManager';
+import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { ContextMenu } from '../ContextMenu';
+import { DashboardView } from '../DashboardView';
+import { Borders, Colors } from '../global/globalEnums';
+import { MainView } from '../MainView';
+import './TopBar.scss';
 
 /**
  * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user
- * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard. 
+ * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard.
  */
 @observer
 export class TopBar extends React.Component {
-      navigateToHome = () => {
-            CollectionDockingView.Instance.CaptureThumbnail()?.then(() => {
-                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 = 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">
-                        <div className="topbar-inner-container">
-                              <div className="topbar-left">
-                                    {activeDashboard ?
-                                          <>
-                                                <div className="topbar-button-icon" onClick={e => {
-                                                      ContextMenu.Instance.addItem({ description: "Logout", event: () => window.location.assign(Utils.prepend("/logout")), icon: "edit" });
-                                                      ContextMenu.Instance.displayMenu(e.clientX + 5, e.clientY + 10);
-                                                }}>{Doc.CurrentUserEmail}</div>
-                                                <div className="topbar-button-icon" onClick={this.navigateToHome}>
-                                                      <FontAwesomeIcon icon="home" />
-                                                </div>
-                                          </>
-                                          : (null)}
-
-                              </div>
-                              <div className="topbar-center" >
-                                    <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 = 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");
-                                            await CurrentUserUtils.snapshotDashboard();
-                                            batch.end();
-                                        }, icon: "edit" });
-                                        dashView?.showContextMenu(e.clientX+20, e.clientY+30);
+    navigateToHome = () => {
+        CollectionDockingView.Instance.CaptureThumbnail()?.then(() => {
+            Doc.ActivePage = 'home';
+            DashboardView.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 = Doc.ActiveDashboard;
+        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">
+                        {activeDashboard ? (
+                            <>
+                                <div
+                                    className="topbar-button-icon"
+                                    onClick={e => {
+                                        ContextMenu.Instance.addItem({ description: 'Logout', event: () => window.location.assign(Utils.prepend('/logout')), icon: 'edit' });
+                                        ContextMenu.Instance.displayMenu(e.clientX + 5, e.clientY + 10);
                                     }}>
-                                          <FontAwesomeIcon color="white" size="lg" icon="bars" />
-                                    </div>
-                                    <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
-                                          <div className="topbar-button-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
-                                                <FontAwesomeIcon color={MainView.Instance._exploreMode ? "red" : "white"} icon="eye" size="lg" />
-                                          </div>
-                                    </Tooltip>
-                              </div>
-                              <div className="topbar-right" >
-                                    {CurrentUserUtils.ActiveDashboard ? <div className="topbar-button-icon" onClick={() => { SharingManager.Instance.open(undefined, activeDashboard) }}>
-                                          {GetEffectiveAcl(Doc.GetProto(CurrentUserUtils.ActiveDashboard)) === AclAdmin ? "Share" : "view original"}
-                                    </div> : (null)}
-                                    <div className="topbar-button-icon" onClick={() => window.open(
-                                          "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
-                                          <FontAwesomeIcon icon="question-circle" />
-                                    </div>
-                                    <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}>
-                                          <FontAwesomeIcon icon="cog" />
-                                    </div>
-                              </div>
+                                    {Doc.CurrentUserEmail}
+                                </div>
+                                <div className="topbar-button-icon" onClick={this.navigateToHome}>
+                                    <FontAwesomeIcon icon="home" />
+                                </div>
+                            </>
+                        ) : null}
+                    </div>
+                    <div className="topbar-center">
+                        <div className="topbar-title" onClick={() => activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
+                            {activeDashboard ? StrCast(activeDashboard.title) : 'Dash'}
                         </div>
-                  </div>
-
-            );
-      }
-}
\ No newline at end of file
+                        <div
+                            className="topbar-button-icon"
+                            onClick={e => {
+                                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');
+                                        await DashboardView.snapshotDashboard();
+                                        batch.end();
+                                    },
+                                    icon: 'edit',
+                                });
+                                dashView?.showContextMenu(e.clientX + 20, e.clientY + 30);
+                            }}>
+                            <FontAwesomeIcon color="white" size="lg" icon="bars" />
+                        </div>
+                        <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
+                            <div className="topbar-button-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => (MainView.Instance._exploreMode = !MainView.Instance._exploreMode))}>
+                                <FontAwesomeIcon color={MainView.Instance._exploreMode ? 'red' : 'white'} icon="eye" size="lg" />
+                            </div>
+                        </Tooltip>
+                    </div>
+                    <div className="topbar-right">
+                        {Doc.ActiveDashboard ? (
+                            <div
+                                className="topbar-button-icon"
+                                onClick={() => {
+                                    SharingManager.Instance.open(undefined, activeDashboard);
+                                }}>
+                                {GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'view original'}
+                            </div>
+                        ) : null}
+                        <div className="topbar-button-icon" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')}>
+                            <FontAwesomeIcon icon="question-circle" />
+                        </div>
+                        <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}>
+                            <FontAwesomeIcon icon="cog" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 6c0c9b301..02e44a793 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -1,86 +1,84 @@
-import { faPhoneSlash, faSync } from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc } from "../../../fields/Doc";
-import { InkTool } from "../../../fields/InkField";
-import "../../views/nodes/WebBox.scss";
-import { DocumentDecorations } from "../DocumentDecorations";
-import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView";
-import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import "./DashWebRTCVideo.scss";
-import { hangup, initialize, refreshVideos } from "./WebCamLogic";
-import React = require("react");
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { IconLookup } from "@fortawesome/fontawesome-svg-core";
-
+import { IconLookup } from '@fortawesome/fontawesome-svg-core';
+import { faPhoneSlash, faSync } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../fields/Doc';
+import { InkTool } from '../../../fields/InkField';
+import '../../views/nodes/WebBox.scss';
+import { DocumentDecorations } from '../DocumentDecorations';
+import { CollectionFreeFormDocumentViewProps } from '../nodes/CollectionFreeFormDocumentView';
+import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import './DashWebRTCVideo.scss';
+import { hangup, initialize, refreshVideos } from './WebCamLogic';
+import React = require('react');
 
 /**
  * This models the component that will be rendered, that can be used as a doc that will reflect the video cams.
  */
 @observer
 export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentViewProps & FieldViewProps> {
-
     private roomText: HTMLInputElement | undefined;
     @observable remoteVideoAdded: boolean = false;
 
     @action
     changeUILook = () => {
         this.remoteVideoAdded = true;
-    }
+    };
 
     /**
-      * Function that submits the title entered by user on enter press.
-      */
+     * Function that submits the title entered by user on enter press.
+     */
     private onEnterKeyDown = (e: React.KeyboardEvent) => {
         if (e.keyCode === 13) {
             const submittedTitle = this.roomText!.value;
-            this.roomText!.value = "";
+            this.roomText!.value = '';
             this.roomText!.blur();
             initialize(submittedTitle, this.changeUILook);
         }
-    }
-
+    };
 
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DashWebRTCVideo, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(DashWebRTCVideo, fieldKey);
+    }
 
     @action
     onClickRefresh = () => {
         refreshVideos();
-    }
+    };
 
     onClickHangUp = () => {
         hangup();
-    }
+    };
 
     render() {
-        const content =
-            <div className="webcam-cont" style={{ width: "100%", height: "100%" }}>
+        const content = (
+            <div className="webcam-cont" style={{ width: '100%', height: '100%' }}>
                 <div className="webcam-header">DashWebRTC</div>
-                <input id="roomName" type="text" placeholder="Enter room name" ref={(e) => this.roomText = e!} onKeyDown={this.onEnterKeyDown} />
+                <input id="roomName" type="text" placeholder="Enter room name" ref={e => (this.roomText = e!)} onKeyDown={this.onEnterKeyDown} />
                 <div className="videoContainer">
-                    <video id="localVideo" className={"RTCVideo" + (this.remoteVideoAdded ? " side" : " main")} autoPlay playsInline muted ref={(e) => {
-                    }}></video>
-                    <video id="remoteVideo" className="RTCVideo main" autoPlay playsInline ref={(e) => {
-                    }}></video>
+                    <video id="localVideo" className={'RTCVideo' + (this.remoteVideoAdded ? ' side' : ' main')} autoPlay playsInline muted ref={e => {}}></video>
+                    <video id="remoteVideo" className="RTCVideo main" autoPlay playsInline ref={e => {}}></video>
                 </div>
                 <div className="buttonContainer">
-                    <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash as IconLookup} color="white" /></div>
-                    <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync as IconLookup} color="white" /></div>
+                    <div className="videoButtons" style={{ background: 'red' }} onClick={this.onClickHangUp}>
+                        <FontAwesomeIcon icon={faPhoneSlash as IconLookup} color="white" />
+                    </div>
+                    <div className="videoButtons" style={{ background: 'green' }} onClick={this.onClickRefresh}>
+                        <FontAwesomeIcon icon={faSync as IconLookup} color="white" />
+                    </div>
                 </div>
-            </div >;
+            </div>
+        );
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? '-interactive' : '');
 
         return (
             <>
-                <div className={classname}  >
-                    {content}
-                </div>
-                {!frozen ? (null) : <div className="webBox-overlay" />}
-            </>);
+                <div className={classname}>{content}</div>
+                {!frozen ? null : <div className="webBox-overlay" />}
+            </>
+        );
     }
-
-
-}
\ No newline at end of file
+}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 31a50301a..0c7504913 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -5,7 +5,6 @@ import { computedFn } from 'mobx-utils';
 import { alias, map, serializable } from 'serializr';
 import { DocServer } from '../client/DocServer';
 import { DocumentType } from '../client/documents/DocumentTypes';
-import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { LinkManager } from '../client/util/LinkManager';
 import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { SelectionManager } from '../client/util/SelectionManager';
@@ -14,6 +13,7 @@ import { UndoManager } from '../client/util/UndoManager';
 import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils';
 import { DateField } from './DateField';
 import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols';
+import { InkTool } from './InkField';
 import { List } from './List';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
@@ -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 { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from './URLField';
 import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from './util';
 import JSZip = require('jszip');
@@ -141,6 +141,95 @@ export function updateCachedAcls(doc: Doc) {
 @scriptingGlobal
 @Deserializable('Doc', updateCachedAcls).withFields(['id'])
 export class Doc extends RefField {
+    //TODO tfs: these should be temporary...
+    private static mainDocId: string | undefined;
+    public static get MainDocId() {
+        return this.mainDocId;
+    }
+    public static set MainDocId(id: string | undefined) {
+        this.mainDocId = id;
+    }
+    @observable public static GuestDashboard: Doc | undefined;
+    @observable public static GuestTarget: Doc | undefined;
+    @observable public static GuestMobile: Doc | undefined;
+    public static get MySharedDocs() {
+        return DocCast(Doc.UserDoc().mySharedDocs);
+    }
+    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 MyHeaderBar() {
+        return DocCast(Doc.UserDoc().myHeaderBar);
+    }
+    public static get MyLeftSidebarMenu() {
+        return DocCast(Doc.UserDoc().myLeftSidebarMenu);
+    }
+    public static get MyLeftSidebarPanel() {
+        return DocCast(Doc.UserDoc().myLeftSidebarPanel);
+    }
+    public static get MyContextMenuBtns() {
+        return DocCast(Doc.UserDoc().myContextMenuBtns);
+    }
+    public static get MyRecentlyClosed() {
+        return DocCast(Doc.UserDoc().myRecentlyClosed);
+    }
+    public static get MyTrails() {
+        return DocCast(Doc.UserDoc().myTrails);
+    }
+    public static get MyOverlayDocs() {
+        return DocCast(Doc.UserDoc().myOverlayDocs);
+    }
+    public static get MyPublishedDocs() {
+        return DocCast(Doc.UserDoc().myPublishedDocs);
+    }
+    public static get MyDashboards() {
+        return DocCast(Doc.UserDoc().myDashboards);
+    }
+    public static get MyTemplates() {
+        return DocCast(Doc.UserDoc().myTemplates);
+    }
+    public static get MyImports() {
+        return DocCast(Doc.UserDoc().myImports);
+    }
+    public static get MyFilesystem() {
+        return DocCast(Doc.UserDoc().myFilesystem);
+    }
+    public static get MyFileOrphans() {
+        return DocCast(Doc.UserDoc().myFileOrphans);
+    }
+    public static get MyTools() {
+        return DocCast(Doc.UserDoc().myTools);
+    }
+    public static get ActivePage() {
+        return StrCast(Doc.UserDoc().activePage);
+    }
+    public static set ActivePage(val) {
+        Doc.UserDoc().activePage = val;
+    }
+    public static get ActiveDashboard() {
+        return DocCast(Doc.UserDoc().activeDashboard);
+    }
+    public static set ActiveDashboard(val: Doc | undefined) {
+        Doc.UserDoc().activeDashboard = val;
+    }
+    public static set ActiveTool(tool: InkTool) {
+        Doc.UserDoc().activeTool = tool;
+    }
+    public static get ActiveTool(): InkTool {
+        return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool;
+    }
+    public static get ActivePresentation() {
+        return DocCast(Doc.UserDoc().activePresentation);
+    }
+    public static set ActivePresentation(val) {
+        Doc.UserDoc().activePresentation = val;
+    }
     constructor(id?: FieldId, forceSave?: boolean) {
         super(id);
         const doc = new Proxy<this>(this, {
@@ -1035,7 +1124,7 @@ export namespace Doc {
         return manager._user_doc;
     }
     export function SharingDoc(): Doc {
-        return CurrentUserUtils.MySharedDocs;
+        return Doc.MySharedDocs;
     }
     export function LinkDBDoc(): Doc {
         return Cast(Doc.UserDoc().myLinkDatabase, Doc, null);
@@ -1295,50 +1384,34 @@ export namespace Doc {
 
     export function isDocPinned(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = CurrentUserUtils.ActivePresentation;
+        const curPres = Doc.ActivePresentation;
         return !curPres ? false : DocListCast(curPres.data).findIndex(val => Doc.AreProtosEqual(val, doc)) !== -1;
     }
 
+    // prettier-ignore
     export function toIcon(doc?: Doc, isOpen?: boolean) {
         switch (StrCast(doc?.type)) {
-            case DocumentType.IMG:
-                return 'image';
-            case DocumentType.COMPARISON:
-                return 'columns';
-            case DocumentType.RTF:
-                return 'sticky-note';
+            case DocumentType.IMG:  return 'image';
+            case DocumentType.COMPARISON: return 'columns';
+            case DocumentType.RTF: return 'sticky-note';
             case DocumentType.COL:
                 const folder: IconProp = isOpen ? 'folder-open' : 'folder';
                 const chevron: IconProp = isOpen ? 'chevron-down' : 'chevron-right';
                 return !doc?.isFolder ? folder : chevron;
-            case DocumentType.WEB:
-                return 'globe-asia';
-            case DocumentType.SCREENSHOT:
-                return 'photo-video';
-            case DocumentType.WEBCAM:
-                return 'video';
-            case DocumentType.AUDIO:
-                return 'microphone';
-            case DocumentType.BUTTON:
-                return 'bolt';
-            case DocumentType.PRES:
-                return 'tv';
-            case DocumentType.SCRIPTING:
-                return 'terminal';
-            case DocumentType.IMPORT:
-                return 'cloud-upload-alt';
-            case DocumentType.VID:
-                return 'video';
-            case DocumentType.INK:
-                return 'pen-nib';
-            case DocumentType.PDF:
-                return 'file-pdf';
-            case DocumentType.LINK:
-                return 'link';
-            case DocumentType.MAP:
-                return 'map-marker-alt';
-            default:
-                return 'question';
+            case DocumentType.WEB: return 'globe-asia';
+            case DocumentType.SCREENSHOT: return 'photo-video';
+            case DocumentType.WEBCAM: return 'video';
+            case DocumentType.AUDIO: return 'microphone';
+            case DocumentType.BUTTON: return 'bolt';
+            case DocumentType.PRES: return 'tv';
+            case DocumentType.SCRIPTING: return 'terminal';
+            case DocumentType.IMPORT: return 'cloud-upload-alt';
+            case DocumentType.VID: return 'video';
+            case DocumentType.INK: return 'pen-nib';
+            case DocumentType.PDF: return 'file-pdf';
+            case DocumentType.LINK: return 'link';
+            case DocumentType.MAP: return 'map-marker-alt';
+            default: return 'question';
         }
     }
 
@@ -1551,7 +1624,7 @@ ScriptingGlobals.add(function docCast(doc: FieldResult): any {
     return DocCastAsync(doc);
 });
 ScriptingGlobals.add(function activePresentationItem() {
-    const curPres = CurrentUserUtils.ActivePresentation;
+    const curPres = Doc.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/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index e1360553a..f19496d25 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -104,14 +104,13 @@ import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
-import { DocumentType } from '../client/documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { SettingsManager, ColorScheme } from '../client/util/SettingsManager';
 import { Transform } from '../client/util/Transform';
 import { UndoManager } from '../client/util/UndoManager';
 import { TabDocView } from '../client/views/collections/TabDocView';
-import { CollectionViewType } from '../client/views/collections/CollectionView';
 import { GestureOverlay } from '../client/views/GestureOverlay';
 import { AudioBox } from '../client/views/nodes/AudioBox';
 import { DocumentView } from '../client/views/nodes/DocumentView';
@@ -249,7 +248,7 @@ export class MobileInterface extends React.Component {
     @observable private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
 
     @computed private get mainContainer() {
-        return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : CurrentUserUtils.GuestMobile;
+        return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : Doc.GuestMobile;
     }
 
     constructor(props: Readonly<{}>) {
@@ -262,7 +261,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.ActiveTool = InkTool.None; // ink should intially be set to none
+        Doc.ActiveTool = InkTool.None; // ink should intially be set to none
         Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
         AudioBox.Enabled = true;
 
@@ -511,7 +510,7 @@ export class MobileInterface extends React.Component {
             );
         }
         // stores dashboards documents as 'dashboards' variable
-        let dashboards = CurrentUserUtils.MyDashboards;
+        let dashboards = Doc.MyDashboards;
         if (this.dashboards) {
             dashboards = this.dashboards;
         }
@@ -580,14 +579,14 @@ export class MobileInterface extends React.Component {
      */
     @action
     createNewDashboard = (id?: string) => {
-        const scens = CurrentUserUtils.MyDashboards;
+        const scens = Doc.MyDashboards;
         const dashboardCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
             title: 'Collection ' + dashboardCount,
         };
-        const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+        const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row');
 
         const toggleTheme = ScriptField.MakeScript(`self.colorScheme = self.colorScheme ? undefined: ${ColorScheme.Dark}}`);
@@ -607,10 +606,10 @@ export class MobileInterface extends React.Component {
         button.style.color = this._ink ? 'black' : 'white';
 
         if (!this._ink) {
-            CurrentUserUtils.ActiveTool = InkTool.Pen;
+            Doc.ActiveTool = InkTool.Pen;
             this._ink = true;
         } else {
-            CurrentUserUtils.ActiveTool = InkTool.None;
+            Doc.ActiveTool = InkTool.None;
             this._ink = false;
         }
     };
@@ -762,7 +761,7 @@ export class MobileInterface extends React.Component {
     // For setting up the presentation document for the home menu
     @action
     setupDefaultPresentation = () => {
-        const presentation = CurrentUserUtils.ActivePresentation;
+        const presentation = Doc.ActivePresentation;
 
         if (presentation) {
             this.switchCurrentView(presentation);
diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx
index 4a1e26078..f85f05f53 100644
--- a/src/mobile/MobileMain.tsx
+++ b/src/mobile/MobileMain.tsx
@@ -1,25 +1,29 @@
-import { MobileInterface } from "./MobileInterface";
-import { Docs } from "../client/documents/Documents";
-import { CurrentUserUtils } from "../client/util/CurrentUserUtils";
-import * as ReactDOM from 'react-dom';
 import * as React from 'react';
-import { DocServer } from "../client/DocServer";
-import { AssignAllExtensions } from "../extensions/General/Extensions";
+import * as ReactDOM from 'react-dom';
+import { DocServer } from '../client/DocServer';
+import { Docs } from '../client/documents/Documents';
+import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
+import { AssignAllExtensions } from '../extensions/General/Extensions';
+import { MobileInterface } from './MobileInterface';
 
 AssignAllExtensions();
 
 (async () => {
     const info = await CurrentUserUtils.loadCurrentUser();
-    DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + " (mobile)");
+    DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + ' (mobile)');
     await Docs.Prototypes.initialize();
-    if (info.id !== "__guest__") {
+    if (info.id !== '__guest__') {
         // a guest will not have an id registered
         await CurrentUserUtils.loadUserDocument(info.id);
     }
-    document.getElementById('root')!.addEventListener('wheel', event => {
-        if (event.ctrlKey) {
-            event.preventDefault();
-        }
-    }, true);
+    document.getElementById('root')!.addEventListener(
+        'wheel',
+        event => {
+            if (event.ctrlKey) {
+                event.preventDefault();
+            }
+        },
+        true
+    );
     ReactDOM.render(<MobileInterface />, document.getElementById('root'));
-})();
\ No newline at end of file
+})();
-- 
cgit v1.2.3-70-g09d2


From 358f9e266ef264442aea1e2c7d5d959a19f7624c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 22 Jul 2022 14:11:30 -0400
Subject: adjusted native dim scaling slightly combining props.scaling and
 props.ContentScaling into props.NativeDimScaling and fixing some resizing
 behaviors for fitWidth freeformviews and native-sized text boxes.   Also
 fixed clicking on presboxe elements to not drag.

---
 src/client/views/DocumentDecorations.tsx           |   3 +-
 src/client/views/InkingStroke.tsx                  | 536 +++++++++++++--------
 src/client/views/SidebarAnnos.tsx                  |   4 +-
 src/client/views/StyleProvider.tsx                 |   4 +-
 .../collections/CollectionStackedTimeline.tsx      |   2 +-
 .../views/collections/CollectionTreeView.tsx       |  11 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  30 +-
 .../CollectionMulticolumnView.tsx                  | 187 +++----
 .../CollectionMultirowView.tsx                     | 180 +++----
 src/client/views/nodes/DocumentContentsView.tsx    |   5 +-
 src/client/views/nodes/DocumentLinksButton.tsx     | 384 ++++++++-------
 src/client/views/nodes/DocumentView.tsx            |  24 +-
 src/client/views/nodes/EquationBox.tsx             |  90 ++--
 src/client/views/nodes/FieldView.tsx               |  55 ++-
 src/client/views/nodes/ImageBox.tsx                |  10 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   9 +-
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |  12 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   4 +-
 src/client/views/nodes/VideoBox.tsx                |   6 +-
 src/client/views/nodes/WebBox.tsx                  |  25 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  10 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |   8 +-
 src/client/views/pdf/PDFViewer.tsx                 |  17 +-
 25 files changed, 883 insertions(+), 741 deletions(-)

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 780dcfb6d..964fd36c8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -521,7 +521,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             } else doc._height = actualdH;
                         }
                     } else {
-                        dH && (doc._height = actualdH);
+                        const maxHeight = Math.max(nheight, NumCast(doc.scrollHeight)) * docView.NativeDimScaling();
+                        dH && (doc._height = actualdH > maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
                         dH && (doc._autoHeight = false);
                     }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index dace9716a..e5de7a0c5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -20,35 +20,37 @@
     
     Most of the operations that can be performed on an InkStroke (eg delete a point, rotate, stretch) are implemented in the InkStrokeProperties helper class
 */
-import React = require("react");
-import { action, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, WidthSym } from "../../fields/Doc";
-import { InkData, InkField, InkTool } from "../../fields/InkField";
-import { Cast, NumCast, RTFCast, StrCast } from "../../fields/Types";
-import { TraceMobx } from "../../fields/util";
-import { OmitKeys, returnFalse, setupMoveUpEvents } from "../../Utils";
-import { CognitiveServices } from "../cognitive_services/CognitiveServices";
-import { InteractionUtils } from "../util/InteractionUtils";
-import { SnappingManager } from "../util/SnappingManager";
-import { Transform } from "../util/Transform";
-import { UndoManager } from "../util/UndoManager";
-import { ContextMenu } from "./ContextMenu";
-import { ViewBoxBaseComponent } from "./DocComponent";
-import { Colors } from "./global/globalEnums";
-import { InkControlPtHandles, InkEndPtHandles } from "./InkControlPtHandles";
-import { InkStrokeProperties } from "./InkStrokeProperties";
-import { InkTangentHandles } from "./InkTangentHandles";
-import { DocComponentView } from "./nodes/DocumentView";
-import { FieldView, FieldViewProps } from "./nodes/FieldView";
-import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
-import "./InkStroke.scss";
-import Color = require("color");
+import React = require('react');
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, WidthSym } from '../../fields/Doc';
+import { InkData, InkField, InkTool } from '../../fields/InkField';
+import { Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
+import { TraceMobx } from '../../fields/util';
+import { OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils';
+import { CognitiveServices } from '../cognitive_services/CognitiveServices';
+import { InteractionUtils } from '../util/InteractionUtils';
+import { SnappingManager } from '../util/SnappingManager';
+import { Transform } from '../util/Transform';
+import { UndoManager } from '../util/UndoManager';
+import { ContextMenu } from './ContextMenu';
+import { ViewBoxBaseComponent } from './DocComponent';
+import { Colors } from './global/globalEnums';
+import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles';
+import { InkStrokeProperties } from './InkStrokeProperties';
+import { InkTangentHandles } from './InkTangentHandles';
+import { DocComponentView } from './nodes/DocumentView';
+import { FieldView, FieldViewProps } from './nodes/FieldView';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import './InkStroke.scss';
+import Color = require('color');
 
 @observer
 export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     static readonly MaskDim = 50000; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big)
-    public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); }
+    public static LayoutString(fieldStr: string) {
+        return FieldView.LayoutString(InkingStroke, fieldStr);
+    }
     public static IsClosed(inkData: InkData) {
         return inkData && inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y;
     }
@@ -56,13 +58,15 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     private _selDisposer?: IReactionDisposer;
 
     @observable _nearestSeg?: number; // nearest Bezier segment along the ink stroke to the cursor (used for displaying the Add Point highlight)
-    @observable _nearestT?: number;  // nearest t value within the nearest Bezier segment "
-    @observable _nearestScrPt?: { X: number, Y: number }; // nearst screen point on the ink stroke ""
+    @observable _nearestT?: number; // nearest t value within the nearest Bezier segment "
+    @observable _nearestScrPt?: { X: number; Y: number }; // nearst screen point on the ink stroke ""
 
     componentDidMount() {
         this.props.setContentView?.(this);
-        this._selDisposer = reaction(() => this.props.isSelected(), // react to stroke being deselected by turning off ink handles
-            selected => !selected && (InkStrokeProperties.Instance._controlButton = false));
+        this._selDisposer = reaction(
+            () => this.props.isSelected(), // react to stroke being deselected by turning off ink handles
+            selected => !selected && (InkStrokeProperties.Instance._controlButton = false)
+        );
     }
     componentWillUnmount() {
         this._selDisposer?.();
@@ -70,36 +74,36 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke
     // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
-    screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1);
+    screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.NativeDimScaling?.() || 1);
 
     getAnchor = () => {
         console.log(document.activeElement);
         return this._subContentView?.getAnchor?.() || this.rootDoc;
-    }
+    };
 
     scrollFocus = (textAnchor: Doc, smooth: boolean) => {
         return this._subContentView?.scrollFocus?.(textAnchor, smooth);
-    }
+    };
     /**
-    * @returns the center of the ink stroke in the ink document's coordinate space (not screen space, and not the ink data coordinate space); 
-    * DocumentDecorations calls getBounds() on DocumentViews which call getCenter() if defined - in the case of ink it needs to be defined since
-    * the center of the ink stroke changes as the stroke is rotated.
-    */
+     * @returns the center of the ink stroke in the ink document's coordinate space (not screen space, and not the ink data coordinate space);
+     * DocumentDecorations calls getBounds() on DocumentViews which call getCenter() if defined - in the case of ink it needs to be defined since
+     * the center of the ink stroke changes as the stroke is rotated.
+     */
     getCenter = (xf: Transform) => {
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
         const angle = -NumCast(this.layoutDoc.rotation);
         const newPoints = inkData.map(pt => {
-            const newX = Math.cos(angle) * pt.X - Math.sin(angle) * pt.Y * inkScaleY / inkScaleX;
-            const newY = Math.sin(angle) * pt.X * inkScaleX / inkScaleY + Math.cos(angle) * pt.Y;
+            const newX = Math.cos(angle) * pt.X - (Math.sin(angle) * pt.Y * inkScaleY) / inkScaleX;
+            const newY = (Math.sin(angle) * pt.X * inkScaleX) / inkScaleY + Math.cos(angle) * pt.Y;
             return { X: newX, Y: newY };
         });
         const crx = (Math.max(...newPoints.map(np => np.X)) + Math.min(...newPoints.map(np => np.X))) / 2;
         const cry = (Math.max(...newPoints.map(np => np.Y)) + Math.min(...newPoints.map(np => np.Y))) / 2;
-        const cx = Math.cos(-angle) * crx - Math.sin(-angle) * cry * inkScaleY / inkScaleX;
-        const cy = Math.sin(-angle) * crx * inkScaleX / inkScaleY + Math.cos(-angle) * cry;
+        const cx = Math.cos(-angle) * crx - (Math.sin(-angle) * cry * inkScaleY) / inkScaleX;
+        const cy = (Math.sin(-angle) * crx * inkScaleX) / inkScaleY + Math.cos(-angle) * cry;
         const tc = xf.transformPoint((cx - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (cy - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2);
         return { X: tc[0], Y: tc[1] };
-    }
+    };
 
     /**
      * analyzes the ink stroke and saves the analysis of the stroke to the 'inkAnalysis' field,
@@ -107,7 +111,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     analyzeStrokes() {
         const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
-        CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]);
+        CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ['inkAnalysis', 'handwriting'], [data]);
     }
 
     /**
@@ -115,12 +119,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      *    When displayed as a mask, the stroke is rendered with mixBlendMode set to multiply so that the stroke will
      *  appear to illuminate what it covers up.  At the same time, all pixels that are not under the stroke will be
      *  dimmed by a semi-opaque overlay mask.
-    */
+     */
     public static toggleMask = action((inkDoc: Doc) => {
         inkDoc.isInkMask = !inkDoc.isInkMask;
-        inkDoc._backgroundColor = inkDoc.isInkMask ? "rgba(0,0,0,0.7)" : undefined;
-        inkDoc.mixBlendMode = inkDoc.isInkMask ? "hard-light" : undefined;
-        inkDoc.color = "#9b9b9bff";
+        inkDoc._backgroundColor = inkDoc.isInkMask ? 'rgba(0,0,0,0.7)' : undefined;
+        inkDoc.mixBlendMode = inkDoc.isInkMask ? 'hard-light' : undefined;
+        inkDoc.color = '#9b9b9bff';
         inkDoc._stayInCollection = inkDoc.isInkMask ? true : undefined;
     });
     /**
@@ -132,46 +136,60 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         this._handledClick = false;
         const inkView = this.props.docViewPath().lastElement();
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
-        const screenPts = inkData.map(point => this.screenToLocal().inverse().transformPoint(
-            (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
-            (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
+        const screenPts = inkData
+            .map(point =>
+                this.screenToLocal()
+                    .inverse()
+                    .transformPoint((point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)
+            )
+            .map(p => ({ X: p[0], Y: p[1] }));
         const { nearestSeg } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY });
         const controlIndex = nearestSeg;
         const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex;
         var controlUndo: UndoManager.Batch | undefined;
         const isEditing = InkStrokeProperties.Instance._controlButton && this.props.isSelected();
-        setupMoveUpEvents(this, e,
-            !isEditing ? returnFalse : action((e: PointerEvent, down: number[], delta: number[]) => {
-                if (!controlUndo) controlUndo = UndoManager.StartBatch("drag ink ctrl pt");
-                const inkMoveEnd = this.ptFromScreen({ X: delta[0], Y: delta[1] });
-                const inkMoveStart = this.ptFromScreen({ X: 0, Y: 0 });
-                InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex);
-                InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex + 3);
-                return false;
-            }),
-            !isEditing ? returnFalse : action(() => {
-                controlUndo?.end();
-                controlUndo = undefined;
-                UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
-            }),
+        setupMoveUpEvents(
+            this,
+            e,
+            !isEditing
+                ? returnFalse
+                : action((e: PointerEvent, down: number[], delta: number[]) => {
+                      if (!controlUndo) controlUndo = UndoManager.StartBatch('drag ink ctrl pt');
+                      const inkMoveEnd = this.ptFromScreen({ X: delta[0], Y: delta[1] });
+                      const inkMoveStart = this.ptFromScreen({ X: 0, Y: 0 });
+                      InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex);
+                      InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex + 3);
+                      return false;
+                  }),
+            !isEditing
+                ? returnFalse
+                : action(() => {
+                      controlUndo?.end();
+                      controlUndo = undefined;
+                      UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
+                  }),
             action((e: PointerEvent, doubleTap: boolean | undefined) => {
                 doubleTap = doubleTap || this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick;
                 if (doubleTap) {
                     InkStrokeProperties.Instance._controlButton = true;
                     InkStrokeProperties.Instance._currentPoint = -1;
-                    this._handledClick = true;  // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView
+                    this._handledClick = true; // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView
                     if (isEditing) {
                         this._nearestT && this._nearestSeg !== undefined && InkStrokeProperties.Instance.addPoints(this.props.docViewPath().lastElement(), this._nearestT, this._nearestSeg, this.inkScaledData().inkData.slice());
                     }
                 }
-            }), isEditing, isEditing, action(() => wasSelected && (InkStrokeProperties.Instance._currentPoint = -1)));
-    }
+            }),
+            isEditing,
+            isEditing,
+            action(() => wasSelected && (InkStrokeProperties.Instance._currentPoint = -1))
+        );
+    };
 
     /**
      * @param scrPt a point in the screen coordinate space
      * @returns the point in the ink data's coordinate space.
      */
-    ptFromScreen = (scrPt: { X: number, Y: number }) => {
+    ptFromScreen = (scrPt: { X: number; Y: number }) => {
         const { inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
         const docPt = this.screenToLocal().transformPoint(scrPt.X, scrPt.Y);
         const inkPt = {
@@ -179,39 +197,39 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             Y: (docPt[1] - inkStrokeWidth / 2) / inkScaleY + inkStrokeWidth / 2 + inkTop,
         };
         return inkPt;
-    }
+    };
 
     /**
      * @param inkPt a point in the ink data's coordinate space
      * @returns the screen point corresponding to the ink point
      */
-    ptToScreen = (inkPt: { X: number, Y: number }) => {
+    ptToScreen = (inkPt: { X: number; Y: number }) => {
         const { inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
         const docPt = {
             X: (inkPt.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
-            Y: (inkPt.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2
+            Y: (inkPt.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2,
         };
         const scrPt = this.screenToLocal().inverse().transformPoint(docPt.X, docPt.Y);
         return { X: scrPt[0], Y: scrPt[1] };
-    }
+    };
 
     /**
-    * Snaps a screen space point to this stroke, optionally skipping bezier segments indicated by 'excludeSegs'
-    * @param scrPt - the point to snap to this stroke
-    * @param excludeSegs - optional segments in this stroke to skip (this is used when dragging a point on the stroke and not wanting the drag point to snap to its neighboring segments)
-    *
-    * @returns the nearest ink space point on this stroke to the screen point AND the screen space distance from the snapped point to the nearest point
-    */
-    snapPt = (scrPt: { X: number, Y: number }, excludeSegs?: number[]) => {
+     * Snaps a screen space point to this stroke, optionally skipping bezier segments indicated by 'excludeSegs'
+     * @param scrPt - the point to snap to this stroke
+     * @param excludeSegs - optional segments in this stroke to skip (this is used when dragging a point on the stroke and not wanting the drag point to snap to its neighboring segments)
+     *
+     * @returns the nearest ink space point on this stroke to the screen point AND the screen space distance from the snapped point to the nearest point
+     */
+    snapPt = (scrPt: { X: number; Y: number }, excludeSegs?: number[]) => {
         const { inkData } = this.inkScaledData();
         const { nearestPt, distance } = InkStrokeProperties.nearestPtToStroke(inkData, this.ptFromScreen(scrPt), excludeSegs ?? []);
         return { nearestPt, distance: distance * this.screenToLocal().inverse().Scale };
-    }
+    };
 
     /**
-    * extracts key features from the inkData, including: the data points, the ink width, the ink bounds (top,left, width, height), and the scale
-    * factor for converting between ink and screen space.
-    */
+     * extracts key features from the inkData, including: the data points, the ink width, the ink bounds (top,left, width, height), and the scale
+     * factor for converting between ink and screen space.
+     */
     inkScaledData = () => {
         const inkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
         const inkStrokeWidth = NumCast(this.rootDoc.strokeWidth, 1);
@@ -228,27 +246,31 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             inkLeft,
             inkWidth,
             inkHeight,
-            inkScaleX: ((this.props.PanelWidth() - inkStrokeWidth) / ((inkWidth - inkStrokeWidth) || 1) || 1),
-            inkScaleY: ((this.props.PanelHeight() - inkStrokeWidth) / ((inkHeight - inkStrokeWidth) || 1) || 1)
+            inkScaleX: (this.props.PanelWidth() - inkStrokeWidth) / (inkWidth - inkStrokeWidth || 1) || 1,
+            inkScaleY: (this.props.PanelHeight() - inkStrokeWidth) / (inkHeight - inkStrokeWidth || 1) || 1,
         };
-    }
+    };
 
     //
-    // this updates the highlight for the nearest point on the curve to the cursor.  
+    // this updates the highlight for the nearest point on the curve to the cursor.
     // if the user double clicks, this highlighted point will be added as a control point in the curve.
     //
     @action
     onPointerMove = (e: React.PointerEvent) => {
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
-        const screenPts = inkData.map(point => this.screenToLocal().inverse().transformPoint(
-            (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
-            (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
+        const screenPts = inkData
+            .map(point =>
+                this.screenToLocal()
+                    .inverse()
+                    .transformPoint((point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)
+            )
+            .map(p => ({ X: p[0], Y: p[1] }));
         const { distance, nearestT, nearestSeg, nearestPt } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY });
 
         this._nearestT = nearestT;
         this._nearestSeg = nearestSeg;
         this._nearestScrPt = nearestPt;
-    }
+    };
 
     /**
      * @returns the nearest screen point to the cursor (to render a highlight for the point to be added)
@@ -263,50 +285,66 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     componentUI = (boundsLeft: number, boundsTop: number) => {
         const inkDoc = this.props.Document;
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
-        const screenSpaceCenterlineStrokeWidth = Math.min(3, inkStrokeWidth * this.screenToLocal().inverse().Scale);  // the width of the blue line widget that shows the centerline of the ink stroke
+        const screenSpaceCenterlineStrokeWidth = Math.min(3, inkStrokeWidth * this.screenToLocal().inverse().Scale); // the width of the blue line widget that shows the centerline of the ink stroke
 
         const screenInkWidth = this.screenToLocal().inverse().transformDirection(inkStrokeWidth, inkStrokeWidth);
-        const screenPts = inkData.map(point => this.screenToLocal().inverse().transformPoint(
-            (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
-            (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
+        const screenPts = inkData
+            .map(point =>
+                this.screenToLocal()
+                    .inverse()
+                    .transformPoint((point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)
+            )
+            .map(p => ({ X: p[0], Y: p[1] }));
         const screenHdlPts = screenPts;
 
         const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
         const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale);
-        return SnappingManager.GetIsDragging() ? (null) :
-            !InkStrokeProperties.Instance._controlButton ?
-                (!this.props.isSelected() || InkingStroke.IsClosed(inkData) ? (null) :
-                    <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
-                        <InkEndPtHandles
-                            inkView={this.props.docViewPath().lastElement()}
-                            inkDoc={inkDoc}
-                            startPt={screenPts[0]}
-                            endPt={screenPts.lastElement()}
-                            screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
-                    </div>) :
+        return SnappingManager.GetIsDragging() ? null : !InkStrokeProperties.Instance._controlButton ? (
+            !this.props.isSelected() || InkingStroke.IsClosed(inkData) ? null : (
                 <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
-                    {InteractionUtils.CreatePolyline(screenPts, 0, 0, Colors.MEDIUM_BLUE, screenInkWidth[0], screenSpaceCenterlineStrokeWidth,
-                        StrCast(inkDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), StrCast(inkDoc.strokeBezier),
-                        "none", startMarker, endMarker, markerScale * Math.min(screenSpaceCenterlineStrokeWidth, screenInkWidth[0] / screenSpaceCenterlineStrokeWidth), StrCast(inkDoc.strokeDash), 1, 1, "", "none", 1.0, false)}
-                    <InkControlPtHandles
-                        inkView={this.props.docViewPath().lastElement()}
-                        inkDoc={inkDoc}
-                        inkCtrlPoints={inkData}
-                        screenCtrlPoints={screenHdlPts}
-                        nearestScreenPt={this.nearestScreenPt}
-                        screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
-                    <InkTangentHandles
-                        inkView={this.props.docViewPath().lastElement()}
-                        inkDoc={inkDoc}
-                        screenCtrlPoints={screenHdlPts}
-                        screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth}
-                        ScreenToLocalTransform={this.screenToLocal} />
-                </div>;
-    }
+                    <InkEndPtHandles inkView={this.props.docViewPath().lastElement()} inkDoc={inkDoc} startPt={screenPts[0]} endPt={screenPts.lastElement()} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
+                </div>
+            )
+        ) : (
+            <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
+                {InteractionUtils.CreatePolyline(
+                    screenPts,
+                    0,
+                    0,
+                    Colors.MEDIUM_BLUE,
+                    screenInkWidth[0],
+                    screenSpaceCenterlineStrokeWidth,
+                    StrCast(inkDoc.strokeLineJoin),
+                    StrCast(this.layoutDoc.strokeLineCap),
+                    StrCast(inkDoc.strokeBezier),
+                    'none',
+                    startMarker,
+                    endMarker,
+                    markerScale * Math.min(screenSpaceCenterlineStrokeWidth, screenInkWidth[0] / screenSpaceCenterlineStrokeWidth),
+                    StrCast(inkDoc.strokeDash),
+                    1,
+                    1,
+                    '',
+                    'none',
+                    1.0,
+                    false
+                )}
+                <InkControlPtHandles
+                    inkView={this.props.docViewPath().lastElement()}
+                    inkDoc={inkDoc}
+                    inkCtrlPoints={inkData}
+                    screenCtrlPoints={screenHdlPts}
+                    nearestScreenPt={this.nearestScreenPt}
+                    screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth}
+                />
+                <InkTangentHandles inkView={this.props.docViewPath().lastElement()} inkDoc={inkDoc} screenCtrlPoints={screenHdlPts} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} ScreenToLocalTransform={this.screenToLocal} />
+            </div>
+        );
+    };
 
     _subContentView: DocComponentView | undefined;
-    setSubContentView = (doc: DocComponentView) => this._subContentView = doc;
+    setSubContentView = (doc: DocComponentView) => (this._subContentView = doc);
     render() {
         TraceMobx();
         const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData();
@@ -315,105 +353,181 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1);
         const closed = InkingStroke.IsClosed(inkData);
-        const fillColor = StrCast(this.layoutDoc.fillColor, "transparent");
-        const strokeColor = !closed && fillColor && fillColor !== "transparent" ? fillColor : StrCast(this.layoutDoc.color);
+        const fillColor = StrCast(this.layoutDoc.fillColor, 'transparent');
+        const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : StrCast(this.layoutDoc.color);
 
         // Visually renders the polygonal line made by the user.
-        const inkLine = InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, strokeColor, inkStrokeWidth, inkStrokeWidth,
-            StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap),
-            StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" ? "none" : fillColor, startMarker, endMarker,
-            markerScale, StrCast(this.layoutDoc.strokeDash), inkScaleX, inkScaleY, "", "none", 1.0, false);
+        const inkLine = InteractionUtils.CreatePolyline(
+            inkData,
+            inkLeft,
+            inkTop,
+            strokeColor,
+            inkStrokeWidth,
+            inkStrokeWidth,
+            StrCast(this.layoutDoc.strokeLineJoin),
+            StrCast(this.layoutDoc.strokeLineCap),
+            StrCast(this.layoutDoc.strokeBezier),
+            !closed ? 'none' : fillColor === 'transparent' ? 'none' : fillColor,
+            startMarker,
+            endMarker,
+            markerScale,
+            StrCast(this.layoutDoc.strokeDash),
+            inkScaleX,
+            inkScaleY,
+            '',
+            'none',
+            1.0,
+            false
+        );
         const highlightIndex = /*BoolCast(this.props.Document.isLinkButton) && */ Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        const highlightColor = !highlightIndex ?
-            StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== "transparent" ? StrCast(this.layoutDoc.color, "transparent") : "transparent") :
-            ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "yellow", "magenta", "cyan", "orange"][highlightIndex];
+        const highlightColor = !highlightIndex
+            ? StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent')
+            : ['transparent', 'rgb(68, 118, 247)', 'rgb(68, 118, 247)', 'yellow', 'magenta', 'cyan', 'orange'][highlightIndex];
         // Invisible polygonal line that enables the ink to be selected by the user.
-        const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, suppressFill: boolean = false) => InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, highlightColor,
-            inkStrokeWidth, fillColor && closed && highlightIndex ? highlightIndex / 2 : inkStrokeWidth + (fillColor ? closed ? 0 : (highlightIndex + 2) : 0),
-            StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap),
-            StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" || suppressFill ? "none" : fillColor, startMarker, endMarker,
-            markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? "none" : "visiblepainted"), 0.0,
-            false, downHdlr);
-        const fsize = +(StrCast(this.props.Document.fontSize, "12px").replace("px", ""));
-        // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size. 
+        const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, suppressFill: boolean = false) =>
+            InteractionUtils.CreatePolyline(
+                inkData,
+                inkLeft,
+                inkTop,
+                highlightColor,
+                inkStrokeWidth,
+                fillColor && closed && highlightIndex ? highlightIndex / 2 : inkStrokeWidth + (fillColor ? (closed ? 0 : highlightIndex + 2) : 0),
+                StrCast(this.layoutDoc.strokeLineJoin),
+                StrCast(this.layoutDoc.strokeLineCap),
+                StrCast(this.layoutDoc.strokeBezier),
+                !closed ? 'none' : fillColor === 'transparent' || suppressFill ? 'none' : fillColor,
+                startMarker,
+                endMarker,
+                markerScale,
+                undefined,
+                inkScaleX,
+                inkScaleY,
+                '',
+                this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? 'none' : 'visiblepainted'),
+                0.0,
+                false,
+                downHdlr
+            );
+        const fsize = +StrCast(this.props.Document.fontSize, '12px').replace('px', '');
+        // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size.
         // this attempts to figure out the lineHeight ratio by inquiring the body's lineHeight and dividing by the fontsize which should yield 1.428571429
         // see: https://bibwild.wordpress.com/2019/06/10/bootstrap-3-to-4-changes-in-how-font-size-line-height-and-spacing-is-done-or-what-happened-to-line-height-computed/
-        const lineHeightGuess = (+getComputedStyle(document.body).lineHeight.replace("px", "")) / (+getComputedStyle(document.body).fontSize.replace("px", ""));
+        const lineHeightGuess = +getComputedStyle(document.body).lineHeight.replace('px', '') / +getComputedStyle(document.body).fontSize.replace('px', '');
         const interactions = {
-            onPointerLeave: action(() => this._nearestScrPt = undefined),
+            onPointerLeave: action(() => (this._nearestScrPt = undefined)),
             onPointerMove: this.props.isSelected() ? this.onPointerMove : undefined,
             onClick: (e: React.MouseEvent) => this._handledClick && e.stopPropagation(),
             onContextMenu: () => {
                 const cm = ContextMenu.Instance;
-                !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" });
-            }
+                !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' });
+            },
         };
-        return <div className="inkStroke-wrapper">
-            <svg className="inkStroke"
-                style={{
-                    transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
-                    mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
-                    cursor: this.props.isSelected() ? "default" : undefined
-                }}
-                {...(!closed ? interactions : {})}
-            >
-                {closed ? inkLine : clickableLine(this.onPointerDown)}
-                {closed ? clickableLine(this.onPointerDown) : inkLine}
-            </svg>
-            {!closed || (!RTFCast(this.rootDoc.text)?.Text && !this.props.isSelected()) ? (null) :
-                <div className="inkStroke-text" style={{
-                    color: StrCast(this.layoutDoc.textColor, "black"),
-                    pointerEvents: this.props.isDocumentActive?.() ? "all" : undefined,
-                    width: this.layoutDoc[WidthSym](),
-                    transform: `scale(${this.props.scaling?.() || 1})`,
-                    transformOrigin: "top left",
-                    top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.scaling?.() || 1)) / 2
-                }}>
-                    <FormattedTextBox
-                        {...OmitKeys(this.props, ['children']).omit}
-                        setContentView={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text)
-                        yPadding={10}
-                        xPadding={10}
-                        fieldKey={"text"}
-                        fontSize={fsize}
-                        dontRegisterView={true}
-                        noSidebar={true}
-                        dontScale={true}
-                        isContentActive={this.isContentActive}
-                    />
-                </div>
-            }
-            {!closed ? null : <svg className="inkStroke"
-                style={{
-                    transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
-                    mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
-                    cursor: this.props.isSelected() ? "default" : undefined, position: "absolute"
-                }}
-                {...interactions}
-            >
-                {clickableLine(this.onPointerDown, true)}
-            </svg>}
-        </div>;
+        return (
+            <div className="inkStroke-wrapper">
+                <svg
+                    className="inkStroke"
+                    style={{
+                        transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                        mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
+                        cursor: this.props.isSelected() ? 'default' : undefined,
+                    }}
+                    {...(!closed ? interactions : {})}>
+                    {closed ? inkLine : clickableLine(this.onPointerDown)}
+                    {closed ? clickableLine(this.onPointerDown) : inkLine}
+                </svg>
+                {!closed || (!RTFCast(this.rootDoc.text)?.Text && !this.props.isSelected()) ? null : (
+                    <div
+                        className="inkStroke-text"
+                        style={{
+                            color: StrCast(this.layoutDoc.textColor, 'black'),
+                            pointerEvents: this.props.isDocumentActive?.() ? 'all' : undefined,
+                            width: this.layoutDoc[WidthSym](),
+                            transform: `scale(${this.props.NativeDimScaling?.() || 1})`,
+                            transformOrigin: 'top left',
+                            top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.NativeDimScaling?.() || 1)) / 2,
+                        }}>
+                        <FormattedTextBox
+                            {...OmitKeys(this.props, ['children']).omit}
+                            setContentView={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text)
+                            yPadding={10}
+                            xPadding={10}
+                            fieldKey={'text'}
+                            fontSize={fsize}
+                            dontRegisterView={true}
+                            noSidebar={true}
+                            dontScale={true}
+                            isContentActive={this.isContentActive}
+                        />
+                    </div>
+                )}
+                {!closed ? null : (
+                    <svg
+                        className="inkStroke"
+                        style={{
+                            transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                            mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
+                            cursor: this.props.isSelected() ? 'default' : undefined,
+                            position: 'absolute',
+                        }}
+                        {...interactions}>
+                        {clickableLine(this.onPointerDown, true)}
+                    </svg>
+                )}
+            </div>
+        );
     }
 }
 
-
-export function SetActiveInkWidth(width: string): void { !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); }
-export function SetActiveBezierApprox(bezier: string): void { ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? "" : bezier); }
-export function SetActiveInkColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeInkColor = value); }
-export function SetActiveFillColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeFillColor = value); }
-export function SetActiveArrowStart(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); }
-export function SetActiveArrowEnd(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); }
-export function SetActiveArrowScale(value: number) { ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); }
-export function SetActiveDash(dash: string): void { !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); }
-export function ActiveInkPen(): Doc { return Doc.UserDoc(); }
-export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, "black"); }
-export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ""); }
-export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); }
-export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); }
-export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); }
-export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); }
-export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); }
-export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); }
+export function SetActiveInkWidth(width: string): void {
+    !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width);
+}
+export function SetActiveBezierApprox(bezier: string): void {
+    ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier);
+}
+export function SetActiveInkColor(value: string) {
+    ActiveInkPen() && (ActiveInkPen().activeInkColor = value);
+}
+export function SetActiveFillColor(value: string) {
+    ActiveInkPen() && (ActiveInkPen().activeFillColor = value);
+}
+export function SetActiveArrowStart(value: string) {
+    ActiveInkPen() && (ActiveInkPen().activeArrowStart = value);
+}
+export function SetActiveArrowEnd(value: string) {
+    ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value);
+}
+export function SetActiveArrowScale(value: number) {
+    ActiveInkPen() && (ActiveInkPen().activeArrowScale = value);
+}
+export function SetActiveDash(dash: string): void {
+    !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash);
+}
+export function ActiveInkPen(): Doc {
+    return Doc.UserDoc();
+}
+export function ActiveInkColor(): string {
+    return StrCast(ActiveInkPen()?.activeInkColor, 'black');
+}
+export function ActiveFillColor(): string {
+    return StrCast(ActiveInkPen()?.activeFillColor, '');
+}
+export function ActiveArrowStart(): string {
+    return StrCast(ActiveInkPen()?.activeArrowStart, '');
+}
+export function ActiveArrowEnd(): string {
+    return StrCast(ActiveInkPen()?.activeArrowEnd, '');
+}
+export function ActiveArrowScale(): number {
+    return NumCast(ActiveInkPen()?.activeArrowScale, 1);
+}
+export function ActiveDash(): string {
+    return StrCast(ActiveInkPen()?.activeDash, '0');
+}
+export function ActiveInkWidth(): number {
+    return Number(ActiveInkPen()?.activeInkWidth);
+}
+export function ActiveInkBezierApprox(): string {
+    return StrCast(ActiveInkPen()?.activeInkBezier);
+}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index e81a9c40f..9fdf9d2be 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -93,7 +93,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         this.props
             .ScreenToLocalTransform()
             .translate(Doc.NativeWidth(this.props.dataDoc), 0)
-            .scale(this.props.scaling?.() || 1);
+            .scale(this.props.NativeDimScaling?.() || 1);
     // panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 :
     //     this.props.usePanelWidth ? this.props.PanelWidth() :
     //         (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth);
@@ -164,7 +164,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
                         select={emptyFunction}
-                        scaling={returnOne}
+                        NativeDimScaling={returnOne}
                         childShowTitle={this.showTitle}
                         childDocumentsActive={this.props.isContentActive}
                         whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 340a5df45..334f381be 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -250,9 +250,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     return doc.z
                         ? `#9c9396  ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
                         : props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK
-                        ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                        ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : NumCast(doc.group, -1) !== -1 && doc.type !== DocumentType.INK
-                        ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                        ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : isBackground()
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
                         : StrCast(doc.boxShadow, '');
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index f1d842386..714d8421a 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -519,7 +519,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                     isAnnotationOverlay={true}
                     isDocumentActive={returnFalse}
                     select={emptyFunction}
-                    scaling={returnOne}
+                    NativeDimScaling={returnOne}
                     xMargin={25}
                     yMargin={10}
                     ScreenToLocalTransform={this.dictationScreenToLocalTransform}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 809a73a77..dce792d19 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -235,7 +235,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
                 PanelWidth={this.documentTitleWidth}
                 PanelHeight={this.documentTitleHeight}
-                scaling={returnOne}
+                NativeDimScaling={returnOne}
                 onKey={this.onKey}
                 docFilters={returnEmptyFilter}
                 docRangeFilters={returnEmptyFilter}
@@ -351,7 +351,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return Doc.NativeHeight(this.Document, undefined, true);
     }
 
-    @computed get contentScaling() {
+    /// scale factor for tree view so that it will fit within it's panel bounds
+    @computed get nativeDimScaling() {
         const nw = this.nativeWidth;
         const nh = this.nativeHeight;
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
@@ -365,7 +366,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins);
     truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
     onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
-    panelWidth = () => Math.max(0, this.props.PanelWidth() - this.marginX() - CollectionTreeView.expandViewLabelSize) * (this.props.scaling?.() || 1);
+    panelWidth = () => Math.max(0, this.props.PanelWidth() - this.marginX() - CollectionTreeView.expandViewLabelSize) * (this.props.NativeDimScaling?.() || 1);
 
     addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
     remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
@@ -389,9 +390,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 <div
                     className="collectionTreeView-container"
                     style={{
-                        transform: this.outlineMode ? `scale(${this.contentScaling})` : '',
+                        transform: this.outlineMode ? `scale(${this.nativeDimScaling})` : '',
                         paddingLeft: `${this.marginX()}px`,
-                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : '',
+                        width: this.outlineMode ? `calc(${100 / this.nativeDimScaling}%)` : '',
                     }}
                     onContextMenu={this.onContextMenu}>
                     {!this.buttonMenu && !this.noviceExplainer ? null : (
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 5a2103e98..aa1330762 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -403,7 +403,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const aspect = Doc.NativeAspect(layoutDoc);
         if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]());
         if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth()));
-        return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
+        return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.NativeDimScaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
     };
     docHeight = () => {
         const layoutDoc = this.layoutDoc;
@@ -514,7 +514,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     rtfWidth = () => {
         const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
-        return Math.min(layout[WidthSym](), this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1);
+        return Math.min(layout[WidthSym](), this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.NativeDimScaling?.() || 1);
     };
     rtfHeight = () => {
         const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
@@ -921,7 +921,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.treeView.props.Document}
-                ContentScaling={returnOne}
             />
         );
 
@@ -992,7 +991,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideResizeHandles={this.props.treeView.outlineMode}
                 onClick={this.onChildClick}
                 focus={this.refocus}
-                ContentScaling={returnOne}
                 onKey={this.onKeyDown}
                 hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
                 dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5d7a12122..3e938ec1c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -58,7 +58,7 @@ import e = require('connect-flash');
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
     viewDefDivClick?: ScriptField;
-    childPointerEvents?: boolean;
+    childPointerEvents?: string;
     scaleField?: string;
     noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
     engineProps?: any;
@@ -150,11 +150,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
     }
     @computed get cachedCenteringShiftX(): number {
-        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
         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.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
         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 {
@@ -1065,7 +1065,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || DocListCast(Doc.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();
@@ -1251,7 +1251,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         if (scale) {
             const maxZoom = 5; // sets the limit for how far we will zoom. this is useful for preventing small text boxes from filling the screen. So probably needs to be more sophisticated to consider more about the target and context
-            const newScale = Math.min(maxZoom, (1 / (this.contentScaling || 1)) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
+            const newScale = Math.min(maxZoom, (1 / (this.nativeDimScaling || 1)) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
             return {
                 panX: this.props.isAnnotationOverlay ? bounds.left - (Doc.NativeWidth(this.layoutDoc) / newScale - bounds.width) / 2 : (bounds.left + bounds.right) / 2,
                 panY: this.props.isAnnotationOverlay ? bounds.top - (Doc.NativeHeight(this.layoutDoc) / newScale - bounds.height) / 2 : (bounds.top + bounds.bot) / 2,
@@ -1301,7 +1301,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     pointerEvents = () => {
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
-        const pointerEvents = this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ? 'all' : this.props.viewDefDivClick || (engine === 'pass' && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.();
+        const pointerEvents = this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === 'pass' && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.());
         return pointerEvents;
     };
     getChildDocView(entry: PoolData) {
@@ -1631,9 +1631,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 img.style.width = canvas.style.width;
                 img.style.height = canvas.style.height;
                 const newCan = newDiv as HTMLCanvasElement;
-                const parEle = newCan.parentElement as HTMLElement;
-                parEle.removeChild(newCan);
-                parEle.appendChild(img);
+                if (newCan) {
+                    const parEle = newCan.parentElement as HTMLElement;
+                    parEle.removeChild(newCan);
+                    parEle.appendChild(img);
+                }
             }
         }
     }
@@ -1943,13 +1945,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         );
     }
 
-    @computed get contentScaling() {
+    @computed get nativeDimScaling() {
         if (this._firstRender || (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent)) return 0;
         const nw = this.nativeWidth;
         const nh = this.nativeHeight;
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
-        return wscale < hscale ? wscale : hscale;
+        return wscale < hscale || this.layoutDoc.fitWidth ? wscale : hscale;
     }
 
     private groupDropDisposer?: DragManager.DragDropDisposer;
@@ -1983,9 +1985,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                             : SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement())
                             ? 'all'
                             : (this.props.pointerEvents?.() as any),
-                    transform: `scale(${this.contentScaling || 1})`,
-                    width: `${100 / (this.contentScaling || 1)}%`,
-                    height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`, // : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
+                    transform: `scale(${this.nativeDimScaling || 1})`,
+                    width: `${100 / (this.nativeDimScaling || 1)}%`,
+                    height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.nativeDimScaling || 1)}%`, // : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
                 }}>
                 {this._firstRender ? this.placeholder : this.marqueeView}
                 {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 777ef464f..465dbfe6d 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,6 +1,6 @@
 import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
-import * as React from "react";
+import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -11,11 +11,10 @@ import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
-import "./CollectionMulticolumnView.scss";
+import './CollectionMulticolumnView.scss';
 import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
 
-
 interface WidthSpecifier {
     magnitude: number;
     unit: string;
@@ -27,8 +26,8 @@ interface LayoutData {
 }
 
 export const DimUnit = {
-    Pixel: "px",
-    Ratio: "*"
+    Pixel: 'px',
+    Ratio: '*',
 };
 
 const resolvedUnits = Object.values(DimUnit);
@@ -36,14 +35,13 @@ const resizerWidth = 8;
 
 @observer
 export class CollectionMulticolumnView extends CollectionSubView() {
-
     /**
      * @returns the list of layout documents whose width unit is
      * *, denoting that it will be displayed with a ratio, not fixed pixel, value
      */
     @computed
     private get ratioDefinedDocs() {
-        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio);
+        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, '*') === DimUnit.Ratio);
     }
 
     @computed
@@ -65,10 +63,10 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         let starSum = 0;
         const widthSpecifiers: WidthSpecifier[] = [];
         this.childLayoutPairs.map(pair => {
-            const unit = StrCast(pair.layout._dimUnit, "*");
+            const unit = StrCast(pair.layout._dimUnit, '*');
             const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim);
             if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
-                (unit === DimUnit.Ratio) && (starSum += magnitude);
+                unit === DimUnit.Ratio && (starSum += magnitude);
                 widthSpecifiers.push({ magnitude, unit });
             }
             /**
@@ -100,14 +98,13 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      * This returns the total quantity, in pixels, that this
      * view needs to reserve for child documents that have
      * (with higher priority) requested a fixed pixel width.
-     * 
+     *
      * If the underlying resolvedLayoutInformation returns null
      * because we're waiting on promises to resolve, this value will be undefined as well.
      */
     @computed
     private get totalFixedAllocation(): number | undefined {
-        return this.resolvedLayoutInformation?.widthSpecifiers.reduce(
-            (sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
+        return this.resolvedLayoutInformation?.widthSpecifiers.reduce((sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
     }
 
     /**
@@ -115,7 +112,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      * view needs to reserve for child documents that have
      * (with lower priority) requested a certain relative proportion of the
      * remaining pixel width not allocated for fixed widths.
-     * 
+     *
      * If the underlying totalFixedAllocation returns undefined
      * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
      */
@@ -135,7 +132,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      * this accessor returns 1000 / (2 + 2 + 1), or 200px.
      * Elsewhere, this is then multiplied by each relative-width
      * document's (potentially decimal) * count to compute its actual width (400px, 400px and 200px).
-     * 
+     *
      * If the underlying totalRatioAllocation or this.resolveLayoutInformation return undefined
      * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
      */
@@ -165,17 +162,17 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             return 0; // we're still waiting on promises to resolve
         }
         let width = NumCast(layout._dimMagnitude, this.minimumDim);
-        if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) {
+        if (StrCast(layout._dimUnit, '*') === DimUnit.Ratio) {
             width *= columnUnitLength;
         }
         return width;
-    }
+    };
 
     /**
      * @returns the transform that will correctly place
      * the document decorations box, shifted to the right by
      * the sum of all the resolved column widths of the
-     * documents before the target. 
+     * documents before the target.
      */
     private lookupIndividualTransform = (layout: Doc) => {
         const columnUnitLength = this.columnUnitLength;
@@ -185,12 +182,12 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         let offset = 0;
         for (const { layout: candidate } of this.childLayoutPairs) {
             if (candidate === layout) {
-                return this.props.ScreenToLocalTransform().translate(-offset / (this.props.scaling?.() || 1), 0);
+                return this.props.ScreenToLocalTransform().translate(-offset / (this.props.NativeDimScaling?.() || 1), 0);
             }
             offset += this.lookupPixels(candidate) + resizerWidth;
         }
         return Transform.Identity(); // type coersion, this case should never be hit
-    }
+    };
 
     @undoBatch
     @action
@@ -198,16 +195,17 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         let dropInd = -1;
         if (de.complete.docDragData && this._mainCont) {
             let curInd = -1;
-            de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                curInd = this.childDocs.indexOf(d);
-            }));
+            de.complete.docDragData?.droppedDocuments.forEach(
+                action((d: Doc) => {
+                    curInd = this.childDocs.indexOf(d);
+                })
+            );
             Array.from(this._mainCont.children).forEach((child, index) => {
                 const brect = child.getBoundingClientRect();
                 if (brect.x < de.x && brect.x + brect.width > de.x) {
                     if (curInd !== -1 && curInd === Math.floor(index / 2)) {
                         dropInd = curInd;
-                    }
-                    else if (child.className === "multiColumnResizer") {
+                    } else if (child.className === 'multiColumnResizer') {
                         dropInd = Math.floor(index / 2);
                     } else {
                         dropInd = Math.ceil(index / 2 + (de.x - brect.x > brect.width / 2 ? 0 : -1));
@@ -215,76 +213,80 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 }
             });
             if (super.onInternalDrop(e, de)) {
-                de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                    d._dimUnit = "*";
-                    d._dimMagnitude = 1;
-                    if (dropInd !== curInd || dropInd === -1) {
-                        if (this.childDocs.includes(d)) {
-                            if (dropInd > this.childDocs.indexOf(d)) dropInd--;
+                de.complete.docDragData?.droppedDocuments.forEach(
+                    action((d: Doc) => {
+                        d._dimUnit = '*';
+                        d._dimMagnitude = 1;
+                        if (dropInd !== curInd || dropInd === -1) {
+                            if (this.childDocs.includes(d)) {
+                                if (dropInd > this.childDocs.indexOf(d)) dropInd--;
+                            }
+                            Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
+                            Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
                         }
-                        Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
-                        Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
-                    }
-                }));
+                    })
+                );
             }
         }
         return false;
-    }
-
+    };
 
     onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     addDocTab = (doc: Doc, where: string) => {
-        if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+        if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
             this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
             return true;
         }
         return this.props.addDocTab(doc, where);
-    }
+    };
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || 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}
-            DataDoc={layout.resolvedDataDoc as Doc}
-            styleProvider={this.props.styleProvider}
-            docViewPath={this.props.docViewPath}
-            LayoutTemplate={this.props.childLayoutTemplate}
-            LayoutTemplateString={this.props.childLayoutString}
-            renderDepth={this.props.renderDepth + 1}
-            PanelWidth={width}
-            PanelHeight={height}
-            rootSelected={this.rootSelected}
-            dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
-            onClick={this.onChildClickHandler}
-            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}
-            searchFilterDocs={this.searchFilterDocs}
-            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-            ContainingCollectionView={this.props.CollectionView}
-            dontRegisterView={this.props.dontRegisterView}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-            addDocTab={this.addDocTab}
-            pinToPres={this.props.pinToPres}
-            bringToFront={returnFalse}
-        />;
-    }
+        return (
+            <DocumentView
+                Document={layout}
+                DataDoc={layout.resolvedDataDoc as Doc}
+                styleProvider={this.props.styleProvider}
+                docViewPath={this.props.docViewPath}
+                LayoutTemplate={this.props.childLayoutTemplate}
+                LayoutTemplateString={this.props.childLayoutString}
+                renderDepth={this.props.renderDepth + 1}
+                PanelWidth={width}
+                PanelHeight={height}
+                rootSelected={this.rootSelected}
+                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+                onClick={this.onChildClickHandler}
+                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}
+                searchFilterDocs={this.searchFilterDocs}
+                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                ContainingCollectionView={this.props.CollectionView}
+                dontRegisterView={this.props.dontRegisterView}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                addDocTab={this.addDocTab}
+                pinToPres={this.props.pinToPres}
+                bringToFront={returnFalse}
+            />
+        );
+    };
     /**
      * @returns the resolved list of rendered child documents, displayed
-     * at their resolved pixel widths, each separated by a resizer. 
+     * at their resolved pixel widths, each separated by a resizer.
      */
     @computed
     private get contents(): JSX.Element[] | null {
@@ -293,22 +295,20 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         const collector: JSX.Element[] = [];
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
-            const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)).scale((this.props.scaling?.() || 1));
+            const dxf = () =>
+                this.lookupIndividualTransform(layout)
+                    .translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin))
+                    .scale(this.props.NativeDimScaling?.() || 1);
             const width = () => this.lookupPixels(layout);
             const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
             collector.push(
-                <div className={"document-wrapper"}
-                    key={"wrapper" + i}
-                    style={{ width: width() }} >
+                <div className={'document-wrapper'} key={'wrapper' + i} style={{ width: width() }}>
                     {this.getDisplayDoc(layout, dxf, width, height)}
-                    <WidthLabel
-                        layout={layout}
-                        collectionDoc={Document}
-                    />
+                    <WidthLabel layout={layout} collectionDoc={Document} />
                 </div>,
                 <ResizeBar
                     width={resizerWidth}
-                    key={"resizer" + i}
+                    key={'resizer' + i}
                     styleProvider={this.props.styleProvider}
                     isContentActive={this.props.isContentActive}
                     select={this.props.select}
@@ -324,16 +324,19 @@ export class CollectionMulticolumnView extends CollectionSubView() {
 
     render(): JSX.Element {
         return (
-            <div className={"collectionMulticolumnView_contents"} ref={this.createDashEventsTarget}
+            <div
+                className={'collectionMulticolumnView_contents'}
+                ref={this.createDashEventsTarget}
                 style={{
                     width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
                     height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
-                    marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
-                    marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin)
-                }} >
+                    marginLeft: NumCast(this.props.Document._xMargin),
+                    marginRight: NumCast(this.props.Document._xMargin),
+                    marginTop: NumCast(this.props.Document._yMargin),
+                    marginBottom: NumCast(this.props.Document._yMargin),
+                }}>
                 {this.contents}
             </div>
         );
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 08385bcb5..f8de4e5de 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,6 +1,6 @@
 import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
-import * as React from "react";
+import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -11,7 +11,7 @@ import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
-import "./CollectionMultirowView.scss";
+import './CollectionMultirowView.scss';
 import HeightLabel from './MultirowHeightLabel';
 import ResizeBar from './MultirowResizer';
 
@@ -26,8 +26,8 @@ interface LayoutData {
 }
 
 export const DimUnit = {
-    Pixel: "px",
-    Ratio: "*"
+    Pixel: 'px',
+    Ratio: '*',
 };
 
 const resolvedUnits = Object.values(DimUnit);
@@ -35,14 +35,13 @@ const resizerHeight = 8;
 
 @observer
 export class CollectionMultirowView extends CollectionSubView() {
-
     /**
      * @returns the list of layout documents whose width unit is
      * *, denoting that it will be displayed with a ratio, not fixed pixel, value
      */
     @computed
     private get ratioDefinedDocs() {
-        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio);
+        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, '*') === DimUnit.Ratio);
     }
 
     @computed
@@ -64,10 +63,10 @@ export class CollectionMultirowView extends CollectionSubView() {
         let starSum = 0;
         const heightSpecifiers: HeightSpecifier[] = [];
         this.childLayoutPairs.map(pair => {
-            const unit = StrCast(pair.layout._dimUnit, "*");
+            const unit = StrCast(pair.layout._dimUnit, '*');
             const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim);
             if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
-                (unit === DimUnit.Ratio) && (starSum += magnitude);
+                unit === DimUnit.Ratio && (starSum += magnitude);
                 heightSpecifiers.push({ magnitude, unit });
             }
             /**
@@ -99,14 +98,13 @@ export class CollectionMultirowView extends CollectionSubView() {
      * This returns the total quantity, in pixels, that this
      * view needs to reserve for child documents that have
      * (with higher priority) requested a fixed pixel width.
-     * 
+     *
      * If the underlying resolvedLayoutInformation returns null
      * because we're waiting on promises to resolve, this value will be undefined as well.
      */
     @computed
     private get totalFixedAllocation(): number | undefined {
-        return this.resolvedLayoutInformation?.heightSpecifiers.reduce(
-            (sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
+        return this.resolvedLayoutInformation?.heightSpecifiers.reduce((sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
     }
 
     /**
@@ -114,7 +112,7 @@ export class CollectionMultirowView extends CollectionSubView() {
      * view needs to reserve for child documents that have
      * (with lower priority) requested a certain relative proportion of the
      * remaining pixel width not allocated for fixed widths.
-     * 
+     *
      * If the underlying totalFixedAllocation returns undefined
      * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
      */
@@ -134,7 +132,7 @@ export class CollectionMultirowView extends CollectionSubView() {
      * this accessor returns 1000 / (2 + 2 + 1), or 200px.
      * Elsewhere, this is then multiplied by each relative-width
      * document's (potentially decimal) * count to compute its actual width (400px, 400px and 200px).
-     * 
+     *
      * If the underlying totalRatioAllocation or this.resolveLayoutInformation return undefined
      * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
      */
@@ -164,17 +162,17 @@ export class CollectionMultirowView extends CollectionSubView() {
             return 0; // we're still waiting on promises to resolve
         }
         let height = NumCast(layout._dimMagnitude, this.minimumDim);
-        if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) {
+        if (StrCast(layout._dimUnit, '*') === DimUnit.Ratio) {
             height *= rowUnitLength;
         }
         return height;
-    }
+    };
 
     /**
      * @returns the transform that will correctly place
      * the document decorations box, shifted to the right by
      * the sum of all the resolved row widths of the
-     * documents before the target. 
+     * documents before the target.
      */
     private lookupIndividualTransform = (layout: Doc) => {
         const rowUnitLength = this.rowUnitLength;
@@ -184,13 +182,12 @@ export class CollectionMultirowView extends CollectionSubView() {
         let offset = 0;
         for (const { layout: candidate } of this.childLayoutPairs) {
             if (candidate === layout) {
-                return this.props.ScreenToLocalTransform().translate(0, -offset / (this.props.scaling?.() || 1));
+                return this.props.ScreenToLocalTransform().translate(0, -offset / (this.props.NativeDimScaling?.() || 1));
             }
             offset += this.lookupPixels(candidate) + resizerHeight;
         }
         return Transform.Identity(); // type coersion, this case should never be hit
-    }
-
+    };
 
     @undoBatch
     @action
@@ -198,16 +195,17 @@ export class CollectionMultirowView extends CollectionSubView() {
         let dropInd = -1;
         if (de.complete.docDragData && this._mainCont) {
             let curInd = -1;
-            de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                curInd = this.childDocs.indexOf(d);
-            }));
+            de.complete.docDragData?.droppedDocuments.forEach(
+                action((d: Doc) => {
+                    curInd = this.childDocs.indexOf(d);
+                })
+            );
             Array.from(this._mainCont.children).forEach((child, index) => {
                 const brect = child.getBoundingClientRect();
                 if (brect.y < de.y && brect.y + brect.height > de.y) {
                     if (curInd !== -1 && curInd === Math.floor(index / 2)) {
                         dropInd = curInd;
-                    }
-                    else if (child.className === "multiColumnResizer") {
+                    } else if (child.className === 'multiColumnResizer') {
                         dropInd = Math.floor(index / 2);
                     } else {
                         dropInd = Math.ceil(index / 2 + (de.y - brect.y > brect.height / 2 ? 0 : -1));
@@ -215,75 +213,79 @@ export class CollectionMultirowView extends CollectionSubView() {
                 }
             });
             if (super.onInternalDrop(e, de)) {
-                de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                    d._dimUnit = "*";
-                    d._dimMagnitude = 1;
-                    if (dropInd !== curInd || dropInd === -1) {
-                        if (this.childDocs.includes(d)) {
-                            if (dropInd > this.childDocs.indexOf(d)) dropInd--;
+                de.complete.docDragData?.droppedDocuments.forEach(
+                    action((d: Doc) => {
+                        d._dimUnit = '*';
+                        d._dimMagnitude = 1;
+                        if (dropInd !== curInd || dropInd === -1) {
+                            if (this.childDocs.includes(d)) {
+                                if (dropInd > this.childDocs.indexOf(d)) dropInd--;
+                            }
+                            Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
+                            Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
                         }
-                        Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
-                        Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
-                    }
-                }));
+                    })
+                );
             }
         }
         return false;
-    }
-
+    };
 
     onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     addDocTab = (doc: Doc, where: string) => {
-        if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+        if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
             this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
             return true;
         }
         return this.props.addDocTab(doc, where);
-    }
+    };
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || 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}
-            DataDoc={layout.resolvedDataDoc as Doc}
-            styleProvider={this.props.styleProvider}
-            docViewPath={this.props.docViewPath}
-            LayoutTemplate={this.props.childLayoutTemplate}
-            LayoutTemplateString={this.props.childLayoutString}
-            renderDepth={this.props.renderDepth + 1}
-            PanelWidth={width}
-            PanelHeight={height}
-            rootSelected={this.rootSelected}
-            dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
-            onClick={this.onChildClickHandler}
-            onDoubleClick={this.onChildDoubleClickHandler}
-            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}
-            searchFilterDocs={this.searchFilterDocs}
-            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-            ContainingCollectionView={this.props.CollectionView}
-            dontRegisterView={this.props.dontRegisterView}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-            addDocTab={this.addDocTab}
-            pinToPres={this.props.pinToPres}
-            bringToFront={returnFalse}
-        />;
-    }
+        return (
+            <DocumentView
+                Document={layout}
+                DataDoc={layout.resolvedDataDoc as Doc}
+                styleProvider={this.props.styleProvider}
+                docViewPath={this.props.docViewPath}
+                LayoutTemplate={this.props.childLayoutTemplate}
+                LayoutTemplateString={this.props.childLayoutString}
+                renderDepth={this.props.renderDepth + 1}
+                PanelWidth={width}
+                PanelHeight={height}
+                rootSelected={this.rootSelected}
+                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+                onClick={this.onChildClickHandler}
+                onDoubleClick={this.onChildDoubleClickHandler}
+                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}
+                searchFilterDocs={this.searchFilterDocs}
+                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                ContainingCollectionView={this.props.CollectionView}
+                dontRegisterView={this.props.dontRegisterView}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                addDocTab={this.addDocTab}
+                pinToPres={this.props.pinToPres}
+                bringToFront={returnFalse}
+            />
+        );
+    };
     /**
      * @returns the resolved list of rendered child documents, displayed
-     * at their resolved pixel widths, each separated by a resizer. 
+     * at their resolved pixel widths, each separated by a resizer.
      */
     @computed
     private get contents(): JSX.Element[] | null {
@@ -292,13 +294,14 @@ export class CollectionMultirowView extends CollectionSubView() {
         const collector: JSX.Element[] = [];
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
-            const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)).scale((this.props.scaling?.() || 1));
+            const dxf = () =>
+                this.lookupIndividualTransform(layout)
+                    .translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin))
+                    .scale(this.props.NativeDimScaling?.() || 1);
             const height = () => this.lookupPixels(layout);
             const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
             collector.push(
-                <div className={"document-wrapper"}
-                    style={{ height: height() }}
-                    key={"wrapper" + i} >
+                <div className={'document-wrapper'} style={{ height: height() }} key={'wrapper' + i}>
                     {this.getDisplayDoc(layout, dxf, width, height)}
                     <HeightLabel layout={layout} collectionDoc={Document} />
                 </div>,
@@ -306,7 +309,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                     height={resizerHeight}
                     styleProvider={this.props.styleProvider}
                     isContentActive={this.props.isContentActive}
-                    key={"resizer" + i}
+                    key={'resizer' + i}
                     columnUnitLength={this.getRowUnitLength}
                     toTop={layout}
                     toBottom={childLayoutPairs[i + 1]?.layout}
@@ -319,16 +322,19 @@ export class CollectionMultirowView extends CollectionSubView() {
 
     render(): JSX.Element {
         return (
-            <div className={"collectionMultirowView_contents"}
+            <div
+                className={'collectionMultirowView_contents'}
                 style={{
                     width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
                     height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
-                    marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
-                    marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin)
-                }} ref={this.createDashEventsTarget}>
+                    marginLeft: NumCast(this.props.Document._xMargin),
+                    marginRight: NumCast(this.props.Document._xMargin),
+                    marginTop: NumCast(this.props.Document._yMargin),
+                    marginBottom: NumCast(this.props.Document._yMargin),
+                }}
+                ref={this.createDashEventsTarget}>
                 {this.contents}
             </div>
         );
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index f1d8123da..381436a56 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -118,7 +118,7 @@ export class DocumentContentsView extends React.Component<
         FormattedTextBoxProps & {
             isSelected: (outsideReaction: boolean) => boolean;
             select: (ctrl: boolean) => void;
-            scaling?: () => number;
+            NativeDimScaling?: () => number;
             setHeight?: (height: number) => void;
             layoutKey: string;
         }
@@ -161,7 +161,6 @@ export class DocumentContentsView extends React.Component<
             'LayoutTemplateString',
             'LayoutTemplate',
             'dontCenter',
-            'ContentScaling',
             'contextMenuItems',
             'onClick',
             'onDoubleClick',
@@ -195,7 +194,7 @@ export class DocumentContentsView extends React.Component<
 
         // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'>
         const replacer2 = (match: any, p1: string, offset: any, string: any) => {
-            return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.scaling?.() || 1}' htmltag='${p1}'`;
+            return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.NativeDimScaling?.() || 1}' htmltag='${p1}'`;
         };
         layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
 
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 78d35ab99..5939d1680 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -1,24 +1,24 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../fields/Doc";
-import { StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { DragManager } from "../../util/DragManager";
-import { Hypothesis } from "../../util/HypothesisUtils";
-import { LinkManager } from "../../util/LinkManager";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { Colors } from "../global/globalEnums";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Opt } from '../../../fields/Doc';
+import { StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { DragManager } from '../../util/DragManager';
+import { Hypothesis } from '../../util/HypothesisUtils';
+import { LinkManager } from '../../util/LinkManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { Colors } from '../global/globalEnums';
 import './DocumentLinksButton.scss';
-import { DocumentView } from "./DocumentView";
-import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
-import { TaskCompletionBox } from "./TaskCompletedBox";
-import React = require("react");
+import { DocumentView } from './DocumentView';
+import { LinkDescriptionPopup } from './LinkDescriptionPopup';
+import { TaskCompletionBox } from './TaskCompletedBox';
+import React = require('react');
 
-const higflyout = require("@hig/flyout");
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
@@ -28,7 +28,7 @@ interface DocumentLinksButtonProps {
     AlwaysOn?: boolean;
     InMenu?: boolean;
     StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
-    ContentScaling?: () => number;
+    scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
 }
 @observer
 export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
@@ -42,53 +42,71 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     @observable public static invisibleWebDoc: Opt<Doc>;
     public static invisibleWebRef = React.createRef<HTMLDivElement>();
 
-    @action @undoBatch
+    @action
+    @undoBatch
     onLinkButtonMoved = (e: PointerEvent) => {
         if (this.props.InMenu && this.props.StartLink) {
             if (this._linkButton.current !== null) {
-                const linkDrag = UndoManager.StartBatch("Drag Link");
-                this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
-                    dragComplete: dropEv => {
-                        if (this.props.View && dropEv.linkDocument) {// dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
-                            !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = "hyperlink");
-                        }
-                        linkDrag?.end();
-                    },
-                    hideSource: false
-                });
+                const linkDrag = UndoManager.StartBatch('Drag Link');
+                this.props.View &&
+                    DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
+                        dragComplete: dropEv => {
+                            if (this.props.View && dropEv.linkDocument) {
+                                // dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
+                                !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = 'hyperlink');
+                            }
+                            linkDrag?.end();
+                        },
+                        hideSource: false,
+                    });
                 return true;
             }
             return false;
         }
         return false;
-    }
+    };
 
     onLinkMenuOpen = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
-            if (doubleTap) {
-                DocumentView.showBackLinks(this.props.View.rootDoc);
-            }
-        }), undefined, undefined,
-            action(() => DocumentLinksButton.LinkEditorDocView = this.props.View));
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            this.onLinkButtonMoved,
+            emptyFunction,
+            action((e, doubleTap) => {
+                if (doubleTap) {
+                    DocumentView.showBackLinks(this.props.View.rootDoc);
+                }
+            }),
+            undefined,
+            undefined,
+            action(() => (DocumentLinksButton.LinkEditorDocView = this.props.View))
+        );
+    };
 
     @undoBatch
     onLinkButtonDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
-            if (doubleTap && this.props.InMenu && this.props.StartLink) {
-                //action(() => Doc.BrushDoc(this.props.View.Document));
-                if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
-                    DocumentLinksButton.StartLink = undefined;
-                    DocumentLinksButton.StartLinkView = undefined;
-                } else {
-                    DocumentLinksButton.StartLink = this.props.View.props.Document;
-                    DocumentLinksButton.StartLinkView = this.props.View;
+        setupMoveUpEvents(
+            this,
+            e,
+            this.onLinkButtonMoved,
+            emptyFunction,
+            action((e, doubleTap) => {
+                if (doubleTap && this.props.InMenu && this.props.StartLink) {
+                    //action(() => Doc.BrushDoc(this.props.View.Document));
+                    if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
+                        DocumentLinksButton.StartLink = undefined;
+                        DocumentLinksButton.StartLinkView = undefined;
+                    } else {
+                        DocumentLinksButton.StartLink = this.props.View.props.Document;
+                        DocumentLinksButton.StartLinkView = this.props.View;
+                    }
                 }
-            }
-        }));
-    }
+            })
+        );
+    };
 
-    @action @undoBatch
+    @action
+    @undoBatch
     onLinkClick = (e: React.MouseEvent): void => {
         if (this.props.InMenu && this.props.StartLink) {
             DocumentLinksButton.AnnotationId = undefined;
@@ -96,108 +114,125 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
-            } else { //if this LinkButton's Document is undefined
+            } else {
+                //if this LinkButton's Document is undefined
                 DocumentLinksButton.StartLink = this.props.View.props.Document;
                 DocumentLinksButton.StartLinkView = this.props.View;
             }
             //action(() => Doc.BrushDoc(this.props.View.Document));
         }
-    }
-
+    };
 
     completeLink = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action((e, doubleTap) => {
-            if (doubleTap && !this.props.StartLink) {
-                if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
-                    DocumentLinksButton.StartLink = undefined;
-                    DocumentLinksButton.StartLinkView = undefined;
-                    DocumentLinksButton.AnnotationId = undefined;
-                } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
-                    const sourceDoc = DocumentLinksButton.StartLink;
-                    const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document;
-                    const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "links"); //why is long drag here when this is used for completing links by clicking?
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            emptyFunction,
+            undoBatch(
+                action((e, doubleTap) => {
+                    if (doubleTap && !this.props.StartLink) {
+                        if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
+                            DocumentLinksButton.StartLink = undefined;
+                            DocumentLinksButton.StartLinkView = undefined;
+                            DocumentLinksButton.AnnotationId = undefined;
+                        } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
+                            const sourceDoc = DocumentLinksButton.StartLink;
+                            const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document;
+                            const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, 'links'); //why is long drag here when this is used for completing links by clicking?
 
-                    LinkManager.currentLink = linkDoc;
+                            LinkManager.currentLink = linkDoc;
 
-                    runInAction(() => {
-                        if (linkDoc) {
-                            TaskCompletionBox.textDisplayed = "Link Created";
-                            TaskCompletionBox.popupX = e.screenX;
-                            TaskCompletionBox.popupY = e.screenY - 133;
-                            TaskCompletionBox.taskCompleted = true;
+                            runInAction(() => {
+                                if (linkDoc) {
+                                    TaskCompletionBox.textDisplayed = 'Link Created';
+                                    TaskCompletionBox.popupX = e.screenX;
+                                    TaskCompletionBox.popupY = e.screenY - 133;
+                                    TaskCompletionBox.taskCompleted = true;
 
-                            LinkDescriptionPopup.popupX = e.screenX;
-                            LinkDescriptionPopup.popupY = e.screenY - 100;
-                            LinkDescriptionPopup.descriptionPopup = true;
+                                    LinkDescriptionPopup.popupX = e.screenX;
+                                    LinkDescriptionPopup.popupY = e.screenY - 100;
+                                    LinkDescriptionPopup.descriptionPopup = true;
 
-                            const rect = document.body.getBoundingClientRect();
-                            if (LinkDescriptionPopup.popupX + 200 > rect.width) {
-                                LinkDescriptionPopup.popupX -= 190;
-                                TaskCompletionBox.popupX -= 40;
-                            }
-                            if (LinkDescriptionPopup.popupY + 100 > rect.height) {
-                                LinkDescriptionPopup.popupY -= 40;
-                                TaskCompletionBox.popupY -= 40;
-                            }
+                                    const rect = document.body.getBoundingClientRect();
+                                    if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+                                        LinkDescriptionPopup.popupX -= 190;
+                                        TaskCompletionBox.popupX -= 40;
+                                    }
+                                    if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+                                        LinkDescriptionPopup.popupY -= 40;
+                                        TaskCompletionBox.popupY -= 40;
+                                    }
 
-                            setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
+                                    setTimeout(
+                                        action(() => (TaskCompletionBox.taskCompleted = false)),
+                                        2500
+                                    );
+                                }
+                            });
                         }
-                    });
-                }
-            }
-        })));
-    }
+                    }
+                })
+            )
+        );
+    };
 
-    public static finishLinkClick = undoBatch(action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView,) => {
-        if (startLink === endLink) {
-            DocumentLinksButton.StartLink = undefined;
-            DocumentLinksButton.StartLinkView = undefined;
-            DocumentLinksButton.AnnotationId = undefined;
-            DocumentLinksButton.AnnotationUri = undefined;
-            //!this.props.StartLink 
-        } else if (startLink !== endLink) {
-            endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
-            startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
-            const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink },
-                DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : undefined, undefined, undefined, true);
+    public static finishLinkClick = undoBatch(
+        action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView) => {
+            if (startLink === endLink) {
+                DocumentLinksButton.StartLink = undefined;
+                DocumentLinksButton.StartLinkView = undefined;
+                DocumentLinksButton.AnnotationId = undefined;
+                DocumentLinksButton.AnnotationUri = undefined;
+                //!this.props.StartLink
+            } else if (startLink !== endLink) {
+                endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
+                startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
+                const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined, undefined, undefined, true);
 
-            LinkManager.currentLink = linkDoc;
+                LinkManager.currentLink = linkDoc;
 
-            if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) { // if linking from a Hypothes.is annotation
-                Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
-                Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
-                Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
-                const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
-                Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId,
-                    (startIsAnnotation ? startLink : endLink)); // edit annotation to add a Dash hyperlink to the linked doc
-            }
+                if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) {
+                    // if linking from a Hypothes.is annotation
+                    Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
+                    Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
+                    Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
+                    const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
+                    Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, startIsAnnotation ? startLink : endLink); // edit annotation to add a Dash hyperlink to the linked doc
+                }
 
-            if (linkDoc) {
-                TaskCompletionBox.textDisplayed = "Link Created";
-                TaskCompletionBox.popupX = screenX;
-                TaskCompletionBox.popupY = screenY - 133;
-                TaskCompletionBox.taskCompleted = true;
+                if (linkDoc) {
+                    TaskCompletionBox.textDisplayed = 'Link Created';
+                    TaskCompletionBox.popupX = screenX;
+                    TaskCompletionBox.popupY = screenY - 133;
+                    TaskCompletionBox.taskCompleted = true;
 
-                if (LinkDescriptionPopup.showDescriptions === "ON" || !LinkDescriptionPopup.showDescriptions) {
-                    LinkDescriptionPopup.popupX = screenX;
-                    LinkDescriptionPopup.popupY = screenY - 100;
-                    LinkDescriptionPopup.descriptionPopup = true;
-                }
+                    if (LinkDescriptionPopup.showDescriptions === 'ON' || !LinkDescriptionPopup.showDescriptions) {
+                        LinkDescriptionPopup.popupX = screenX;
+                        LinkDescriptionPopup.popupY = screenY - 100;
+                        LinkDescriptionPopup.descriptionPopup = true;
+                    }
 
-                const rect = document.body.getBoundingClientRect();
-                if (LinkDescriptionPopup.popupX + 200 > rect.width) {
-                    LinkDescriptionPopup.popupX -= 190;
-                    TaskCompletionBox.popupX -= 40;
-                }
-                if (LinkDescriptionPopup.popupY + 100 > rect.height) {
-                    LinkDescriptionPopup.popupY -= 40;
-                    TaskCompletionBox.popupY -= 40;
-                }
+                    const rect = document.body.getBoundingClientRect();
+                    if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+                        LinkDescriptionPopup.popupX -= 190;
+                        TaskCompletionBox.popupX -= 40;
+                    }
+                    if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+                        LinkDescriptionPopup.popupY -= 40;
+                        TaskCompletionBox.popupY -= 40;
+                    }
 
-                setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
+                    setTimeout(
+                        action(() => {
+                            TaskCompletionBox.taskCompleted = false;
+                        }),
+                        2500
+                    );
+                }
             }
-        }
-    }));
+        })
+    );
 
     @action clearLinks() {
         DocumentLinksButton.StartLink = undefined;
@@ -208,9 +243,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         const results = [] as Doc[];
         const filters = this.props.View.props.docFilters();
         Array.from(new Set<Doc>(this.props.View.allLinks)).forEach(link => {
-            if (DocUtils.FilterDocs([link], filters, []).length ||
-                DocUtils.FilterDocs([link.anchor2 as Doc], filters, []).length ||
-                DocUtils.FilterDocs([link.anchor1 as Doc], filters, []).length) {
+            if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.anchor2 as Doc], filters, []).length || DocUtils.FilterDocs([link.anchor1 as Doc], filters, []).length) {
                 results.push(link);
             }
         });
@@ -219,48 +252,45 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
 
     /**
      * gets the JSX of the link button (btn used to start/complete links) OR the link-view button (btn on bottom left of each linked node)
-     * 
+     *
      * todo:glr / anh seperate functionality such as onClick onPointerDown of link menu button
      */
     @computed get linkButtonInner() {
-        const btnDim = "30px";
-        const link = <img style={{ width: "22px", height: "16px" }} src={`/assets/${"link.png"}`} />;
-        const isActive = (DocumentLinksButton.StartLink === this.props.View.props.Document) && this.props.StartLink;
-        return (!this.props.InMenu ?
-            <div className="documentLinksButton-cont"
-                style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}
-            >
-                <div className={"documentLinksButton"}
-                    onPointerDown={this.onLinkMenuOpen} onClick={this.onLinkClick}
+        const btnDim = '30px';
+        const link = <img style={{ width: '22px', height: '16px' }} src={`/assets/${'link.png'}`} />;
+        const isActive = DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.StartLink;
+        return !this.props.InMenu ? (
+            <div className="documentLinksButton-cont" style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}>
+                <div
+                    className={'documentLinksButton'}
+                    onPointerDown={this.onLinkMenuOpen}
+                    onClick={this.onLinkClick}
                     style={{
                         backgroundColor: Colors.LIGHT_BLUE,
                         color: Colors.BLACK,
-                        fontSize: "20px",
+                        fontSize: '20px',
                         width: btnDim,
                         height: btnDim,
                     }}>
                     {Array.from(this.filteredLinks).length}
                 </div>
             </div>
-            :
-            <div className="documentLinksButton-menu" >
-                {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? //if the origin node is not this node
-                    <div className={"documentLinksButton-endLink"} ref={this._linkButton}
+        ) : (
+            <div className="documentLinksButton-menu">
+                {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? ( //if the origin node is not this node
+                    <div
+                        className={'documentLinksButton-endLink'}
+                        ref={this._linkButton}
                         onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
                         onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}>
                         <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                     </div>
-                    : (null)
-                }
-                {
-                    this.props.InMenu && this.props.StartLink ? //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
-                        <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton}
-                            onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
-                        </div>
-                        :
-                        (null)
-                }
+                ) : null}
+                {this.props.InMenu && this.props.StartLink ? ( //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
+                    <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton} onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
+                        <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
+                    </div>
+                ) : null}
             </div>
         );
     }
@@ -268,25 +298,23 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     render() {
         TraceMobx();
 
-        const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link";
-        const buttonTitle = "Tap to view links; double tap to open link collection";
+        const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
+        const buttonTitle = 'Tap to view links; double tap to open link collection';
         const title = this.props.InMenu ? menuTitle : buttonTitle;
 
         //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
-        return (!Array.from(this.filteredLinks).length && !this.props.AlwaysOn) ? (null) :
-            <div className="documentLinksButton-wrapper"
+        return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? null : (
+            <div
+                className="documentLinksButton-wrapper"
                 style={{
-                    transform: this.props.InMenu ? undefined :
-                        `scale(${(this.props.ContentScaling?.() || 1) * this.props.View.screenToLocalTransform().Scale})`
-                }} >
-                {
-                    (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) ||
-                        (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ?
-                        <Tooltip title={<div className="dash-tooltip">{title}</div>}>
-                            {this.linkButtonInner}
-                        </Tooltip>
-                        : this.linkButtonInner
-                }
-            </div>;
+                    transform: this.props.InMenu ? undefined : `scale(${this.props.scaling})`,
+                }}>
+                {(this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) || (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ? (
+                    <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>
+                ) : (
+                    this.linkButtonInner
+                )}
+            </div>
+        );
     }
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3a8552325..6bf0c365a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -175,7 +175,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     LayoutTemplateString?: string;
     dontCenter?: 'x' | 'y' | 'xy';
     dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling
-    ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
     NativeWidth?: () => number;
     NativeHeight?: () => number;
     LayoutTemplate?: () => Opt<Doc>;
@@ -192,6 +191,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
 export interface DocumentViewInternalProps extends DocumentViewProps {
     NativeWidth: () => number;
     NativeHeight: () => number;
+    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to FieldViewProps
     isSelected: (outsideReaction?: boolean) => boolean;
     select: (ctrlPressed: boolean) => void;
     DocumentView: () => DocumentView;
@@ -235,8 +235,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get ShowTitle() {
         return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
     }
-    @computed get ContentScale() {
-        return this.props.ContentScaling?.() || 1;
+    @computed get NativeDimScaling() {
+        return this.props.NativeDimScaling?.() || 1;
     }
     @computed get thumb() {
         return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
@@ -428,7 +428,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 let nheight = Doc.NativeHeight(layoutDoc);
                 const width = layoutDoc._width || 0;
                 const height = layoutDoc._height || (nheight / nwidth) * width;
-                const scale = this.props.ScreenToLocalTransform().Scale * this.ContentScale;
+                const scale = this.props.ScreenToLocalTransform().Scale * this.NativeDimScaling;
                 const actualdW = Math.max(width + dW * scale, 20);
                 const actualdH = Math.max(height + dH * scale, 20);
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
@@ -491,10 +491,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
         if (this._mainCont.current) {
             const dragData = new DragManager.DocumentDragData([this.props.Document]);
-            const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
+            const [left, top] = this.props.ScreenToLocalTransform().scale(this.NativeDimScaling).inverse().transformPoint(0, 0);
             dragData.offset = this.props
                 .ScreenToLocalTransform()
-                .scale(this.ContentScale)
+                .scale(this.NativeDimScaling)
                 .transformDirection(x - left, y - top);
             dragData.offset[0] = Math.min(this.rootDoc[WidthSym](), dragData.offset[0]);
             dragData.offset[1] = Math.min(this.rootDoc[HeightSym](), dragData.offset[1]);
@@ -961,7 +961,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
-    contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
     setHeight = (height: number) => (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
@@ -989,6 +988,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             ? true
             : false;
     };
+    linkButtonInverseScaling = () => (this.props.NativeDimScaling?.() || 1) * this.props.DocumentView().screenToLocalTransform().Scale;
     @computed get contents() {
         TraceMobx();
         const audioView = !this.layoutDoc._showAudio ? null : (
@@ -1041,7 +1041,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     thumbShown={this.thumbShown}
                     isHovering={this.isHovering}
                     setContentView={this.setContentView}
-                    scaling={this.contentScaling}
+                    NativeDimScaling={this.props.NativeDimScaling}
                     PanelHeight={this.panelHeight}
                     setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
                     isContentActive={this.isContentActive}
@@ -1055,7 +1055,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? null : (
                     <DocumentLinksButton
                         View={this.props.DocumentView()}
-                        ContentScaling={this.props.ContentScaling}
+                        scaling={this.linkButtonInverseScaling}
                         Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -30]}
                     />
                 )}
@@ -1503,7 +1503,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         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());
+    toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
     focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options);
     getBounds = () => {
         if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
@@ -1564,7 +1564,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     NativeHeight = () => this.effectiveNativeHeight;
     PanelWidth = () => this.panelWidth;
     PanelHeight = () => this.panelHeight;
-    ContentScale = () => this.nativeScaling;
+    NativeDimScaling = () => this.nativeScaling;
     selfView = () => this;
     screenToLocalTransform = () =>
         this.props
@@ -1620,9 +1620,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             PanelHeight={this.PanelHeight}
                             NativeWidth={this.NativeWidth}
                             NativeHeight={this.NativeHeight}
+                            NativeDimScaling={this.NativeDimScaling}
                             isSelected={this.isSelected}
                             select={this.select}
-                            ContentScaling={this.ContentScale}
                             ScreenToLocalTransform={this.screenToLocalTransform}
                             focus={this.props.focus || emptyFunction}
                             ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index c170f9867..a714518cc 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -12,11 +12,12 @@ import { LightboxView } from '../LightboxView';
 import './EquationBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
 
-
 @observer
 export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); }
-    public static SelectOnLoad: string = "";
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(EquationBox, fieldKey);
+    }
+    public static SelectOnLoad: string = '';
     _ref: React.RefObject<EquationEditor> = React.createRef();
     componentDidMount() {
         if (EquationBox.SelectOnLoad === this.rootDoc[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
@@ -25,75 +26,82 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this._ref.current!.mathField.focus();
             this._ref.current!.mathField.select();
         }
-        reaction(() => StrCast(this.dataDoc.text),
+        reaction(
+            () => StrCast(this.dataDoc.text),
             text => {
                 if (text && text !== this._ref.current!.mathField.latex()) {
                     this._ref.current!.mathField.latex(text);
                 }
-            });
-        reaction(() => this.props.isSelected(),
+            }
+        );
+        reaction(
+            () => this.props.isSelected(),
             selected => {
                 if (this._ref.current) {
-                    if (selected) this._ref.current.element.current.children[0].addEventListener("keydown", this.keyPressed, true);
-                    else this._ref.current.element.current.children[0].removeEventListener("keydown", this.keyPressed);
+                    if (selected) this._ref.current.element.current.children[0].addEventListener('keydown', this.keyPressed, true);
+                    else this._ref.current.element.current.children[0].removeEventListener('keydown', this.keyPressed);
                 }
-            }, { fireImmediately: true });
+            },
+            { fireImmediately: true }
+        );
     }
     plot: any;
     @action
     keyPressed = (e: KeyboardEvent) => {
-        const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace("px", ""));
-        const _width = Number(getComputedStyle(this._ref.current!.element.current).width.replace("px", ""));
-        if (e.key === "Enter") {
+        const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace('px', ''));
+        const _width = Number(getComputedStyle(this._ref.current!.element.current).width.replace('px', ''));
+        if (e.key === 'Enter') {
             const nextEq = Docs.Create.EquationDocument({
-                title: "# math", text: StrCast(this.dataDoc.text), _width, _height: 25,
-                x: NumCast(this.layoutDoc.x), y: NumCast(this.layoutDoc.y) + _height + 10
+                title: '# math',
+                text: StrCast(this.dataDoc.text),
+                _width,
+                _height: 25,
+                x: NumCast(this.layoutDoc.x),
+                y: NumCast(this.layoutDoc.y) + _height + 10,
             });
             EquationBox.SelectOnLoad = nextEq[Id];
             this.props.addDocument?.(nextEq);
             e.stopPropagation();
-
         }
-        if (e.key === "Tab") {
+        if (e.key === 'Tab') {
             const graph = Docs.Create.FunctionPlotDocument([this.rootDoc], {
                 x: NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym](),
                 y: NumCast(this.layoutDoc.y),
-                _width: 400, _height: 300, backgroundColor: "white"
+                _width: 400,
+                _height: 300,
+                backgroundColor: 'white',
             });
             this.props.addDocument?.(graph);
             e.stopPropagation();
         }
-        if (e.key === "Backspace" && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
-    }
+        if (e.key === 'Backspace' && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
+    };
     onChange = (str: string) => {
         this.dataDoc.text = str;
         const style = this._ref.current && getComputedStyle(this._ref.current.element.current);
         if (style) {
-            const _height = Number(style.height.replace("px", ""));
-            const _width = Number(style.width.replace("px", ""));
+            const _height = Number(style.height.replace('px', ''));
+            const _width = Number(style.width.replace('px', ''));
             this.layoutDoc._width = Math.max(35, _width);
             this.layoutDoc._height = Math.max(25, _height);
         }
-    }
+    };
     render() {
         TraceMobx();
-        const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
-        return (<div className="equationBox-cont"
-            onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
-            style={{
-                transform: `scale(${scale})`,
-                width: `${100 / scale}%`,
-                height: `${100 / scale}%`,
-                pointerEvents: !this.props.isSelected() ? "none" : undefined,
-            }}
-            onKeyDown={e => e.stopPropagation()}
-        >
-            <EquationEditor ref={this._ref}
-                value={this.dataDoc.text || "x"}
-                spaceBehavesLikeTab={true}
-                onChange={this.onChange}
-                autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
-                autoOperatorNames="sin cos tan" />
-        </div>);
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        return (
+            <div
+                className="equationBox-cont"
+                onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
+                style={{
+                    transform: `scale(${scale})`,
+                    width: `${100 / scale}%`,
+                    height: `${100 / scale}%`,
+                    pointerEvents: !this.props.isSelected() ? 'none' : undefined,
+                }}
+                onKeyDown={e => e.stopPropagation()}>
+                <EquationEditor ref={this._ref} value={this.dataDoc.text || 'x'} spaceBehavesLikeTab={true} onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 67cf18d8b..5a6c49809 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,18 +1,18 @@
-import React = require("react");
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { DateField } from "../../../fields/DateField";
-import { Doc, Field, FieldResult, Opt } from "../../../fields/Doc";
-import { List } from "../../../fields/List";
-import { WebField } from "../../../fields/URLField";
-import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
-import { ScriptField } from "../../../fields/ScriptField";
-import { RecordingBox } from "./RecordingBox";
+import React = require('react');
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { DateField } from '../../../fields/DateField';
+import { Doc, Field, FieldResult, Opt } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { WebField } from '../../../fields/URLField';
+import { DocumentView, DocumentViewSharedProps } from './DocumentView';
+import { ScriptField } from '../../../fields/ScriptField';
+import { RecordingBox } from './RecordingBox';
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
 // However, that only happens because the properties are "defined" in the markup for the field view.
-// See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc. 
+// See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc.
 //
 export interface FieldViewProps extends DocumentViewSharedProps {
     // FieldView specific props that are not part of DocumentView props
@@ -23,10 +23,10 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     isContentActive: (outsideReaction?: boolean) => boolean | undefined;
     isDocumentActive?: () => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
-    scaling?: () => number;
     setHeight?: (height: number) => void;
-    onBrowseClick?: () => (ScriptField | undefined);
-    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined);
+    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps
+    onBrowseClick?: () => ScriptField | undefined;
+    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     pointerEvents?: () => Opt<string>;
@@ -42,7 +42,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldType: { name: string }, fieldStr: string) {
-        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`;  //e.g., "<ImageBox {...props} fieldKey={"data} />"
+        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
     }
 
     @computed
@@ -75,23 +75,22 @@ export class FieldView extends React.Component<FieldViewProps> {
         //}
         else if (field instanceof DateField) {
             return <p>{field.date.toLocaleString()}</p>;
-        }
-        else if (field instanceof Doc) {
-            return <p><b>{field.title?.toString()}</b></p>;
-        }
-        else if (field instanceof List) {
-            return <div> {field.length ? field.map(f => Field.toString(f)).join(", ") : ""}  </div>;
+        } else if (field instanceof Doc) {
+            return (
+                <p>
+                    <b>{field.title?.toString()}</b>
+                </p>
+            );
+        } else if (field instanceof List) {
+            return <div> {field.length ? field.map(f => Field.toString(f)).join(', ') : ''} </div>;
         }
         // bcz: this belongs here, but it doesn't render well so taking it out for now
         else if (field instanceof WebField) {
             return <p>{Field.toString(field.url.href)}</p>;
-        }
-        else if (!(field instanceof Promise)) {
+        } else if (!(field instanceof Promise)) {
             return <p>{Field.toString(field)}</p>;
-        }
-        else {
-            return <p> {"Waiting for server..."} </p>;
+        } else {
+            return <p> {'Waiting for server...'} </p>;
         }
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ffa839fcb..9590bcb15 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -148,8 +148,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
-        cropping._width = anchw * (this.props.scaling?.() || 1);
-        cropping._height = anchh * (this.props.scaling?.() || 1);
+        cropping._width = anchw * (this.props.NativeDimScaling?.() || 1);
+        cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
         cropping.isLinkButton = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
@@ -384,7 +384,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     render() {
         TraceMobx();
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
-        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
+        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.NativeDimScaling?.() || 1)}px` : borderRad;
         return (
             <div
                 className="imageBox"
@@ -407,7 +407,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     PanelHeight={this.props.PanelHeight}
                     ScreenToLocalTransform={this.screenToLocalTransform}
                     select={emptyFunction}
-                    scaling={returnOne}
+                    NativeDimScaling={returnOne}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                     removeDocument={this.removeDocument}
                     moveDocument={this.moveDocument}
@@ -420,7 +420,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         rootDoc={this.rootDoc}
                         scrollTop={0}
                         down={this._marqueeing}
-                        scaling={this.props.scaling}
+                        scaling={this.props.NativeDimScaling}
                         docView={this.props.docViewPath().slice(-1)[0]}
                         addDocument={this.addDocument}
                         finishMarquee={this.finishMarquee}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c4b42301e..e9ed594d3 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -373,7 +373,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 runInAction(() => {
                     const localDelta = this.props
                         .ScreenToLocalTransform()
-                        .scale(this.props.scaling?.() || 1)
+                        .scale(this.props.NativeDimScaling?.() || 1)
                         .transformDirection(delta[0], delta[1]);
                     const fullWidth = this.layoutDoc[WidthSym]();
                     const mapWidth = fullWidth - this.sidebarWidth();
@@ -559,8 +559,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // }
     };
 
-    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
-    panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
@@ -589,13 +589,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     docFilters={docFilters || this.props.docFilters}
         //     dontRenderDocuments={docFilters ? false : true}
         //     select={emptyFunction}
-        //     ContentScaling={returnOne}
         //     bringToFront={emptyFunction}
         //     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
         //     removeDocument={this.removeDocument}
         //     moveDocument={this.moveDocument}
         //     addDocument={this.sidebarAddDocument}
-        //     childPointerEvents={true}
+        //     childPointerEvents={"all"}
         //     pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="mapBox" ref={this._ref}>
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 72569135b..630ae18f5 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -62,7 +62,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
                             setHeight={emptyFunction}
                             isAnnotationOverlay={false}
                             select={emptyFunction}
-                            scaling={returnOne}
+                            NativeDimScaling={returnOne}
                             isContentActive={returnTrue}
                             chromeHidden={true}
                             rootSelected={returnFalse}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index fe18abba9..fad22d6e9 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -104,8 +104,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         const anchx = NumCast(cropping.x);
         const anchy = NumCast(cropping.y);
-        const anchw = cropping[WidthSym]() * (this.props.scaling?.() || 1);
-        const anchh = cropping[HeightSym]() * (this.props.scaling?.() || 1);
+        const anchw = cropping[WidthSym]() * (this.props.NativeDimScaling?.() || 1);
+        const anchh = cropping[HeightSym]() * (this.props.NativeDimScaling?.() || 1);
         const viewScale = 1;
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
@@ -290,11 +290,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             (e, down, delta) => {
                 const localDelta = this.props
                     .ScreenToLocalTransform()
-                    .scale(this.props.scaling?.() || 1)
+                    .scale(this.props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
                 const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
                 const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / nativeWidth;
+                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
@@ -433,12 +433,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
     }
 
-    contentScaling = () => 1;
     isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected();
     @computed get renderPdfView() {
         TraceMobx();
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
-        const scale = previewScale * (this.props.scaling?.() || 1);
+        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
         return (
             <div
                 className={'pdfBox'}
@@ -473,7 +472,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         removeDocument={this.removeDocument}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                         crop={this.crop}
-                        ContentScaling={returnOne}
                     />
                 </div>
                 <SidebarAnnos
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 942072524..76a24d831 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -295,7 +295,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                             isAnnotationOverlay={true}
                             select={emptyFunction}
                             isContentActive={returnFalse}
-                            scaling={returnOne}
+                            NativeDimScaling={returnOne}
                             whenChildContentsActiveChanged={emptyFunction}
                             removeDocument={returnFalse}
                             moveDocument={returnFalse}
@@ -317,7 +317,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                                 isAnnotationOverlay={true}
                                 select={emptyFunction}
                                 isContentActive={emptyFunction}
-                                scaling={returnOne}
+                                NativeDimScaling={returnOne}
                                 xPadding={25}
                                 yPadding={10}
                                 whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b1f7d8023..681f7c5b2 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -898,7 +898,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
 
-    scaling = () => this.props.scaling?.() || 1;
+    scaling = () => this.props.NativeDimScaling?.() || 1;
 
     panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100;
     panelHeight = () => (this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
@@ -911,7 +911,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             .scale(100 / this.heightPercent);
     };
 
-    marqueeFitScaling = () => ((this.props.scaling?.() || 1) * this.heightPercent) / 100;
+    marqueeFitScaling = () => ((this.props.NativeDimScaling?.() || 1) * this.heightPercent) / 100;
     marqueeOffset = () => [((this.panelWidth() / 2) * (1 - this.heightPercent / 100)) / (this.heightPercent / 100), 0];
 
     timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
@@ -1124,7 +1124,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                             ScreenToLocalTransform={this.screenToLocalTransform}
                             docFilters={this.timelineDocFilter}
                             select={emptyFunction}
-                            scaling={returnOne}
+                            NativeDimScaling={returnOne}
                             whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                             removeDocument={this.removeDocument}
                             moveDocument={this.moveDocument}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 05da8bd7b..71d6959a3 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -200,7 +200,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             autoHeight => {
                 if (autoHeight) {
                     this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -276,7 +276,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         if (doc !== this.rootDoc && this._outerRef.current) {
-            const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+            const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
             const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
             if (scrollTo !== undefined && this._initialScroll === undefined) {
                 const focusSpeed = smooth ? 500 : 0;
@@ -310,7 +310,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
         if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
             const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
-            const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
+            const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
@@ -322,7 +322,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     iframeDown = (e: PointerEvent) => {
         const sel = this._iframe?.contentWindow?.getSelection?.();
         const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
-        const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
+        const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
         this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
@@ -604,7 +604,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             funcs.push({
                 description: (!this.layoutDoc.forceReflow ? 'Force' : 'Prevent') + ' Reflow',
                 event: () => {
-                    const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
+                    const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
                     this.layoutDoc.forceReflow = !nw;
                     if (nw) {
                         Doc.SetInPlace(this.layoutDoc, this.fieldKey + '-nativeWidth', nw, true);
@@ -717,12 +717,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._draggingSidebar = true;
                 const localDelta = this.props
                     .ScreenToLocalTransform()
-                    .scale(this.props.scaling?.() || 1)
+                    .scale(this.props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
                 const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
                 const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight']);
                 const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / nativeWidth;
+                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
@@ -834,8 +834,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
     showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
-    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
-    panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
@@ -852,7 +852,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     render() {
         const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
-        const scale = previewScale * (this.props.scaling?.() || 1);
+        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
         const renderAnnotations = (docFilters?: () => string[]) => (
             <CollectionFreeFormView
                 {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
@@ -864,12 +864,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 PanelWidth={this.panelWidth}
                 PanelHeight={this.panelHeight}
                 ScreenToLocalTransform={this.scrollXf}
-                scaling={returnOne}
+                NativeDimScaling={returnOne}
                 dropAction={'alias'}
                 docFilters={docFilters || this.basicFilter}
                 dontRenderDocuments={docFilters ? false : true}
                 select={emptyFunction}
-                ContentScaling={returnOne}
                 bringToFront={emptyFunction}
                 whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                 removeDocument={this.removeDocument}
@@ -950,7 +949,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
                     setHeight={emptyFunction}
-                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.scaling?.() || 1)}
+                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.NativeDimScaling?.() || 1)}
                     showSidebar={this.SidebarShown}
                     sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 383c22ba8..e9b048c5e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -968,7 +968,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     @computed get contentScaling() {
-        return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.scaling?.() || 1 : 1;
+        return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.NativeDimScaling?.() || 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.
@@ -1691,7 +1691,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     };
     fitContentsToBox = () => this.props.Document._fitContentsToBox;
-    sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+    sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         // console.log("printting allSideBarDocs");
@@ -1705,7 +1705,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     sidebarScreenToLocal = () =>
         this.props
             .ScreenToLocalTransform()
-            .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.scaling?.() || 1), 0)
+            .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.NativeDimScaling?.() || 1), 0)
             .scale(1 / NumCast(this.layoutDoc._viewScale, 1));
 
     @computed get audioHandle() {
@@ -1768,7 +1768,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     scaleField={this.SidebarKey + '-scale'}
                     isAnnotationOverlay={false}
                     select={emptyFunction}
-                    scaling={this.sidebarContentScaling}
+                    NativeDimScaling={this.sidebarContentScaling}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                     removeDocument={this.sidebarRemDocument}
                     moveDocument={this.sidebarMoveDocument}
@@ -1793,7 +1793,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         TraceMobx();
         const active = this.props.isContentActive() || this.props.isSelected();
         const selected = active;
-        const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
         const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index bd5e8caab..0cf15d297 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -199,11 +199,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    headerUp = (e: React.PointerEvent<HTMLDivElement>) => {
-        e.stopPropagation();
-        e.preventDefault();
-    };
-
     /**
      * Function to drag and drop the pres element to a diferent location
      */
@@ -477,8 +472,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 })}
                 onPointerOver={this.onPointerOver}
                 onPointerLeave={this.onPointerLeave}
-                onPointerDown={this.headerDown}
-                onPointerUp={this.headerUp}>
+                onPointerDown={this.headerDown}>
                 {/* {miniView ?
                               // when width is LESS than 110 px 
                               <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 837734edf..2c83082b7 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -41,7 +41,6 @@ interface IViewerProps extends FieldViewProps {
     url: string;
     loaded?: (nw: number, nh: number, np: number) => void;
     setPdfViewer: (view: PDFViewer) => void;
-    ContentScaling?: () => number;
     anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
     crop: (region: Doc | undefined, addCrop?: boolean) => Doc | undefined;
 }
@@ -97,7 +96,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             autoHeight => {
                 if (autoHeight) {
                     this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -162,7 +161,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         const mainCont = this._mainCont.current;
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
-            const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+            const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
             const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight));
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
                 focusSpeed = 500;
@@ -486,8 +485,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
     overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1));
-    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
-    panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
@@ -509,7 +508,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
             PanelWidth={this.panelWidth}
             dropAction={'alias'}
             select={emptyFunction}
-            ContentScaling={this.contentZoom}
             bringToFront={emptyFunction}
             docFilters={docFilters || this.basicFilter}
             styleProvider={this.childStyleProvider}
@@ -556,10 +554,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @computed get pdfViewerDiv() {
         return <div className={'pdfViewerDash-text' + (this.props.pointerEvents?.() !== 'none' && this._textSelecting && this.props.isContentActive() ? '-selected' : '')} ref={this._viewer} />;
     }
-    @computed get contentScaling() {
-        return this.props.ContentScaling?.() || 1;
-    }
-    contentZoom = () => NumCast(this.props.layoutDoc._viewScale, 1);
     savedAnnotations = () => this._savedAnnotations;
     render() {
         TraceMobx();
@@ -574,8 +568,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     onClick={this.onClick}
                     style={{
                         overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? 'scroll' : undefined,
-                        height: !this.props.Document._fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`,
-                        transform: `scale(${this.contentScaling})`,
+                        height: !this.props.Document._fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `100%`,
                     }}>
                     {this.pdfViewerDiv}
                     {this.annotationLayer}
-- 
cgit v1.2.3-70-g09d2

