From 3817e7e380031a60b0d5cfe31bc350ca8f4f6056 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 14 Nov 2023 13:34:04 -0500
Subject: simplified goldenlayout tab buttons and made open in lightbox more
 accessible for a tab.

---
 src/client/util/CaptureManager.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/util/CaptureManager.tsx')

diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index f42336ee7..8a4f37121 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -73,7 +73,7 @@ export class CaptureManager extends React.Component<{}> {
                     <div
                         className="save"
                         onClick={() => {
-                            LightboxView.SetLightboxDoc(this._document);
+                            LightboxView.Instance.SetLightboxDoc(this._document);
                             this.close();
                         }}>
                         Save
-- 
cgit v1.2.3-70-g09d2


From 85c09a9e5df77ad00f3e00a2fb1e0f2e3449f97d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 12 Dec 2023 14:39:09 -0500
Subject: cleaned up SelectionManager. fixed schema view

---
 src/client/util/CaptureManager.tsx                 |   2 +-
 src/client/util/DictationManager.ts                |   2 +-
 src/client/util/DragManager.ts                     |   4 +-
 src/client/util/HypothesisUtils.ts                 |   2 +-
 src/client/util/SelectionManager.ts                | 124 +++++++++------------
 src/client/util/SharingManager.tsx                 |   8 +-
 src/client/util/SnappingManager.ts                 |  28 ++---
 src/client/views/ComponentDecorations.tsx          |   2 +-
 src/client/views/DocumentButtonBar.tsx             |   6 +-
 src/client/views/DocumentDecorations.tsx           |  80 ++++++-------
 src/client/views/GlobalKeyHandler.ts               |  48 +++-----
 src/client/views/InkControlPtHandles.tsx           |   4 +-
 src/client/views/MainView.tsx                      |   6 +-
 src/client/views/PropertiesButtons.tsx             |  22 ++--
 .../views/PropertiesDocBacklinksSelector.tsx       |   4 +-
 src/client/views/PropertiesView.tsx                |  22 ++--
 src/client/views/collections/CollectionMenu.tsx    |   2 +-
 src/client/views/collections/CollectionSubView.tsx |   4 +-
 .../views/collections/CollectionTreeView.tsx       |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  12 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   2 +-
 .../collectionSchema/CollectionSchemaView.tsx      | 116 ++++++++++---------
 .../collections/collectionSchema/SchemaRowBox.tsx  |  38 ++++---
 src/client/views/global/globalScripts.ts           |  40 ++++---
 src/client/views/nodes/DocumentView.tsx            |   8 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   2 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |   4 +-
 src/client/views/nodes/MapBox/MapAnchorMenu.tsx    |   2 +-
 src/client/views/nodes/MapBox/MapBox2.tsx          |   4 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |  16 +--
 src/client/views/nodes/trails/PresBox.tsx          |   4 +-
 src/client/views/pdf/AnchorMenu.tsx                |   4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 33 files changed, 308 insertions(+), 318 deletions(-)

(limited to 'src/client/util/CaptureManager.tsx')

diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index 8a4f37121..271cf7495 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -81,7 +81,7 @@ export class CaptureManager extends React.Component<{}> {
                     <div
                         className="cancel"
                         onClick={() => {
-                            const selected = SelectionManager.Views().slice();
+                            const selected = SelectionManager.Views.slice();
                             SelectionManager.DeselectAll();
                             selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
                             this.close();
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 0fd7e840c..039bb360e 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -236,7 +236,7 @@ export namespace DictationManager {
         export const execute = async (phrase: string) => {
             return UndoManager.RunInBatch(async () => {
                 console.log('PHRASE: ' + phrase);
-                const targets = SelectionManager.Views();
+                const targets = SelectionManager.Views;
                 if (!targets || !targets.length) {
                     return;
                 }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e96bbcaaf..c711db31a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -618,7 +618,7 @@ export namespace DragManager {
 
 ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) {
     if (readOnly) {
-        return SelectionManager.Views().some(dv => dv.Document.keepZWhenDragged);
+        return SelectionManager.Views.some(dv => dv.Document.keepZWhenDragged);
     }
-    SelectionManager.Views().map(dv => (dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged));
+    SelectionManager.Views.map(dv => (dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged));
 });
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 151f18d6f..990798ed3 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -27,7 +27,7 @@ export namespace Hypothesis {
      * Search for a WebDocument whose url field matches the given uri, return undefined if not found
      */
     export const findWebDoc = async (uri: string) => {
-        const currentDoc = SelectionManager.Docs().lastElement();
+        const currentDoc = SelectionManager.Docs.lastElement();
         if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
 
         const results: Doc[] = [];
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index b837cdd08..64cced3f1 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -10,90 +10,72 @@ import { LinkManager } from './LinkManager';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { UndoManager } from './UndoManager';
 
-export namespace SelectionManager {
-    class Manager {
-        @observable.shallow SelectedViews: DocumentView[] = [];
-        @observable IsDragging: boolean = false;
-        @observable SelectedSchemaDocument: Doc | undefined = undefined;
-
-        constructor() {
-            makeObservable(this);
-        }
-
-        @action
-        SelectSchemaViewDoc(doc: Opt<Doc>) {
-            manager.SelectedSchemaDocument = doc;
-        }
-        @action
-        SelectView(docView: DocumentView, extendSelection: boolean): void {
-            if (!docView.SELECTED) {
-                if (!extendSelection) this.DeselectAll();
-                manager.SelectedViews.push(docView);
-                docView.SELECTED = true;
-                docView._props.whenChildContentsActiveChanged(true);
-            }
-        }
-        @action
-        DeselectView(docView?: DocumentView): void {
-            if (docView && manager.SelectedViews.includes(docView)) {
-                docView.SELECTED = false;
-                manager.SelectedViews.splice(manager.SelectedViews.indexOf(docView), 1);
-                docView._props.whenChildContentsActiveChanged(false);
-            }
-        }
-        @action
-        DeselectAll(): void {
-            LinkManager.currentLink = undefined;
-            LinkManager.currentLinkAnchor = undefined;
-            manager.SelectedSchemaDocument = undefined;
-            manager.SelectedViews.forEach(dv => {
-                dv.SELECTED = false;
-                dv._props.whenChildContentsActiveChanged(false);
-            });
-            manager.SelectedViews.length = 0;
-        }
+export class SelectionManager {
+    private static _manager: SelectionManager;
+    private static get Instance() {
+        return SelectionManager._manager ?? new SelectionManager();
     }
 
-    const manager = new Manager();
+    @observable.shallow SelectedViews: DocumentView[] = [];
+    @observable IsDragging: boolean = false;
+    @observable SelectedSchemaDocument: Doc | undefined = undefined;
 
-    export function DeselectView(docView?: DocumentView): void {
-        manager.DeselectView(docView);
-    }
-    export function SelectView(docView: DocumentView | undefined, ctrlPressed: boolean): void {
-        if (!docView) DeselectAll();
-        else manager.SelectView(docView, ctrlPressed);
-    }
-    export function SelectSchemaViewDoc(document: Opt<Doc>, deselectAllFirst?: boolean): void {
-        if (deselectAllFirst) manager.DeselectAll();
-        manager.SelectSchemaViewDoc(document);
+    private constructor() {
+        SelectionManager._manager = this;
+        makeObservable(this);
     }
 
-    export function IsSelected(doc?: Doc): boolean {
-        return Array.from(doc?.[DocViews] ?? []).some(dv => dv?.SELECTED);
+    @action
+    public static SelectSchemaViewDoc(doc: Opt<Doc>, deselectAllFirst?: boolean) {
+        if (deselectAllFirst) this.DeselectAll();
+        else this.Instance.SelectedSchemaDocument = doc;
     }
 
-    export function DeselectAll(except?: Doc): void {
-        const found = manager.SelectedViews.find(dv => dv.Document === except);
-        manager.DeselectAll();
-        if (found) manager.SelectView(found, false);
+    @action
+    public static SelectView(docView: DocumentView, extendSelection: boolean): void {
+        if (!docView) this.DeselectAll();
+        else if (!docView.SELECTED) {
+            if (!extendSelection) this.DeselectAll();
+            this.Instance.SelectedViews.push(docView);
+            docView.SELECTED = true;
+            docView._props.whenChildContentsActiveChanged(true);
+        }
     }
 
-    export function Views(): Array<DocumentView> {
-        return manager.SelectedViews;
-    }
-    export function SelectedSchemaDoc(): Doc | undefined {
-        return manager.SelectedSchemaDocument;
+    @action
+    public static DeselectView(docView?: DocumentView): void {
+        if (docView && this.Instance.SelectedViews.includes(docView)) {
+            docView.SELECTED = false;
+            this.Instance.SelectedViews.splice(this.Instance.SelectedViews.indexOf(docView), 1);
+            docView._props.whenChildContentsActiveChanged(false);
+        }
     }
-    export function Docs(): Doc[] {
-        return manager.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking);
+    @action
+    public static DeselectAll(except?: Doc): void {
+        const found = this.Instance.SelectedViews.find(dv => dv.Document === except);
+        LinkManager.currentLink = undefined;
+        LinkManager.currentLinkAnchor = undefined;
+        this.Instance.SelectedSchemaDocument = undefined;
+        this.Instance.SelectedViews.forEach(dv => {
+            dv.SELECTED = false;
+            dv._props.whenChildContentsActiveChanged(false);
+        });
+        this.Instance.SelectedViews.length = 0;
+        if (found) this.SelectView(found, false);
     }
+
+    public static IsSelected = (doc?: Doc) => Array.from(doc?.[DocViews] ?? []).some(dv => dv?.SELECTED);
+    public static get Views() { return this.Instance.SelectedViews; } // prettier-ignore
+    public static get SelectedSchemaDoc() { return this.Instance.SelectedSchemaDocument; } // prettier-ignore
+    public static get Docs() { return this.Instance.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } // prettier-ignore
 }
+
 ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) {
     if (Doc.noviceMode && expertMode) return false;
     if (type === 'tab') {
-        return SelectionManager.Views().lastElement()?._props.renderDepth === 0;
+        return SelectionManager.Views.lastElement()?._props.renderDepth === 0;
     }
-    let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
+    let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc ?? SelectionManager.Docs.lastElement());
     return selected?.type === type || selected?.type_collection === type || !type;
 });
 ScriptingGlobals.add(function deselectAll() {
@@ -118,8 +100,8 @@ ScriptingGlobals.add(function redo() {
     return UndoManager.Redo();
 });
 ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
-    const docs = SelectionManager.Views()
-        .map(dv => dv.Document)
-        .filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
+    const docs = SelectionManager.Views.map(dv => dv.Document).filter(
+        d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))
+    );
     return docs.length ? new List(docs) : prevValue;
 });
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index a46c6363e..7176b568e 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -162,7 +162,7 @@ export class SharingManager extends React.Component<{}> {
         const { user, sharingDoc } = recipient;
         const target = targetDoc || this.targetDoc!;
         const acl = `acl-${normalizeEmail(user.email)}`;
-        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.Document);
+        const docs = SelectionManager.Views.length < 2 ? [target] : SelectionManager.Views.map(docView => docView.Document);
         docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => {
             distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined);
             if (permission !== SharingPermissions.None) {
@@ -180,7 +180,7 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const acl = `acl-${normalizeEmail(StrCast(group.title))}`;
 
-        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.Document);
+        const docs = SelectionManager.Views.length < 2 ? [target] : SelectionManager.Views.map(docView => docView.Document);
         docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => {
             distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined);
 
@@ -317,7 +317,7 @@ export class SharingManager extends React.Component<{}> {
 
     private focusOn = (contents: string) => {
         const title = this.targetDoc ? StrCast(this.targetDoc.title) : '';
-        const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc];
+        const docs = SelectionManager.Views.length > 1 ? SelectionManager.Views.map(docView => docView.props.Document) : [this.targetDoc];
         return (
             <span
                 className="focus-span"
@@ -444,7 +444,7 @@ export class SharingManager extends React.Component<{}> {
         const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
         const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
 
-        let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.Document);
+        let docs = SelectionManager.Views.length < 2 ? [this.targetDoc] : SelectionManager.Views.map(docView => docView.Document);
 
         if (this.myDocAcls) {
             const newDocs: Doc[] = [];
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 44c6aad52..48ec61f85 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -2,7 +2,7 @@ import { observable, action, runInAction, reaction, makeObservable } from 'mobx'
 import { Doc } from '../../fields/Doc';
 
 export class SnappingManager {
-    static _manager: SnappingManager;
+    private static _manager: SnappingManager;
     private static get Instance() {
         return SnappingManager._manager ?? new SnappingManager();
     }
@@ -15,7 +15,7 @@ export class SnappingManager {
     @observable _horizSnapLines: number[] = [];
     @observable _vertSnapLines: number[] = [];
 
-    constructor() {
+    private constructor() {
         SnappingManager._manager = this;
         makeObservable(this);
     }
@@ -26,16 +26,16 @@ export class SnappingManager {
         this.Instance._vertSnapLines.push(...vertLines);
     };
 
-    public static get HorizSnapLines() { return SnappingManager.Instance._horizSnapLines; } // prettier-ignore
-    public static get VertSnapLines()  { return  SnappingManager.Instance._vertSnapLines; } // prettier-ignore
-    public static get ShiftKey()       { return SnappingManager.Instance._shiftKey; } // prettier-ignore
-    public static get CtrlKey()        { return SnappingManager.Instance._ctrlKey; } // prettier-ignore
-    public static get IsDragging()     { return SnappingManager.Instance._isDragging; } // prettier-ignore
-    public static get IsResizing()     { return SnappingManager.Instance._isResizing; } // prettier-ignore
-    public static get CanEmbed()       { return SnappingManager.Instance._canEmbed; } // prettier-ignore
-    public static SetShiftKey = (down: boolean) => runInAction(() => (SnappingManager.Instance._shiftKey = down));
-    public static SetCtrlKey = (down: boolean) => runInAction(() => (SnappingManager.Instance._ctrlKey = down));
-    public static SetIsDragging = (dragging: boolean) => runInAction(() => (SnappingManager.Instance._isDragging = dragging));
-    public static SetIsResizing = (doc: Doc | undefined) => runInAction(() => (SnappingManager.Instance._isResizing = doc));
-    public static SetCanEmbed = (canEmbed: boolean) => runInAction(() => (SnappingManager.Instance._canEmbed = canEmbed));
+    public static get HorizSnapLines() { return this.Instance._horizSnapLines; } // prettier-ignore
+    public static get VertSnapLines()  { return this.Instance._vertSnapLines; } // prettier-ignore
+    public static get ShiftKey()       { return this.Instance._shiftKey; } // prettier-ignore
+    public static get CtrlKey()        { return this.Instance._ctrlKey; } // prettier-ignore
+    public static get IsDragging()     { return this.Instance._isDragging; } // prettier-ignore
+    public static get IsResizing()     { return this.Instance._isResizing; } // prettier-ignore
+    public static get CanEmbed()       { return this.Instance._canEmbed; } // prettier-ignore
+    public static SetShiftKey = (down: boolean) => runInAction(() => (this.Instance._shiftKey = down));
+    public static SetCtrlKey = (down: boolean) => runInAction(() => (this.Instance._ctrlKey = down));
+    public static SetIsDragging = (dragging: boolean) => runInAction(() => (this.Instance._isDragging = dragging));
+    public static SetIsResizing = (doc: Doc | undefined) => runInAction(() => (this.Instance._isResizing = doc));
+    public static SetCanEmbed = (canEmbed: boolean) => runInAction(() => (this.Instance._canEmbed = canEmbed));
 }
diff --git a/src/client/views/ComponentDecorations.tsx b/src/client/views/ComponentDecorations.tsx
index 0a34b283b..ca4e5f2bc 100644
--- a/src/client/views/ComponentDecorations.tsx
+++ b/src/client/views/ComponentDecorations.tsx
@@ -8,7 +8,7 @@ export class ComponentDecorations extends React.Component<{ boundsTop: number; b
     static Instance: ComponentDecorations;
 
     render() {
-        const seldoc = SelectionManager.Views().lastElement();
+        const seldoc = SelectionManager.Views.lastElement();
         return seldoc?.ComponentView?.componentUI?.(this.props.boundsLeft, this.props.boundsTop) ?? null;
     }
 }
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 47ec0f1b4..a8ad4150e 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -375,7 +375,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             );
         };
         return !targetDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">{`Pin Document ${SelectionManager.Views().length > 1 ? 'multiple documents' : ''} to Trail`}</div>}>
+            <Tooltip title={<div className="dash-tooltip">{`Pin Document ${SelectionManager.Views.length > 1 ? 'multiple documents' : ''} to Trail`}</div>}>
                 <div
                     className="documentButtonBar-icon documentButtonBar-pin"
                     onClick={e => {
@@ -526,7 +526,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     }
 
     openContextMenu = (e: PointerEvent) => {
-        let child = SelectionManager.Views()[0].ContentDiv!.children[0];
+        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');
             if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
@@ -606,7 +606,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                     Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.templateButton}</div>
                     /*<div className="documentButtonBar-button"> {this.metadataButton} </div> */
                 }
-                {!SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
+                {!SelectionManager.Views?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
                 <div className="documentButtonBar-button">{this.pinButton}</div>
                 <div className="documentButtonBar-button">{this.recordButton}</div>
                 {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b93df36eb..8bcc1cb56 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -77,7 +77,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
                 const center = {x: (this.Bounds.x+this.Bounds.r)/2, y: (this.Bounds.y+this.Bounds.b)/2};
                 const {x,y} = Utils.rotPt(e.clientX - center.x, 
                                           e.clientY - center.y, 
-                                          NumCast(SelectionManager.Views().lastElement()?.screenToLocalTransform().Rotate));
+                                          NumCast(SelectionManager.Views.lastElement()?.screenToLocalTransform().Rotate));
                 (this._showNothing = !(this.Bounds.x !== Number.MAX_VALUE && //
                     (this.Bounds.x > center.x+x || this.Bounds.r < center.x+x || 
                      this.Bounds.y > center.y+y || this.Bounds.b < center.y+y )));
@@ -99,7 +99,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     @computed get Bounds() {
         return (LinkFollower.IsFollowing || DocumentView.ExploreMode) ? 
             { x: 0, y: 0, r: 0, b: 0 }
-            : SelectionManager.Views()
+            : SelectionManager.Views
                 .filter(dv => dv._props.renderDepth > 0)
                 .map(dv => dv.getBounds())
                 .reduce((bounds, rect) => !rect ? bounds
@@ -107,7 +107,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
                                 y: Math.min(rect.top, bounds.y),
                                 r: Math.max(rect.right, bounds.r),
                                 b: Math.max(rect.bottom, bounds.b),
-                                c: SelectionManager.Views().length === 1 ? rect.center : undefined },
+                                c: SelectionManager.Views.length === 1 ? rect.center : undefined },
                     { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as { X: number; Y: number } | undefined }); // prettier-ignore
     }
 
@@ -121,7 +121,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
             UndoManager.RunInBatch(
                 () =>
                     titleFieldKey &&
-                    SelectionManager.Views().forEach(d => {
+                    SelectionManager.Views.forEach(d => {
                         if (titleFieldKey === 'title') {
                             d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-');
                             if (StrCast(d.Document.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
@@ -165,7 +165,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     };
 
     onContainerDown = (e: React.PointerEvent) => {
-        const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].Document);
+        const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document);
         if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), emptyFunction, emptyFunction);
             e.stopPropagation();
@@ -173,7 +173,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     };
 
     onTitleDown = (e: React.PointerEvent) => {
-        const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].Document);
+        const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document);
         if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
@@ -196,17 +196,17 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     };
     @action
     onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
-        const dragDocView = SelectionManager.Views()[0];
+        const dragDocView = SelectionManager.Views[0];
         const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.Document);
         if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) {
             return false;
         }
         const containers = new Set<Doc | undefined>();
-        SelectionManager.Views().forEach(v => containers.add(DocCast(v.Document.embedContainer)));
+        SelectionManager.Views.forEach(v => containers.add(DocCast(v.Document.embedContainer)));
         if (containers.size > 1) return false;
         const { left, top } = dragDocView.getBounds() || { left: 0, top: 0 };
         const dragData = new DragManager.DocumentDragData(
-            SelectionManager.Views().map(dv => dv.Document),
+            SelectionManager.Views.map(dv => dv.Document),
             dragDocView._props.dropAction
         );
         dragData.offset = dragDocView._props.ScreenToLocalTransform().transformDirection(e.x - left, e.y - top);
@@ -216,7 +216,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
         dragData.canEmbed = dragTitle;
         this._hidden = true;
         DragManager.StartDocumentDrag(
-            SelectionManager.Views().map(dv => dv.ContentDiv!),
+            SelectionManager.Views.map(dv => dv.ContentDiv!),
             dragData,
             e.x,
             e.y,
@@ -231,7 +231,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     _deleteAfterIconify = false;
     _iconifyBatch: UndoManager.Batch | undefined;
     onCloseClick = (forceDeleteOrIconify: boolean | undefined) => {
-        const views = SelectionManager.Views().filter(v => v && v._props.renderDepth > 0);
+        const views = SelectionManager.Views.filter(v => v && v._props.renderDepth > 0);
         if (forceDeleteOrIconify === false && this._iconifyBatch) return;
         this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false;
         var iconifyingCount = views.length;
@@ -264,11 +264,11 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     };
 
     onMaximizeDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views().lastElement().Document]) ?? false, emptyFunction, this.onMaximizeClick, false, false);
+        setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views.lastElement().Document]) ?? false, emptyFunction, this.onMaximizeClick, false, false);
         e.stopPropagation();
     };
     onMaximizeClick = (e: any): void => {
-        const selectedDocs = SelectionManager.Views();
+        const selectedDocs = SelectionManager.Views;
         if (selectedDocs.length) {
             if (e.ctrlKey) {
                 // open an embedding in a new tab with Ctrl Key
@@ -300,11 +300,11 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     };
 
     onIconifyClick = (): void => {
-        SelectionManager.Views().forEach(dv => dv?.iconify());
+        SelectionManager.Views.forEach(dv => dv?.iconify());
         SelectionManager.DeselectAll();
     };
 
-    onSelectContainerDocClick = () => SelectionManager.Views()?.[0]?._props.docViewPath?.().lastElement()?.select(false);
+    onSelectContainerDocClick = () => SelectionManager.Views?.[0]?._props.docViewPath?.().lastElement()?.select(false);
     /**
      * sets up events when user clicks on the border radius editor
      */
@@ -319,7 +319,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
                 const [x, y] = [this.Bounds.x + 3, this.Bounds.y + 3];
                 const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
                 const dist = e.clientX < x && e.clientY < y ? 0 : Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y));
-                SelectionManager.Docs().map(doc => {
+                SelectionManager.Docs.map(doc => {
                     const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2);
                     const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box
                     doc.layout_borderRounding = `${radius}px`;
@@ -344,7 +344,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
             returnFalse, // don't care about move or up event,
             emptyFunction, // just care about whether we get a click event
             e => UndoManager.RunInBatch(
-                () => SelectionManager.Docs().forEach(doc =>
+                () => SelectionManager.Docs.forEach(doc =>
                         doc._pointerEvents = (doc._lockedPosition = !doc._lockedPosition)? 'none' : undefined ),
                 'toggleBackground' ) // prettier-ignore
         );
@@ -362,7 +362,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     @action
     onRotateCenterDown = (e: React.PointerEvent): void => {
         this._isRotating = true;
-        const seldocview = SelectionManager.Views()[0];
+        const seldocview = SelectionManager.Views[0];
         setupMoveUpEvents(
             this,
             e,
@@ -378,11 +378,11 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
         this._isRotating = true;
         const rcScreen = { X: this.rotCenter[0], Y: this.rotCenter[1] };
         const rotateUndo = UndoManager.StartBatch('drag rotation');
-        const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke);
+        const selectedInk = SelectionManager.Views.filter(i => i.ComponentView instanceof InkingStroke);
         const centerPoint = this.rotCenter.slice();
         const infos = new Map<Doc, { unrotatedDocPos: { x: number; y: number }; startRotCtr: { x: number; y: number }; accumRot: number }>();
-        const seldocview = SelectionManager.Views()[0];
-        SelectionManager.Views().forEach(dv => {
+        const seldocview = SelectionManager.Views[0];
+        SelectionManager.Views.forEach(dv => {
             const accumRot = (NumCast(dv.Document._rotation) / 180) * Math.PI;
             const localRotCtr = dv._props.ScreenToLocalTransform().transformPoint(rcScreen.X, rcScreen.Y);
             const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2];
@@ -391,7 +391,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
             infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot });
         });
         const infoRot = (angle: number, isAbs = false) => {
-            SelectionManager.Views().forEach(
+            SelectionManager.Views.forEach(
                 action(dv => {
                     const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.Document)!;
                     const endRotCtr = Utils.rotPt(startRotCtr.x, startRotCtr.y, isAbs ? angle : accumRot + angle);
@@ -439,7 +439,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        SnappingManager.SetIsResizing(SelectionManager.Docs().lastElement());
+        SnappingManager.SetIsResizing(SelectionManager.Docs.lastElement());
         setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
         e.stopPropagation();
         DocumentView.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
@@ -448,7 +448,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
         this._offset = { x: this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX, y: this._resizeHdlId.toLowerCase().includes('top') ? bounds.bottom - e.clientY : bounds.top - e.clientY };
         this._resizeUndo = UndoManager.StartBatch('drag resizing');
         this._snapPt = { x: e.pageX, y: e.pageY };
-        SelectionManager.Views().forEach(docView => docView.CollectionFreeFormView?.dragStarting(false, false));
+        SelectionManager.Views.forEach(docView => docView.CollectionFreeFormView?.dragStarting(false, false));
     };
 
     projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => {
@@ -466,7 +466,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
         return project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]);
     };
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
-        const first = SelectionManager.Views()[0];
+        const first = SelectionManager.Views[0];
         const effectiveAcl = GetEffectiveAcl(first.Document);
         if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false;
         if (!first) return false;
@@ -483,10 +483,10 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
         !this._interactionLock && runInAction(async () => {   // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate)
             this._interactionLock = true;
             this._snapPt = thisPt;
-            e.ctrlKey && (SelectionManager.Views().forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions()));
-            const fixedAspect = SelectionManager.Docs().some(this.hasFixedAspect);
+            e.ctrlKey && (SelectionManager.Views.forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions()));
+            const fixedAspect = SelectionManager.Docs.some(this.hasFixedAspect);
             const scaleAspect = {x:scale.x === 1 && fixedAspect ? scale.y : scale.x, y: scale.x !== 1 && fixedAspect ? scale.x : scale.y};
-            SelectionManager.Views().forEach(docView => 
+            SelectionManager.Views.forEach(docView => 
                 this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore
             await new Promise<any>(res => setTimeout(() => res(this._interactionLock = undefined)));
         }); // prettier-ignore
@@ -600,7 +600,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
         this._resizeUndo?.end();
 
         // detect layout_autoHeight gesture and apply
-        SelectionManager.Docs().forEach(doc => NumCast(doc._height) < 20 && (doc._layout_autoHeight = true));
+        SelectionManager.Docs.forEach(doc => NumCast(doc._height) < 20 && (doc._layout_autoHeight = true));
         //need to change points for resize, or else rotation/control points will fail.
         this._inkDragDocs
             .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
@@ -617,8 +617,8 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
 
     @computed
     get selectionTitle(): string {
-        if (SelectionManager.Views().length === 1) {
-            const selected = SelectionManager.Views()[0];
+        if (SelectionManager.Views.length === 1) {
+            const selected = SelectionManager.Views[0];
             if (this._titleControlString.startsWith('=')) {
                 return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.Document, this: selected.layoutDoc }, console.log).result?.toString() || '';
             }
@@ -627,11 +627,11 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
             }
             return this._accumulatedTitle;
         }
-        return SelectionManager.Views().length > 1 ? '-multiple-' : '-unset-';
+        return SelectionManager.Views.length > 1 ? '-multiple-' : '-unset-';
     }
 
     @computed get rotCenter() {
-        const lastView = SelectionManager.Views().lastElement();
+        const lastView = SelectionManager.Views.lastElement();
         if (lastView) {
             const invXf = lastView._props.ScreenToLocalTransform().inverse();
             const seldoc = lastView.layoutDoc;
@@ -643,7 +643,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
 
     render() {
         const { b, r, x, y } = this.Bounds;
-        const seldocview = SelectionManager.Views().lastElement();
+        const seldocview = SelectionManager.Views.lastElement();
         if (SnappingManager.IsDragging || r - x < 1 || x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) {
             setTimeout(action(() => (this._showNothing = true)));
             return null;
@@ -667,7 +667,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
             hideDecorations ||
             seldocview._props.hideOpenButton ||
             seldocview.Document.layout_hideOpenButton ||
-            SelectionManager.Views().some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) ||
+            SelectionManager.Views.some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) ||
             this._isRounding ||
             this._isRotating;
         const hideDeleteButton =
@@ -677,7 +677,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
             this._isRotating ||
             seldocview._props.hideDeleteButton ||
             seldocview.Document.hideDeleteButton ||
-            SelectionManager.Views().some(docView => {
+            SelectionManager.Views.some(docView => {
                 const collectionAcl = docView._props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView._props.docViewPath().lastElement().dataDoc) : AclEdit;
                 return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin;
             });
@@ -692,7 +692,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
         const bounds = this.ClippedBounds;
         const useLock = bounds.r - bounds.x > 135 && seldocview.CollectionFreeFormDocumentView;
         const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate?
-        const rotation = SelectionManager.Views().length == 1 ? seldocview.screenToLocalTransform().inverse().RotateDeg : 0;
+        const rotation = SelectionManager.Views.length == 1 ? seldocview.screenToLocalTransform().inverse().RotateDeg : 0;
 
         // Radius constants
         const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
@@ -754,7 +754,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
         );
         const centery = hideTitle ? 0 : this._titleHeight;
         const transformOrigin = `${50}% calc(50% + ${centery / 2}px)`;
-        const freeformDoc = SelectionManager.Views().some(v => v.CollectionFreeFormDocumentView);
+        const freeformDoc = SelectionManager.Views.some(v => v.CollectionFreeFormDocumentView);
         return (
             <div className="documentDecorations" style={{ display: this._showNothing && !freeformDoc ? 'none' : undefined }}>
                 <div
@@ -767,7 +767,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
                         transformOrigin,
                         background: SnappingManager.ShiftKey ? undefined : 'yellow',
                         pointerEvents: SnappingManager.ShiftKey || DocumentView.Interacting ? 'none' : 'all',
-                        display: SelectionManager.Views().length <= 1 || hideDecorations ? 'none' : undefined,
+                        display: SelectionManager.Views.length <= 1 || hideDecorations ? 'none' : undefined,
                         transform: `rotate(${rotation}deg)`,
                     }}
                     onPointerDown={this.onBackgroundDown}
@@ -824,7 +824,7 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
                                     style={{
                                         transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
                                     }}>
-                                    <DocumentButtonBar views={SelectionManager.Views} />
+                                    <DocumentButtonBar views={() => SelectionManager.Views} />
                                 </div>
                             )}
                         </div>
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 174f7951d..4015fb321 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -90,8 +90,8 @@ export class KeyManager {
     });
 
     nudge = (x: number, y: number, label: string) => {
-        const nudgeable = SelectionManager.Views().some(dv => dv.CollectionFreeFormDocumentView?.nudge);
-        nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.CollectionFreeFormDocumentView?.nudge(x, y)), label);
+        const nudgeable = SelectionManager.Views.some(dv => dv.CollectionFreeFormDocumentView?.nudge);
+        nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views.map(dv => dv.CollectionFreeFormDocumentView?.nudge(x, y)), label);
         return { stopPropagation: nudgeable, preventDefault: nudgeable };
     };
 
@@ -99,18 +99,18 @@ export class KeyManager {
         switch (keyname) {
             case 'u':
                 if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
-                    const ungroupings = SelectionManager.Views();
+                    const ungroupings = SelectionManager.Views;
                     UndoManager.RunInBatch(() => ungroupings.map(dv => (dv.layoutDoc.group = undefined)), 'ungroup');
                     SelectionManager.DeselectAll();
                 }
                 break;
             case 'g':
                 if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
-                    const selected = SelectionManager.Views();
+                    const selected = SelectionManager.Views;
                     const collectionView = selected.reduce((col, dv) => (col === null || dv.CollectionFreeFormView === col ? dv.CollectionFreeFormView : undefined), null as null | undefined | CollectionFreeFormView);
                     if (collectionView) {
                         UndoManager.RunInBatch(() =>
-                            collectionView._marqueeViewRef.current?.collection(e, true, SelectionManager.Docs())
+                            collectionView._marqueeViewRef.current?.collection(e, true, SelectionManager.Docs)
                         , 'grouping');
                         break;
                     }
@@ -182,14 +182,14 @@ export class KeyManager {
             case 'arrowdown':  return this.nudge(0, 10, 'nudge down');
             case 'u' : 
                 if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
-                    UndoManager.RunInBatch(() => SelectionManager.Docs().forEach(doc => (doc.group = undefined)), 'unggroup');
+                    UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => (doc.group = undefined)), 'unggroup');
                     SelectionManager.DeselectAll();
                 }
                 break;
             case 'g':
                 if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
                     const randomGroup = random(0, 1000);
-                    UndoManager.RunInBatch(() => SelectionManager.Docs().forEach(doc => (doc.group = randomGroup)), 'group');
+                    UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => (doc.group = randomGroup)), 'group');
                     SelectionManager.DeselectAll();
                 }
                 break;
@@ -208,7 +208,7 @@ export class KeyManager {
         switch (keyname) {
             case 'ƒ':
             case 'f':
-                const dv = SelectionManager.Views()?.[0];
+                const dv = SelectionManager.Views?.[0];
                 UndoManager.RunInBatch(() => dv.CollectionFreeFormDocumentView?.float(), 'float');
         }
 
@@ -256,8 +256,8 @@ export class KeyManager {
                 }
                 break;
             case 'f':
-                if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) {
-                    SelectionManager.Views()[0].ComponentView?.search?.('', false, false);
+                if (SelectionManager.Views.length === 1 && SelectionManager.Views[0].ComponentView?.search) {
+                    SelectionManager.Views[0].ComponentView?.search?.('', false, false);
                 } else {
                     const searchBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MySearcher);
                     if (searchBtn) {
@@ -299,17 +299,11 @@ export class KeyManager {
                 preventDefault = false;
                 break;
             case 'x':
-                if (SelectionManager.Views().length) {
+                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(':');
-                    SelectionManager.Views().length && navigator.clipboard.writeText(text);
+                    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;
@@ -318,15 +312,9 @@ export class KeyManager {
             case 'c':
                 if ((document.activeElement as any)?.type !== 'text' && !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(':');
-                    SelectionManager.Views().length && navigator.clipboard.writeText(text);
+                    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;
                 }
                 preventDefault = false;
@@ -344,7 +332,7 @@ export class KeyManager {
         if (!plain) return;
         const clone = plain.startsWith('__DashCloneId(');
         const docids = plain.split(':'); // hack!  docids[0] is the top left of the selection rectangle
-        const addDocument = SelectionManager.Views().lastElement()?.ComponentView?.addDocument;
+        const addDocument = SelectionManager.Views.lastElement()?.ComponentView?.addDocument;
         if (addDocument && (plain.startsWith('__DashDocId(') || clone)) {
             Doc.Paste(docids.slice(1), clone, addDocument);
         }
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index b835a3798..a5c2d99d2 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -208,8 +208,8 @@ export class InkEndPtHandles extends React.Component<InkEndProps> {
                 const v1n = { X: v1.X / v1len, Y: v1.Y / v1len };
                 const v2n = { X: v2.X / v2len, Y: v2.Y / v2len };
                 const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y);
-                InkStrokeProperties.Instance.stretchInk(SelectionManager.Views(), scaling, p2, v1n, e.shiftKey);
-                InkStrokeProperties.Instance.rotateInk(SelectionManager.Views(), angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call
+                InkStrokeProperties.Instance.stretchInk(SelectionManager.Views, scaling, p2, v1n, e.shiftKey);
+                InkStrokeProperties.Instance.rotateInk(SelectionManager.Views, angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call
                 return false;
             }),
             action(() => {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 73a6b687a..4f96b9234 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -145,7 +145,7 @@ export class MainView extends React.Component {
     componentDidMount() {
         reaction(
             // when a multi-selection occurs, remove focus from all active elements to allow keyboad input to go only to global key manager to act upon selection
-            () => SelectionManager.Views().slice(),
+            () => SelectionManager.Views.slice(),
             views => views.length > 1 && (document.activeElement as any)?.blur !== undefined && (document.activeElement as any)!.blur()
         );
         const scriptTag = document.createElement('script');
@@ -924,9 +924,7 @@ export class MainView extends React.Component {
         );
     }
     @computed get snapLines() {
-        SnappingManager.IsDragging;
-        SnappingManager.IsResizing;
-        const dragged = DragManager.docsBeingDragged.lastElement() ?? SelectionManager.Docs().lastElement();
+        const dragged = DragManager.docsBeingDragged.lastElement() ?? SelectionManager.Docs.lastElement();
         const dragPar = dragged ? DocumentManager.Instance.getDocumentView(dragged)?.CollectionFreeFormView : undefined;
         return !dragPar?.layoutDoc.freeform_snapLines ? null : (
             <div className="mainView-snapLines">
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 7824ad1c8..8540e81e1 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -40,13 +40,13 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @observable public static Instance: PropertiesButtons;
 
     @computed get selectedDoc() {
-        return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.Document;
+        return SelectionManager.SelectedSchemaDoc || SelectionManager.Views.lastElement()?.Document;
     }
     @computed get selectedLayoutDoc() {
-        return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.layoutDoc;
+        return SelectionManager.SelectedSchemaDoc || SelectionManager.Views.lastElement()?.layoutDoc;
     }
     @computed get selectedTabView() {
-        return !SelectionManager.SelectedSchemaDoc() && SelectionManager.Views().lastElement()?.topMost;
+        return !SelectionManager.SelectedSchemaDoc && SelectionManager.Views.lastElement()?.topMost;
     }
 
     propertyToggleBtn = (label: (on?: any) => string, property: string, tooltip: (on?: any) => string, icon: (on?: any) => any, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
@@ -64,8 +64,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 fillWidth={true}
                 toggleType={ToggleType.BUTTON}
                 onClick={undoable(() => {
-                    if (SelectionManager.Views().length > 1) {
-                        SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.Document, property));
+                    if (SelectionManager.Views.length > 1) {
+                        SelectionManager.Views.forEach(dv => (onClick ?? onPropToggle)(dv, dv.Document, property));
                     } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
                 }, property)}
             />
@@ -82,7 +82,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             on => `${on ? 'Set' : 'Remove'} lightbox flag`,
             on => 'window-restore',
             onClick => {
-                SelectionManager.Views().forEach(dv => {
+                SelectionManager.Views.forEach(dv => {
                     const containerDoc = dv.Document;
                     //containerDoc.followAllLinks =
                     // containerDoc.noShadow =
@@ -200,7 +200,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     //         on => `${on ? 'Set' : 'Remove'} lightbox flag`,
     //         on => 'window-restore',
     //         onClick => {
-    //             SelectionManager.Views().forEach(dv => {
+    //             SelectionManager.Views.forEach(dv => {
     //                 const containerDoc = dv.Document;
     //                 //containerDoc.followAllLinks =
     //                 // containerDoc.noShadow =
@@ -352,8 +352,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @undoBatch
     handlePerspectiveChange = (e: any) => {
         this.selectedDoc && (this.selectedDoc._type_collection = e.target.value);
-        SelectionManager.Views()
-            .filter(dv => dv.docView)
+        SelectionManager.Views.filter(dv => dv.docView)
             .map(dv => dv.docView!)
             .forEach(docView => (docView.layoutDoc._type_collection = e.target.value));
     };
@@ -416,8 +415,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @undoBatch
     @action
     handleOptionChange = (onClick: string) => {
-        SelectionManager.Views()
-            .filter(dv => dv.docView)
+        SelectionManager.Views.filter(dv => dv.docView)
             .map(dv => dv.docView!)
             .forEach(docView => {
                 const linkButton = IsFollowLinkScript(docView.props.Document.onClick);
@@ -443,7 +441,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
 
     @undoBatch
     editOnClickScript = () => {
-        if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.Document, undefined, 'onClick'));
+        if (SelectionManager.Views.length) SelectionManager.Views.forEach(dv => DocUtils.makeCustomViewClicked(dv.Document, undefined, 'onClick'));
         else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick');
     };
 
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index b1e99c3f5..58d3b72e8 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -34,10 +34,10 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
     });
 
     render() {
-        return !SelectionManager.Views().length ? null : (
+        return !SelectionManager.Views.length ? null : (
             <div className="propertiesDocBacklinksSelector" style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }}>
                 {this.props.hideTitle ? null : <p key="contexts">Contexts:</p>}
-                <LinkMenu docView={SelectionManager.Views().lastElement()} clearLinkEditor={undefined} itemHandler={this.getOnClick} style={{ left: 0, top: 0 }} />
+                <LinkMenu docView={SelectionManager.Views.lastElement()} clearLinkEditor={undefined} itemHandler={this.getOnClick} style={{ left: 0, top: 0 }} />
             </div>
         );
     }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index b4c591b89..442d09739 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -67,14 +67,14 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get selectedDoc() {
-        return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.Document || Doc.ActiveDashboard;
+        return SelectionManager.SelectedSchemaDoc || this.selectedDocumentView?.Document || Doc.ActiveDashboard;
     }
 
     @computed get selectedLayoutDoc() {
-        return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.layoutDoc || Doc.ActiveDashboard;
+        return SelectionManager.SelectedSchemaDoc || this.selectedDocumentView?.layoutDoc || Doc.ActiveDashboard;
     }
     @computed get selectedDocumentView() {
-        if (SelectionManager.Views().length) return SelectionManager.Views()[0];
+        if (SelectionManager.Views.length) return SelectionManager.Views[0];
         if (PresBox.Instance?.selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.Document);
         return undefined;
     }
@@ -177,7 +177,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const rows: JSX.Element[] = [];
         if (this.dataDoc && this.selectedDoc) {
             const ids = new Set<string>(reqdKeys);
-            const docs: Doc[] = SelectionManager.Views().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
+            const docs: Doc[] = 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 => doc[key] !== ComputedField.undefined && ids.add(key)));
 
             // prettier-ignore
@@ -226,7 +226,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @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) {
                 const newVal = value[0].toUpperCase() + value.substring(1, value.length);
@@ -290,7 +290,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get layoutPreview() {
-        if (SelectionManager.Views().length > 1) {
+        if (SelectionManager.Views.length > 1) {
             return '-- multiple selected --';
         }
         if (this.selectedDoc) {
@@ -339,7 +339,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     changePermissions = (e: any, user: string) => {
-        const docs = SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc));
+        const docs = SelectionManager.Views.length < 2 ? [this.selectedDoc] : SelectionManager.Views.map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc));
         SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls);
     };
 
@@ -457,7 +457,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @computed get sharingTable() {
         // all selected docs
-        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.Document);
+        const docs = SelectionManager.Views.length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views.map(docView => docView.Document);
         const target = docs[0];
 
         const showAdmin = GetEffectiveAcl(target) == AclAdmin;
@@ -573,7 +573,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @computed get editableTitle() {
         const titles = new Set<string>();
-        SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.Document.title)));
+        SelectionManager.Views.forEach(dv => titles.add(StrCast(dv.Document.title)));
         const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
         return (
             <div>
@@ -628,8 +628,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     setTitle = (value: string | number) => {
-        if (SelectionManager.Views().length > 1) {
-            SelectionManager.Views().map(dv => Doc.SetInPlace(dv.Document, 'title', value, true));
+        if (SelectionManager.Views.length > 1) {
+            SelectionManager.Views.map(dv => Doc.SetInPlace(dv.Document, 'title', value, true));
         } else if (this.dataDoc) {
             if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true);
             else KeyValueBox.SetField(this.dataDoc, 'title', value as string, true);
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index f8e80ed87..08fcd544e 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -49,7 +49,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
 
     componentDidMount() {
         reaction(
-            () => SelectionManager.Views().lastElement(),
+            () => SelectionManager.Views.lastElement(),
             view => view && this.SetSelection(view)
         );
     }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a74c6858c..79b15fdb1 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -344,7 +344,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                                 }
                             });
                         } else {
-                            const srcWeb = SelectionManager.Views().lastElement();
+                            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 modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
@@ -353,7 +353,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                             Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text;
                             addDocument(htmlDoc);
                             if (srcWeb) {
-                                const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
+                                const iframe = SelectionManager.Views[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
                                 const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
                                 if (focusNode) {
                                     const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 917241c5f..658d60523 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -168,7 +168,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const targetDataDoc = this.Document[DocData];
         const value = DocListCast(targetDataDoc[this._props.fieldKey]);
         const result = value.filter(v => !docs.includes(v));
-        if ((doc instanceof Doc ? [doc] : doc).some(doc => SelectionManager.Views().some(dv => Doc.AreProtosEqual(dv.Document, doc)))) SelectionManager.DeselectAll();
+        if ((doc instanceof Doc ? [doc] : doc).some(doc => SelectionManager.Views.some(dv => Doc.AreProtosEqual(dv.Document, doc)))) SelectionManager.DeselectAll();
         if (result.length !== value.length && doc instanceof Doc) {
             const ind = DocListCast(targetDataDoc[this._props.fieldKey]).indexOf(doc);
             const prev = ind && DocListCast(targetDataDoc[this._props.fieldKey])[ind - 1];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ec31f77b2..aa40fb809 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1961,18 +1961,18 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
 }
 ScriptingGlobals.add(CollectionBrowseClick);
 ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
-    !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
+    !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
 });
 ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
-    !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
+    !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
 });
 ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
-    const selView = SelectionManager.Views();
+    const selView = SelectionManager.Views;
     if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent';
     runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.()));
 });
 ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
-    SelectionManager.Views().forEach(view =>
+    SelectionManager.Views.forEach(view =>
         view.props.pinToPres(view.Document, {
             currentFrame: Cast(view.Document.currentFrame, 'number', null),
             pinData: {
@@ -1984,8 +1984,8 @@ ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
     );
 });
 ScriptingGlobals.add(function bringToFront() {
-    SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document));
+    SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document));
 });
 ScriptingGlobals.add(function sendToBack(doc: Doc) {
-    SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true));
+    SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true));
 });
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 9f316bef3..10beb120a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -176,7 +176,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 this.pasteTable(ns, x, y);
             })();
             e.stopPropagation();
-        } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
+        } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views.length < 2) {
             FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this._props.childLayoutString ? e.key : '';
             FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('type new note');
             this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this._props.xPadding === 0));
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 9a00595f9..276415d56 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,13 +1,13 @@
 import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, ObservableMap, observe } from 'mobx';
+import { action, computed, makeObservable, observable, ObservableMap, observe, override } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
+import { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
 import { Docs, DocumentOptions, DocUtils, FInfo } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
@@ -19,7 +19,7 @@ import { Colors } from '../../global/globalEnums';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from '../../nodes/DocumentView';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { DefaultStyleProvider, StyleProp } from '../../StyleProvider';
-import { CollectionSubView } from '../CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
@@ -79,15 +79,27 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _filterSearchValue: string = '';
     @observable _selectedCell: [Doc, number] | undefined = undefined;
 
+    _prevProps: SubCollectionViewProps;
+    @override _props: SubCollectionViewProps;
+    constructor(props: SubCollectionViewProps) {
+        super(props);
+        this._props = this._prevProps = props;
+        makeObservable(this);
+    }
+
+    componentDidUpdate() {
+        copyProps(this);
+    }
+
     // target HTMLelement portal for showing a popup menu to edit cell values.
     public get MenuTarget() {
         return this._menuTarget.current;
     }
 
     @computed get _selectedDocs() {
-        const selected = SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document));
+        const selected = SelectionManager.Docs.filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document));
         if (!selected.length) {
-            for (const sel of SelectionManager.Docs()) {
+            for (const sel of SelectionManager.Docs) {
                 const contextPath = DocumentManager.GetContextPath(sel, true);
                 if (contextPath.includes(this.Document)) {
                     const parentInd = contextPath.indexOf(this.Document);
@@ -107,7 +119,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get tableWidth() {
-        return this.props.PanelWidth() - this.previewWidth - (this.previewWidth === 0 ? 0 : CollectionSchemaView._previewDividerWidth);
+        return this._props.PanelWidth() - this.previewWidth - (this.previewWidth === 0 ? 0 : CollectionSchemaView._previewDividerWidth);
     }
 
     @computed get columnKeys() {
@@ -139,9 +151,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         return BoolCast(this.layoutDoc.sortDesc);
     }
 
-    @action
     componentDidMount() {
-        this.props.setContentView?.(this);
+        this._props.setContentView?.(this);
         document.addEventListener('keydown', this.onKeyDown);
 
         Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
@@ -239,7 +250,6 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @undoBatch
-    @action
     setColumnSort = (field: string | undefined, desc: boolean = false) => {
         this.layoutDoc.sortField = field;
         this.layoutDoc.sortDesc = desc;
@@ -248,7 +258,6 @@ export class CollectionSchemaView extends CollectionSubView() {
     addRow = (doc: Doc | Doc[]) => this.addDocument(doc);
 
     @undoBatch
-    @action
     changeColumnKey = (index: number, newKey: string, defaultVal?: any) => {
         if (!this.documentKeys.includes(newKey)) {
             this.addNewKey(newKey, defaultVal);
@@ -260,7 +269,6 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @undoBatch
-    @action
     addColumn = (key: string, defaultVal?: any) => {
         if (!this.documentKeys.includes(key)) {
             this.addNewKey(key, defaultVal);
@@ -281,7 +289,6 @@ export class CollectionSchemaView extends CollectionSubView() {
     addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[key] = defaultVal));
 
     @undoBatch
-    @action
     removeColumn = (index: number) => {
         if (this.columnKeys.length === 1) return;
         const currWidths = this.storedColumnWidths.slice();
@@ -320,8 +327,8 @@ export class CollectionSchemaView extends CollectionSubView() {
                 change = this._displayColumnWidths[shrinking] - CollectionSchemaView._minColWidth;
             }
 
-            this._displayColumnWidths[shrinking] -= change * this.props.ScreenToLocalTransform().Scale;
-            this._displayColumnWidths[growing] += change * this.props.ScreenToLocalTransform().Scale;
+            this._displayColumnWidths[shrinking] -= change * this._props.ScreenToLocalTransform().Scale;
+            this._displayColumnWidths[growing] += change * this._props.ScreenToLocalTransform().Scale;
 
             return false;
         }
@@ -335,7 +342,6 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @undoBatch
-    @action
     moveColumn = (fromIndex: number, toIndex: number) => {
         let currKeys = this.columnKeys.slice();
         currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]);
@@ -379,7 +385,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @action
     highlightDropColumn = (e: PointerEvent) => {
         e.stopPropagation();
-        const mouseX = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+        const mouseX = this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
         const index = this.findDropIndex(mouseX);
         this._colEles.forEach((colRef, i) => {
             let leftStyle = '';
@@ -439,7 +445,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.columnDragData) {
-            const mouseX = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0];
+            const mouseX = this._props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0];
             const index = this.findDropIndex(mouseX);
             this.moveColumn(de.complete.columnDragData.colIndex, index ?? de.complete.columnDragData.colIndex);
 
@@ -483,7 +489,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         const nativeWidth = this._previewRef!.getBoundingClientRect();
         const minWidth = 40;
         const maxWidth = 1000;
-        const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
+        const movedWidth = this._props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
         const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
         this.layoutDoc.schema_previewWidth = width;
         return false;
@@ -507,8 +513,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         const found = this._tableContentRef && Array.from(this._tableContentRef.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
         if (found) {
             const rect = found.getBoundingClientRect();
-            const localRect = this.props.ScreenToLocalTransform().transformBounds(rect.left, rect.top, rect.width, rect.height);
-            if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this.props.PanelHeight()) {
+            const localRect = this._props.ScreenToLocalTransform().transformBounds(rect.left, rect.top, rect.width, rect.height);
+            if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this._props.PanelHeight()) {
                 let focusSpeed = options.zoomTime ?? 50;
                 smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - this.rowHeightFunc(), options.easeFunc);
                 return focusSpeed;
@@ -755,7 +761,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @computed get renderFilterOptions() {
         const keyOptions: string[] = [];
         const columnKey = this.columnKeys[this._filterColumnIndex!];
-        const allDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
+        const allDocs = DocListCast(this.dataDoc[this._props.fieldKey]);
         allDocs.forEach(doc => {
             const value = StrCast(doc[columnKey]);
             if (!keyOptions.includes(value) && value !== '' && (this._filterSearchValue === '' || value.includes(this._filterSearchValue))) {
@@ -779,9 +785,9 @@ export class CollectionSchemaView extends CollectionSubView() {
                         onClick={e => e.stopPropagation()}
                         onChange={action(e => {
                             if (e.target.checked) {
-                                Doc.setDocFilter(this.props.Document, columnKey, key, 'check');
+                                Doc.setDocFilter(this.Document, columnKey, key, 'check');
                             } else {
-                                Doc.setDocFilter(this.props.Document, columnKey, key, 'remove');
+                                Doc.setDocFilter(this.Document, columnKey, key, 'remove');
                             }
                         })}
                         checked={bool}
@@ -828,8 +834,8 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
     rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight);
     sortedDocsFunc = () => this.sortedDocs;
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
+    isContentActive = () => this._props.isSelected() || this._props.isContentActive();
+    screenToLocal = () => this._props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
     previewWidthFunc = () => this.previewWidth;
     render() {
         return (
@@ -838,7 +844,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 <div
                     className="schema-table"
                     style={{ width: `calc(100% - ${this.previewWidth}px)` }}
-                    onWheel={e => this.props.isContentActive() && e.stopPropagation()}
+                    onWheel={e => this._props.isContentActive() && e.stopPropagation()}
                     ref={r => {
                         // prevent wheel events from passively propagating up through containers
                         r?.addEventListener('wheel', (e: WheelEvent) => {}, { passive: false });
@@ -864,7 +870,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 openContextMenu={this.openContextMenu}
                                 dragColumn={this.dragColumn}
                                 setColRef={this.setColRef}
-                                isContentActive={this.props.isContentActive}
+                                isContentActive={this._props.isContentActive}
                             />
                         ))}
                     </div>
@@ -895,10 +901,10 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 onClickScriptDisable="always"
                                 focus={emptyFunction}
                                 defaultDoubleClick={returnIgnore}
-                                renderDepth={this.props.renderDepth + 1}
+                                renderDepth={this._props.renderDepth + 1}
                                 rootSelected={this.rootSelected}
                                 PanelWidth={this.previewWidthFunc}
-                                PanelHeight={this.props.PanelHeight}
+                                PanelHeight={this._props.PanelHeight}
                                 isContentActive={returnTrue}
                                 isDocumentActive={returnFalse}
                                 ScreenToLocalTransform={this.screenToLocal}
@@ -907,12 +913,12 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 searchFilterDocs={this.searchFilterDocs}
                                 styleProvider={DefaultStyleProvider}
                                 docViewPath={returnEmptyDoclist}
-                                moveDocument={this.props.moveDocument}
+                                moveDocument={this._props.moveDocument}
                                 addDocument={this.addRow}
-                                removeDocument={this.props.removeDocument}
+                                removeDocument={this._props.removeDocument}
                                 whenChildContentsActiveChanged={returnFalse}
-                                addDocTab={this.props.addDocTab}
-                                pinToPres={this.props.pinToPres}
+                                addDocTab={this._props.addDocTab}
+                                pinToPres={this._props.pinToPres}
                                 bringToFront={returnFalse}
                             />
                         )}
@@ -936,7 +942,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
         return (
             <div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + this.props.rowHeight()}px)` }}>
                 {this.props.childDocs().docs.map((doc: Doc, index: number) => (
-                    <div className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
+                    <div key={doc[Id]} className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
                         <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} />
                     </div>
                 ))}
@@ -954,8 +960,18 @@ interface CollectionSchemaViewDocProps {
 
 @observer
 class CollectionSchemaViewDoc extends React.Component<CollectionSchemaViewDocProps> {
-    tableWidthFunc = () => this.props.schema.tableWidth;
-    screenToLocalXf = () => this.props.schema.props.ScreenToLocalTransform().translate(0, -this.props.rowHeight() - this.props.index * this.props.rowHeight());
+    _prevProps: CollectionSchemaViewDocProps;
+    @observable _props;
+    constructor(props: CollectionSchemaViewDocProps) {
+        super(props);
+        this._props = this._prevProps = props;
+        makeObservable(this);
+    }
+    componentDidUpdate() {
+        copyProps(this);
+    }
+    tableWidthFunc = () => this._props.schema.tableWidth;
+    screenToLocalXf = () => this._props.schema._props.ScreenToLocalTransform().translate(0, -this._props.rowHeight() - this._props.index * this._props.rowHeight());
     noOpacityStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
         if (property === StyleProp.Opacity) return 1;
         return DefaultStyleProvider(doc, props, property);
@@ -963,30 +979,30 @@ class CollectionSchemaViewDoc extends React.Component<CollectionSchemaViewDocPro
     render() {
         return (
             <DocumentView
-                key={this.props.doc[Id]}
-                {...this.props.schema.props}
-                LayoutTemplate={this.props.schema.props.childLayoutTemplate}
-                LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey, this.props.index)}
-                Document={this.props.doc}
-                renderDepth={this.props.schema.props.renderDepth + 1}
+                key={this._props.doc[Id]}
+                {...this._props.schema._props}
+                LayoutTemplate={this._props.schema._props.childLayoutTemplate}
+                LayoutTemplateString={SchemaRowBox.LayoutString(this._props.schema._props.fieldKey, this._props.index)}
+                Document={this._props.doc}
+                renderDepth={this._props.schema._props.renderDepth + 1}
                 PanelWidth={this.tableWidthFunc}
-                PanelHeight={this.props.rowHeight}
+                PanelHeight={this._props.rowHeight}
                 styleProvider={this.noOpacityStyleProvider}
                 waitForDoubleClickToClick={returnNever}
                 defaultDoubleClick={returnIgnore}
                 dragAction="move"
                 onClickScriptDisable="always"
-                focus={this.props.schema.focusDocument}
-                childFilters={this.props.schema.childDocFilters}
-                childFiltersByRanges={this.props.schema.childDocRangeFilters}
-                searchFilterDocs={this.props.schema.searchFilterDocs}
-                rootSelected={this.props.schema.rootSelected}
+                focus={this._props.schema.focusDocument}
+                childFilters={this._props.schema.childDocFilters}
+                childFiltersByRanges={this._props.schema.childDocRangeFilters}
+                searchFilterDocs={this._props.schema.searchFilterDocs}
+                rootSelected={this._props.schema.rootSelected}
                 ScreenToLocalTransform={this.screenToLocalXf}
                 bringToFront={emptyFunction}
                 dragWhenActive={true}
-                isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive}
+                isDocumentActive={this._props.schema._props.childDocumentsActive?.() ? this._props.schema._props.isDocumentActive : this._props.schema.isContentActive}
                 isContentActive={emptyFunction}
-                whenChildContentsActiveChanged={this.props.schema.props.whenChildContentsActiveChanged}
+                whenChildContentsActiveChanged={this._props.schema._props.whenChildContentsActiveChanged}
                 hideDecorations={true}
                 hideTitle={true}
                 hideDocumentButtonBar={true}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 5a2926159..737d7e0f4 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,13 +1,13 @@
 import * as React from 'react';
 import { IconButton, Size } from 'browndash-components';
-import { computed } from 'mobx';
+import { computed, makeObservable, override } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { CgClose } from 'react-icons/cg';
 import { FaExternalLinkAlt } from 'react-icons/fa';
 import { Doc } from '../../../../fields/Doc';
 import { BoolCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
+import { copyProps, emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
@@ -28,17 +28,27 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
     public static LayoutString(fieldKey: string, rowIndex: number) {
         return FieldView.LayoutString(SchemaRowBox, fieldKey).replace('fieldKey', `rowIndex={${rowIndex}} fieldKey`);
     }
-
     private _ref: HTMLDivElement | null = null;
 
+    _prevProps: FieldViewProps & SchemaRowBoxProps;
+    @override _props: FieldViewProps & SchemaRowBoxProps;
+    constructor(props: FieldViewProps & SchemaRowBoxProps) {
+        super(props);
+        this._props = this._prevProps = props;
+        makeObservable(this);
+    }
+    componentDidUpdate() {
+        copyProps(this);
+    }
+
     bounds = () => this._ref?.getBoundingClientRect();
 
     @computed get schemaView() {
-        return this.props.DocumentView?.()._props.docViewPath().lastElement()?.ComponentView as CollectionSchemaView;
+        return this._props.DocumentView?.()._props.docViewPath().lastElement()?.ComponentView as CollectionSchemaView;
     }
 
     @computed get schemaDoc() {
-        return this.props.DocumentView?.()._props.docViewPath().lastElement()?.Document;
+        return this._props.DocumentView?.()._props.docViewPath().lastElement()?.Document;
     }
 
     @computed get rowIndex() {
@@ -46,7 +56,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
     }
 
     componentDidMount(): void {
-        this.props.setContentView?.(this);
+        this._props.setContentView?.(this);
     }
 
     select = (ctrlKey: boolean, shiftKey: boolean) => {
@@ -54,12 +64,12 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
         const lastSelected = Array.from(this.schemaView._selectedDocs).lastElement();
         if (shiftKey && lastSelected) this.schemaView.selectRows(this.Document, lastSelected);
         else {
-            this.props.select?.(ctrlKey);
+            this._props.select?.(ctrlKey);
         }
     };
 
     onPointerEnter = (e: any) => {
-        if (SnappingManager.IsDragging && this.props.isContentActive()) {
+        if (SnappingManager.IsDragging && this._props.isContentActive()) {
             document.removeEventListener('pointermove', this.onPointerMove);
             document.addEventListener('pointermove', this.onPointerMove);
         }
@@ -103,7 +113,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
         return (
             <div
                 className="schema-row"
-                style={{ height: this.props.PanelHeight(), backgroundColor: this.props.isSelected() ? Colors.LIGHT_BLUE : undefined }}
+                style={{ height: this._props.PanelHeight(), backgroundColor: this._props.isSelected() ? Colors.LIGHT_BLUE : undefined }}
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
@@ -114,7 +124,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                     className="row-menu"
                     style={{
                         width: CollectionSchemaView._rowMenuWidth,
-                        pointerEvents: !this.props.isContentActive() ? 'none' : undefined,
+                        pointerEvents: !this._props.isContentActive() ? 'none' : undefined,
                     }}>
                     <IconButton
                         tooltip="close"
@@ -128,7 +138,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                                 emptyFunction,
                                 undoable(e => {
                                     e.stopPropagation();
-                                    this.props.removeDocument?.(this.Document);
+                                    this._props.removeDocument?.(this.Document);
                                 }, 'Delete Row')
                             )
                         }
@@ -145,7 +155,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                                 emptyFunction,
                                 undoable(e => {
                                     e.stopPropagation();
-                                    this.props.addDocTab(this.Document, OpenWhere.addRight);
+                                    this._props.addDocTab(this.Document, OpenWhere.addRight);
                                 }, 'Open schema Doc preview')
                             )
                         }
@@ -161,7 +171,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                             allowCRs={false} // to enter text with new lines, must use \n
                             columnWidth={this.columnWidth(index)}
                             rowHeight={this.schemaView.rowHeightFunc}
-                            isRowActive={this.props.isContentActive}
+                            isRowActive={this._props.isContentActive}
                             getFinfo={this.getFinfo}
                             selectCell={this.selectCell}
                             deselectCell={this.deselectCell}
@@ -172,7 +182,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                             transform={() => {
                                 const ind = index === this.schemaView.columnKeys.length - 1 ? this.schemaView.columnKeys.length - 3 : index;
                                 const x = this.schemaView?.displayColumnWidths.reduce((p, c, i) => (i <= ind ? p + c : p), 0);
-                                const y = (this.props.rowIndex ?? 0) * this.props.PanelHeight();
+                                const y = (this._props.rowIndex ?? 0) * this._props.PanelHeight();
                                 return new Transform(x + CollectionSchemaView._rowMenuWidth, y, 1);
                             }}
                         />
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index dcd3bffac..814003955 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -21,18 +21,18 @@ import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
 import { WebBox } from '../nodes/WebBox';
 
 ScriptingGlobals.add(function IsNoneSelected() {
-    return SelectionManager.Views().length <= 0;
+    return SelectionManager.Views.length <= 0;
 }, 'are no document selected');
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setView(view: string) {
-    const selected = SelectionManager.Docs().lastElement();
+    const selected = SelectionManager.Docs.lastElement();
     selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed');
 });
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
-    const selectedViews = SelectionManager.Views();
+    const selectedViews = SelectionManager.Views;
     if (Doc.ActiveTool !== InkTool.None) {
         if (checkResult) {
             return ActiveFillColor();
@@ -60,7 +60,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
             }
         });
     } else {
-        const selected = SelectionManager.Docs().length ? SelectionManager.Docs() : LinkManager.currentLink ? [LinkManager.currentLink] : [];
+        const selected = SelectionManager.Docs.length ? SelectionManager.Docs : LinkManager.currentLink ? [LinkManager.currentLink] : [];
         if (checkResult) {
             return selected.lastElement()?._backgroundColor ?? 'transparent';
         }
@@ -72,10 +72,10 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
     if (checkResult) {
-        return SelectionManager.Views().length ? StrCast(SelectionManager.Docs().lastElement().layout_headingColor) : Doc.SharingDoc().headingColor;
+        return SelectionManager.Views.length ? StrCast(SelectionManager.Docs.lastElement().layout_headingColor) : Doc.SharingDoc().headingColor;
     }
-    if (SelectionManager.Views().length) {
-        SelectionManager.Docs().forEach(doc => {
+    if (SelectionManager.Views.length) {
+        SelectionManager.Docs.forEach(doc => {
             Doc.GetProto(doc).layout_headingColor = color;
             doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title');
         });
@@ -88,7 +88,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+    const selected = SelectionManager.Views.length ? SelectionManager.Views[0] : undefined;
     if (checkResult) {
         if (NumCast(selected?.Document.z) >= 1) return true;
         return false;
@@ -97,7 +97,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
 });
 
 ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce', checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
+    const selected = SelectionManager.Docs.lastElement();
     // prettier-ignore
     const map: Map<'flashcards' | 'center' |'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll' | 'fitOnce', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc, dv:DocumentView) => void;}> = new Map([
         ['grid', {
@@ -140,13 +140,13 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'center' | 'grid
         return map.get(attr)?.checkResult(selected);
     }
     const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
-    SelectionManager.Views().map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv));
+    SelectionManager.Views.map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv));
     setTimeout(() => batch.end(), 100);
 });
 
 ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: any, checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-    const selected = SelectionManager.Docs().lastElement();
+    const selected = SelectionManager.Docs.lastElement();
     // prettier-ignore
     const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => any; setDoc: () => void;}> = new Map([
         ['font', {
@@ -338,7 +338,7 @@ ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode');
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement() ?? Doc.UserDoc();
+    const selected = SelectionManager.Docs.lastElement() ?? Doc.UserDoc();
     // prettier-ignore
     const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
         ['inkMask', {
@@ -367,16 +367,14 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
         return map.get(option)?.checkResult();
     }
     map.get(option)?.setMode();
-    SelectionManager.Docs()
-        .filter(doc => doc.type === DocumentType.INK)
-        .map(doc => map.get(option)?.setInk(doc));
+    SelectionManager.Docs.filter(doc => doc.type === DocumentType.INK).map(doc => map.get(option)?.setInk(doc));
 });
 
 /** WEB
  *      webSetURL
  **/
 ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement();
+    const selected = SelectionManager.Views.lastElement();
     if (selected?.Document.type === DocumentType.WEB) {
         if (checkResult) {
             return StrCast(selected.Document.data, Cast(selected.Document.data, WebField, null)?.url?.href);
@@ -386,14 +384,14 @@ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
     }
 });
 ScriptingGlobals.add(function webForward(checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox;
+    const selected = SelectionManager.Views.lastElement()?.ComponentView as WebBox;
     if (checkResult) {
         return selected?.forward(checkResult) ? undefined : 'lightGray';
     }
     selected?.forward();
 });
 ScriptingGlobals.add(function webBack(checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox;
+    const selected = SelectionManager.Views.lastElement()?.ComponentView as WebBox;
     if (checkResult) {
         return selected?.back(checkResult) ? undefined : 'lightGray';
     }
@@ -404,7 +402,7 @@ ScriptingGlobals.add(function webBack(checkResult?: boolean) {
  *      toggleSchemaPreview
  **/
 ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
+    const selected = SelectionManager.Docs.lastElement();
     if (checkResult && selected) {
         const result: boolean = NumCast(selected.schema_previewWidth) > 0;
         if (result) return Colors.MEDIUM_BLUE;
@@ -418,7 +416,7 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
     }
 });
 ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
+    const selected = SelectionManager.Docs.lastElement();
     if (checkResult && selected) {
         return NumCast(selected._schema_singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent';
     }
@@ -431,7 +429,7 @@ ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
  *      groupBy
  */
 ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
-    SelectionManager.Docs().map(doc => (doc._text_fontFamily = key));
+    SelectionManager.Docs.map(doc => (doc._text_fontFamily = key));
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
     if (checkResult) {
         return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cca2ed3ba..08ee708f7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -400,7 +400,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
         if (this._mainCont.current) {
-            const views = SelectionManager.Views().filter(dv => dv.docView?._mainCont.current);
+            const views = SelectionManager.Views.filter(dv => dv.docView?._mainCont.current);
             const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [this._props.DocumentView()];
             const dragData = new DragManager.DocumentDragData(selected.map(dv => dv.Document));
             const [left, top] = this._props.ScreenToLocalTransform().scale(this.NativeDimScaling).inverse().transformPoint(0, 0);
@@ -760,8 +760,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 if (this._props.bringToFront !== emptyFunction) {
                     const zorders = cm.findByDescription('ZOrder...');
                     const zorderItems: ContextMenuProps[] = zorders && 'subitems' in zorders ? zorders.subitems : [];
-                    zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views().forEach(dv => dv._props.bringToFront(dv.Document, false)), icon: 'arrow-up' });
-                    zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv._props.bringToFront(dv.Document, true)), icon: 'arrow-down' });
+                    zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront(dv.Document, false)), icon: 'arrow-up' });
+                    zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront(dv.Document, true)), icon: 'arrow-down' });
                     zorderItems.push({
                         description: !this.layoutDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
                         event: undoBatch(action(() => (this.layoutDoc._keepZWhenDragged = !this.layoutDoc._keepZWhenDragged))),
@@ -1588,7 +1588,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     docViewPathFunc = () => this.docViewPath;
     isSelected = () => this.SELECTED;
     select = (extendSelection: boolean, focusSelection?: boolean) => {
-        if (this.SELECTED && SelectionManager.Views().length > 1) SelectionManager.DeselectView(this);
+        if (this.SELECTED && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this);
         else {
             SelectionManager.SelectView(this, extendSelection);
             if (focusSelection) {
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 22339907f..d8b1f125c 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -187,7 +187,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         let icon: IconProp = 'caret-down';
         const isViewDropdown = script?.script.originalScript.startsWith('setView');
         if (isViewDropdown) {
-            const selected = SelectionManager.Docs();
+            const selected = SelectionManager.Docs;
             if (selected.lastElement()) {
                 if (StrCast(selected.lastElement().type) === DocumentType.COL) {
                     text = StrCast(selected.lastElement()._type_collection);
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 31aa15588..a7e905fca 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -83,9 +83,9 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1';
         const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
         const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
-        const selView = SelectionManager.Views().lastElement()?._props.LayoutTemplateString?.includes('link_anchor_1')
+        const selView = SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_1')
             ? 'link_anchor_1'
-            : SelectionManager.Views().lastElement()?._props.LayoutTemplateString?.includes('link_anchor_2')
+            : SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_2')
               ? 'link_anchor_2'
               : '';
         return (
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index e6c1db0af..ee1ea3ceb 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -47,7 +47,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     componentDidMount() {
         this._disposer = reaction(
-            () => SelectionManager.Views().slice(),
+            () => SelectionManager.Views.slice(),
             sel => MapAnchorMenu.Instance.fadeOut(true)
         );
     }
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 2c4c992d1..fdd8285d5 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -510,8 +510,8 @@
 //     // TODO: auto center on select a document in the sidebar
 //     private handleMapCenter = (map: google.maps.Map) => {
 //         // console.log("print the selected views in selectionManager:")
-//         // if (SelectionManager.Views().lastElement()) {
-//         //     console.log(SelectionManager.Views().lastElement());
+//         // if (SelectionManager.Views.lastElement()) {
+//         //     console.log(SelectionManager.Views.lastElement());
 //         // }
 //     };
 
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index d7e799161..7de1eb717 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -116,7 +116,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     }
     componentDidMount() {
         this._disposer = reaction(
-            () => SelectionManager.Views().slice(),
+            () => SelectionManager.Views.slice(),
             views => this.updateMenu(undefined, undefined, undefined, undefined)
         );
     }
@@ -245,8 +245,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                 m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize);
                 m.type === state.schema.marks.marker && activeHighlights.add(String(m.attrs.highlight));
             });
-        } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) {
-            SelectionManager.Views().forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize)));
+        } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) {
+            SelectionManager.Views.forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize)));
         }
         return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) };
     }
@@ -365,8 +365,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                 this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
                 this.view.focus();
             }
-        } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) {
-            SelectionManager.Views().forEach(dv => (dv.Document._text_fontSize = fontSize));
+        } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) {
+            SelectionManager.Views.forEach(dv => (dv.Document._text_fontSize = fontSize));
         } else Doc.UserDoc().fontSize = fontSize;
         this.updateMenu(this.view, undefined, this._props, this.layoutDoc);
     };
@@ -649,7 +649,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                         if (linkDoc instanceof Doc) {
                             const link_anchor_1 = await Cast(linkDoc.link_anchor_1, Doc);
                             const link_anchor_2 = await Cast(linkDoc.link_anchor_2, Doc);
-                            const currentDoc = SelectionManager.Docs().lastElement();
+                            const currentDoc = SelectionManager.Docs.lastElement();
                             if (currentDoc && link_anchor_1 && link_anchor_2) {
                                 if (Doc.AreProtosEqual(currentDoc, link_anchor_1)) {
                                     return StrCast(link_anchor_2.title);
@@ -781,11 +781,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         //         <div className="collectionMenu-divider" key="divider 3" />
         //         {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
         //             this.activeFontSize = val;
-        //             SelectionManager.Views().map(dv => dv.props.Document._text_fontSize = val);
+        //             SelectionManager.Views.map(dv => dv.props.Document._text_fontSize = val);
         //         })),
         //         this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
         //             this.activeFontFamily = val;
-        //             SelectionManager.Views().map(dv => dv.props.Document._text_fontFamily = val);
+        //             SelectionManager.Views.map(dv => dv.props.Document._text_fontFamily = val);
         //         })),
         //         <div className="collectionMenu-divider" key="divider 4" />,
         //         this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index a224ec7fa..4ff816812 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -139,7 +139,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return false;
     }
     @computed get selectedDocumentView() {
-        if (SelectionManager.Views().length) return SelectionManager.Views()[0];
+        if (SelectionManager.Views.length) return SelectionManager.Views[0];
         if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.Document);
     }
     @computed get isPres() {
@@ -187,7 +187,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this._unmounting = false;
         this.turnOffEdit(true);
         this._disposers.selection = reaction(
-            () => SelectionManager.Views().slice(),
+            () => SelectionManager.Views.slice(),
             views => (!PresBox.Instance || views.some(view => view.Document === this.Document)) && this.updateCurrentPresentation(),
             { fireImmediately: true }
         );
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 3c26c307f..0627876fa 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -69,7 +69,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     componentDidMount() {
         this._disposer = reaction(
-            () => SelectionManager.Views().slice(),
+            () => SelectionManager.Views.slice(),
             sel => AnchorMenu.Instance.fadeOut(true)
         );
     }
@@ -158,7 +158,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
      * all selected text available to summarize but its only supported for pdf and web ATM.
      * @returns Whether the GPT icon for summarization should appear
      */
-    canSummarize = () => SelectionManager.Docs().some(doc => [DocumentType.PDF, DocumentType.WEB].includes(doc.type as any));
+    canSummarize = () => SelectionManager.Docs.some(doc => [DocumentType.PDF, DocumentType.WEB].includes(doc.type as any));
 
     render() {
         const buttons =
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 349f15c96..a581e9df6 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -119,7 +119,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
         this._disposers.selected = reaction(
             () => this._props.isSelected(),
-            selected => SelectionManager.Views().length === 1 && this.setupPdfJsViewer(),
+            selected => SelectionManager.Views.length === 1 && this.setupPdfJsViewer(),
             { fireImmediately: true }
         );
         this._disposers.curPage = reaction(
-- 
cgit v1.2.3-70-g09d2


From cebe9d2a567c20b99c8c394cfa598ee9d4d53ece Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 14 Dec 2023 00:07:52 -0500
Subject: a bunch more fixes to making things observable.  fixed calling
 super.componentDidUpdate on subsclasses

---
 src/Utils.ts                                       |  17 +--
 src/client/util/BranchingTrailManager.tsx          |  12 +-
 src/client/util/CaptureManager.tsx                 |   3 +-
 src/client/util/GroupManager.tsx                   |   6 +-
 src/client/util/PingManager.ts                     |   3 +-
 src/client/util/RTFMarkup.tsx                      |   3 +-
 src/client/util/ReplayMovements.ts                 |   3 +-
 src/client/util/SelectionManager.ts                |  10 +-
 src/client/util/ServerStats.tsx                    |   3 +-
 src/client/util/SharingManager.tsx                 |   3 +-
 src/client/util/TrackMovements.ts                  |   4 +-
 src/client/util/reportManager/ReportManager.tsx    |   3 +-
 src/client/views/ContextMenu.tsx                   |   2 +-
 src/client/views/DictationOverlay.tsx              |   3 +-
 src/client/views/DocumentButtonBar.tsx             | 133 ++-------------------
 src/client/views/EditableView.tsx                  |   1 +
 src/client/views/GestureOverlay.tsx                |  17 +--
 src/client/views/InkStrokeProperties.ts            |   3 +-
 src/client/views/LightboxView.tsx                  |   3 +-
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/MarqueeAnnotator.tsx              |   2 +-
 src/client/views/ObservableReactComponent.tsx      |  10 +-
 src/client/views/PropertiesView.tsx                |   2 +-
 src/client/views/ScriptBox.tsx                     |   3 +-
 src/client/views/ScriptingRepl.tsx                 |  37 ++----
 src/client/views/animationtimeline/Region.tsx      |   3 +-
 .../views/animationtimeline/TimelineMenu.tsx       |   5 +-
 .../views/collections/CollectionDockingView.tsx    |   4 +-
 src/client/views/collections/CollectionMenu.tsx    |   7 +-
 .../collections/CollectionStackedTimeline.tsx      |   4 +-
 .../CollectionStackingViewFieldColumn.tsx          |   2 +-
 src/client/views/collections/CollectionView.tsx    |   1 -
 src/client/views/collections/TabDocView.tsx        |   1 +
 src/client/views/collections/TreeView.tsx          |   1 +
 .../CollectionFreeFormInfoState.tsx                |   3 +-
 .../CollectionFreeFormInfoUI.tsx                   |  14 +--
 .../collectionFreeForm/MarqueeOptionsMenu.tsx      |   5 +-
 .../CollectionMulticolumnView.tsx                  |  78 ++++++------
 .../CollectionMultirowView.tsx                     |  74 ++++++------
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   3 +-
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 src/client/views/nodes/FunctionPlotBox.tsx         |   2 +-
 src/client/views/nodes/ImageBox.tsx                |   3 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   4 +-
 src/client/views/nodes/MapBox/MapAnchorMenu.tsx    |   5 +-
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 .../nodes/PhysicsBox/PhysicsSimulationBox.tsx      |   3 +-
 .../nodes/PhysicsBox/PhysicsSimulationWeight.tsx   |   3 +-
 src/client/views/nodes/RadialMenu.tsx              |   6 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  13 +-
 src/client/views/nodes/ScriptingBox.tsx            |   3 +-
 src/client/views/nodes/VideoBox.tsx                |   2 +-
 src/client/views/nodes/WebBox.tsx                  |   2 +-
 .../nodes/formattedText/DashDocCommentView.tsx     |   2 +-
 .../views/nodes/formattedText/EquationEditor.tsx   |   2 +-
 .../views/nodes/formattedText/EquationView.tsx     |   3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |   3 +-
 src/client/views/pdf/AnchorMenu.tsx                |   2 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |   6 +-
 src/client/views/search/CheckBox.scss              |  58 ---------
 src/client/views/search/CheckBox.tsx               | 130 --------------------
 src/client/views/search/CollectionFilters.scss     |  20 ----
 src/client/views/search/CollectionFilters.tsx      |  83 -------------
 src/client/views/search/IconBar.scss               |  10 --
 src/client/views/search/IconBar.tsx                |  73 -----------
 src/client/views/search/IconButton.scss            |  53 --------
 src/client/views/search/IconButton.tsx             | 118 ------------------
 src/client/views/search/NaviconButton.scss         |  69 -----------
 src/client/views/search/NaviconButton.tsx          |  37 ------
 src/client/views/search/SearchBox.scss             |   1 -
 src/client/views/search/SearchBox.tsx              |   3 +-
 src/client/views/search/ToggleBar.scss             |  41 -------
 src/client/views/search/ToggleBar.tsx              |  85 -------------
 src/fields/URLField.ts                             |   2 +-
 src/server/DashUploadUtils.ts                      |   4 +-
 76 files changed, 224 insertions(+), 1126 deletions(-)
 delete mode 100644 src/client/views/search/CheckBox.scss
 delete mode 100644 src/client/views/search/CheckBox.tsx
 delete mode 100644 src/client/views/search/CollectionFilters.scss
 delete mode 100644 src/client/views/search/CollectionFilters.tsx
 delete mode 100644 src/client/views/search/IconBar.scss
 delete mode 100644 src/client/views/search/IconBar.tsx
 delete mode 100644 src/client/views/search/IconButton.scss
 delete mode 100644 src/client/views/search/IconButton.tsx
 delete mode 100644 src/client/views/search/NaviconButton.scss
 delete mode 100644 src/client/views/search/NaviconButton.tsx
 delete mode 100644 src/client/views/search/ToggleBar.scss
 delete mode 100644 src/client/views/search/ToggleBar.tsx

(limited to 'src/client/util/CaptureManager.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 3a2bbf9a1..5f9475f23 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -7,7 +7,7 @@ import { DocumentType } from './client/documents/DocumentTypes';
 import { Colors } from './client/views/global/globalEnums';
 import { Message } from './server/Message';
 import * as Color from 'color';
-import { action, makeObservable } from 'mobx';
+import { action } from 'mobx';
 
 export namespace Utils {
     export let CLICK_TIME = 300;
@@ -56,10 +56,6 @@ export namespace Utils {
         return uuid.v5(seed, uuid.v5.URL);
     }
 
-    export function GenerateMongoId(id: string): string {
-        return id.length !== 36 ? Utils.GenerateDeterministicGuid(id) : id;
-    }
-
     export function GuestID() {
         return '__guest__';
     }
@@ -440,17 +436,6 @@ export namespace Utils {
         socket.on(message, (room: any) => handler(socket, room));
     }
 }
-export function copyProps(thing: { props: any; _props: any }, prevProps: any) {
-    Object.keys(prevProps).forEach(action(pkey =>
-        (prevProps)[pkey] !== (thing.props as any)[pkey] &&
-            ((thing._props as any)[pkey] = (thing.props as any)[pkey]))); // prettier-ignore
-}
-export function copyPropsFull(thing: { _prevProps: any; props: any; _props: any }) {
-    Object.keys(thing._prevProps).forEach(action(pkey => 
-        (thing._prevProps as any)[pkey] !== (thing.props as any)[pkey] && 
-            ((thing._props as any)[pkey] = (thing.props as any)[pkey]))); // prettier-ignore
-    thing._prevProps = thing.props;
-}
 
 export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?: (dup: any) => void): { omit: any; extract: any } {
     const omit: any = { ...obj };
diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx
index a224b84f4..11f16493f 100644
--- a/src/client/util/BranchingTrailManager.tsx
+++ b/src/client/util/BranchingTrailManager.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../fields/Doc';
@@ -15,13 +15,14 @@ export class BranchingTrailManager extends React.Component {
 
     constructor(props: any) {
         super(props);
+        makeObservable(this);
         if (!BranchingTrailManager.Instance) {
             BranchingTrailManager.Instance = this;
         }
     }
 
     setupUi = () => {
-        OverlayView.Instance.addWindow(<BranchingTrailManager></BranchingTrailManager>, { x: 100, y: 150, width: 1000, title: 'Branching Trail'});
+        OverlayView.Instance.addWindow(<BranchingTrailManager></BranchingTrailManager>, { x: 100, y: 150, width: 1000, title: 'Branching Trail' });
         // OverlayView.Instance.forceUpdate();
         console.log(OverlayView.Instance);
         // let hi = Docs.Create.TextDocument("beee", {
@@ -30,11 +31,10 @@ export class BranchingTrailManager extends React.Component {
         // })
         // hi.overlayX = 100;
         // hi.overlayY = 100;
-        
+
         // Doc.AddToMyOverlay(hi);
         console.log(DocumentManager._overlayViews);
     };
-    
 
     // stack of the history
     @observable private slideHistoryStack: String[] = [];
@@ -69,7 +69,7 @@ export class BranchingTrailManager extends React.Component {
         if (this.prevPresId === null || this.prevPresId !== presId) {
             Doc.UserDoc().isBranchingMode = true;
             this.setPrevPres(presId);
-            
+
             // REVERT THE SET
             const stringified = [presId, targetDocId].toString();
             if (this.containsSet.has([presId, targetDocId].toString())) {
@@ -98,7 +98,7 @@ export class BranchingTrailManager extends React.Component {
 
         const newStack = this.slideHistoryStack.slice(0, removeIndex);
         const removed = this.slideHistoryStack.slice(removeIndex);
-        
+
         this.setSlideHistoryStack(newStack);
 
         removed.forEach(info => this.containsSet.delete(info.toString()));
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index 271cf7495..071fc1ee9 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../fields/Doc';
@@ -20,6 +20,7 @@ export class CaptureManager extends React.Component<{}> {
 
     constructor(props: {}) {
         super(props);
+        makeObservable(this);
         CaptureManager.Instance = this;
     }
 
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index c8c93b7d0..90f65b648 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, IconButton, Size, Type } from 'browndash-components';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import Select from 'react-select';
@@ -17,6 +17,7 @@ import './GroupManager.scss';
 import { GroupMemberView } from './GroupMemberView';
 import { SettingsManager } from './SettingsManager';
 import { SharingManager, User } from './SharingManager';
+import { ObservableReactComponent } from '../views/ObservableReactComponent';
 
 /**
  * Interface for options for the react-select component
@@ -27,7 +28,7 @@ export interface UserOptions {
 }
 
 @observer
-export class GroupManager extends React.Component<{}> {
+export class GroupManager extends ObservableReactComponent<{}> {
     static Instance: GroupManager;
     @observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
     @observable private users: string[] = []; // list of users populated from the database.
@@ -41,6 +42,7 @@ export class GroupManager extends React.Component<{}> {
 
     constructor(props: Readonly<{}>) {
         super(props);
+        makeObservable(this);
         GroupManager.Instance = this;
     }
 
diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts
index 4dd2fcd35..865f8bc02 100644
--- a/src/client/util/PingManager.ts
+++ b/src/client/util/PingManager.ts
@@ -1,4 +1,4 @@
-import { action, observable, runInAction } from 'mobx';
+import { action, makeObservable, observable, runInAction } from 'mobx';
 import { Networking } from '../Network';
 import { CurrentUserUtils } from './CurrentUserUtils';
 export class PingManager {
@@ -33,6 +33,7 @@ export class PingManager {
     private _interval: NodeJS.Timeout | null = null;
     INTERVAL_SECONDS = 1;
     constructor() {
+        makeObservable(this);
         PingManager._instance = this;
         this._interval = setInterval(this.sendPing, this.INTERVAL_SECONDS * 1000);
     }
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index 495af6abd..f96d8a5df 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { MainViewModal } from '../views/MainViewModal';
@@ -23,6 +23,7 @@ export class RTFMarkup extends React.Component<{}> {
 
     constructor(props: {}) {
         super(props);
+        makeObservable(this);
         RTFMarkup.Instance = this;
     }
 
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index d99630f82..b881f18b4 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -1,4 +1,4 @@
-import { IReactionDisposer, observable, reaction } from 'mobx';
+import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { Doc, IdToDoc } from '../../fields/Doc';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
@@ -19,6 +19,7 @@ export class ReplayMovements {
         return ReplayMovements._instance;
     }
     constructor() {
+        makeObservable(this);
         // init the global instance
         ReplayMovements._instance = this;
 
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 4bd6647c0..07bbde36c 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -31,8 +31,7 @@ export class SelectionManager {
         this.Instance.SelectedSchemaDocument = doc;
     };
 
-    @action
-    public static SelectView = (docView: DocumentView | undefined, extendSelection: boolean): void => {
+    public static SelectView = action((docView: DocumentView | undefined, extendSelection: boolean): void => {
         if (!docView) this.DeselectAll();
         else if (!docView.SELECTED) {
             if (!extendSelection) this.DeselectAll();
@@ -40,16 +39,15 @@ export class SelectionManager {
             docView.SELECTED = true;
             docView._props.whenChildContentsActiveChanged(true);
         }
-    };
+    });
 
-    @action
-    public static DeselectView = (docView?: DocumentView): void => {
+    public static DeselectView = action((docView?: DocumentView): void => {
         if (docView && this.Instance.SelectedViews.includes(docView)) {
             docView.SELECTED = false;
             this.Instance.SelectedViews.splice(this.Instance.SelectedViews.indexOf(docView), 1);
             docView._props.whenChildContentsActiveChanged(false);
         }
-    };
+    });
 
     public static DeselectAll = (except?: Doc): void => {
         const found = this.Instance.SelectedViews.find(dv => dv.Document === except);
diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
index 7d5e0458d..c8df9182d 100644
--- a/src/client/util/ServerStats.tsx
+++ b/src/client/util/ServerStats.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { MainViewModal } from '../views/MainViewModal';
@@ -33,6 +33,7 @@ export class ServerStats extends React.Component<{}> {
 
     constructor(props: {}) {
         super(props);
+        makeObservable(this);
         ServerStats.Instance = this;
     }
 
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 7176b568e..ac7de9872 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, IconButton, Size, Type } from 'browndash-components';
 import { concat, intersection } from 'lodash';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import Select from 'react-select';
@@ -119,6 +119,7 @@ export class SharingManager extends React.Component<{}> {
 
     constructor(props: {}) {
         super(props);
+        makeObservable(this);
         SharingManager.Instance = this;
     }
 
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index 0e56ee1bc..0b197cf9a 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -1,4 +1,4 @@
-import { IReactionDisposer, observable, observe, reaction } from 'mobx';
+import { IReactionDisposer, makeObservable, observable, observe, reaction } from 'mobx';
 import { NumCast } from '../../fields/Types';
 import { Doc, DocListCast } from '../../fields/Doc';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
@@ -40,7 +40,7 @@ export class TrackMovements {
     constructor() {
         // init the global instance
         TrackMovements._instance = this;
-
+        makeObservable(this);
         // init the instance variables
         this.currentPresentation = TrackMovements.NULL_PRESENTATION;
         this.tracking = false;
diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx
index 738902a31..0c49aeed4 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -3,7 +3,7 @@ import * as uuid from 'uuid';
 import '.././SettingsManager.scss';
 import './ReportManager.scss';
 import ReactLoading from 'react-loading';
-import { action, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import { BsX, BsArrowsAngleExpand, BsArrowsAngleContract } from 'react-icons/bs';
 import { CgClose } from 'react-icons/cg';
 import { HiOutlineArrowLeft } from 'react-icons/hi';
@@ -103,6 +103,7 @@ export class ReportManager extends React.Component<{}> {
 
     constructor(props: {}) {
         super(props);
+        makeObservable(this);
         ReportManager.Instance = this;
 
         // initializing Github connection
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index e55bf24a1..8dcdd80e5 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -37,8 +37,8 @@ export class ContextMenu extends ObservableReactComponent<{}> {
 
     constructor(props: any) {
         super(props);
-        ContextMenu.Instance = this;
         makeObservable(this);
+        ContextMenu.Instance = this;
     }
 
     public setIgnoreEvents(ignore: boolean) {
diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx
index 0bdcdc303..e098bc361 100644
--- a/src/client/views/DictationOverlay.tsx
+++ b/src/client/views/DictationOverlay.tsx
@@ -1,4 +1,4 @@
-import { computed, observable, runInAction } from 'mobx';
+import { computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DictationManager } from '../util/DictationManager';
@@ -19,6 +19,7 @@ export class DictationOverlay extends React.Component {
 
     constructor(props: any) {
         super(props);
+        makeObservable(this);
         DictationOverlay.Instance = this;
     }
 
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 50ca3af06..47829d12a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -3,32 +3,26 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
-import { Cast, DocCast, NumCast } from '../../fields/Types';
-import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
-import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
-import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
-import { Docs, DocUtils } from '../documents/Documents';
+import { Cast, DocCast } from '../../fields/Types';
+import { DocUtils } from '../documents/Documents';
 import { DragManager } from '../util/DragManager';
 import { IsFollowLinkScript } from '../util/LinkFollower';
 import { SelectionManager } from '../util/SelectionManager';
 import { SharingManager } from '../util/SharingManager';
-import { undoBatch, UndoManager } from '../util/UndoManager';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { TabDocView } from './collections/TabDocView';
+import { UndoManager, undoBatch } from '../util/UndoManager';
 import './DocumentButtonBar.scss';
+import { ObservableReactComponent } from './ObservableReactComponent';
+import { TabDocView } from './collections/TabDocView';
 import { Colors } from './global/globalEnums';
 import { LinkPopup } from './linking/LinkPopup';
-import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere } from './nodes/DocumentView';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
-import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
 import { PinProps } from './nodes/trails';
-import { TemplateMenu } from './TemplateMenu';
-import * as React from 'react';
-import { ObservableReactComponent } from './ObservableReactComponent';
 // import * as higflyout from '@hig/flyout';
 // export const { anchorPoints } = higflyout;
 // export const Flyout = higflyout.default;
@@ -60,10 +54,10 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
     public static hasPushedHack = false;
     public static hasPulledHack = false;
 
-    constructor(props: { views: () => (DocumentView | undefined)[] }) {
+    constructor(props: any) {
         super(props);
-        DocumentButtonBar.Instance = this;
         makeObservable(this);
+        DocumentButtonBar.Instance = this;
     }
 
     public startPullOutcome = action((success: boolean) => {
@@ -115,103 +109,6 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
         return this._props.views()?.[0];
     }
 
-    @computed
-    get considerGoogleDocsPush() {
-        const targetDoc = this.view0?.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>
-        );
-    }
-
-    @computed
-    get considerGoogleDocsPull() {
-        const targetDoc = this.view0?.Document;
-        const dataDoc = targetDoc && Doc.GetProto(targetDoc);
-        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';
-            }
-        })();
-
-        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, data_useCors: false };
-                                googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
-                                dataDoc.googleDoc = googleDoc;
-                            }
-                            CollectionDockingView.AddSplit(googleDoc, OpenWhereMod.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={(() => {
-                            // prettier-ignore
-                            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>
-        );
-    }
     @observable subFollow = '';
     @computed
     get followLinkButton() {
@@ -579,8 +476,6 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
         if (!doc || !this.view0) return null;
 
         const isText = () => doc[this.view0!.LayoutFieldKey] instanceof RichTextField;
-        const considerPull = () => isText() && this.considerGoogleDocsPull;
-        const considerPush = () => isText() && this.considerGoogleDocsPush;
         return (
             <div className="documentButtonBar">
                 <div className="documentButtonBar-button">
@@ -608,14 +503,6 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                 <div className="documentButtonBar-button">{this.pinButton}</div>
                 <div className="documentButtonBar-button">{this.recordButton}</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>
             </div>
         );
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 836a184eb..73ac1b032 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -94,6 +94,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
     }
 
     componentDidUpdate(prevProps: Readonly<EditableProps>) {
+        super.componentDidUpdate(prevProps);
         if (this._editing && this._props.editing === false) {
             this._inputref?.value && this.finalizeEdit(this._inputref.value, false, true, false);
         } else if (this._props.editing !== undefined) {
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 214da5541..33cda6a62 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -1,6 +1,6 @@
 import * as React from 'react';
 import * as fitCurve from 'fit-curve';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction, toJS } from 'mobx';
 import { observer } from 'mobx-react';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
 import { Doc, Opt } from '../../fields/Doc';
@@ -31,12 +31,13 @@ import {
 } from './InkingStroke';
 import { checkInksToGroup } from './global/globalScripts';
 import { DocumentView } from './nodes/DocumentView';
+import { ObservableReactComponent } from './ObservableReactComponent';
 
 interface GestureOverlayProps {
     isActive: boolean;
 }
 @observer
-export class GestureOverlay extends React.Component<React.PropsWithChildren<GestureOverlayProps>> {
+export class GestureOverlay extends ObservableReactComponent<React.PropsWithChildren<GestureOverlayProps>> {
     static Instance: GestureOverlay;
     static Instances: GestureOverlay[] = [];
 
@@ -84,7 +85,7 @@ export class GestureOverlay extends React.Component<React.PropsWithChildren<Gest
 
     constructor(props: any) {
         super(props);
-
+        makeObservable(this);
         GestureOverlay.Instances.push(this);
     }
 
@@ -92,9 +93,9 @@ export class GestureOverlay extends React.Component<React.PropsWithChildren<Gest
         GestureOverlay.Instances.splice(GestureOverlay.Instances.indexOf(this), 1);
         GestureOverlay.Instance = GestureOverlay.Instances.lastElement();
     }
-    componentDidMount = () => {
+    componentDidMount() {
         GestureOverlay.Instance = this;
-    };
+    }
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
@@ -383,9 +384,9 @@ export class GestureOverlay extends React.Component<React.PropsWithChildren<Gest
         return GestureOverlay.getBounds(this._points);
     }
 
-    @computed get elements() {
+    get elements() {
         const selView = GestureOverlay.DownDocView;
-        const width = Number(ActiveInkWidth()) * NumCast(selView?.Document._freeform_scale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1);
+        const width = Number(ActiveInkWidth()) * NumCast(selView?.Document._freeform_scale, 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;
@@ -499,7 +500,7 @@ export class GestureOverlay extends React.Component<React.PropsWithChildren<Gest
 
     render() {
         return (
-            <div className="gestureOverlay-cont" style={{ pointerEvents: this.props.isActive ? 'all' : 'none' }} ref={this._overlayRef} onPointerDown={this.onPointerDown}>
+            <div className="gestureOverlay-cont" style={{ pointerEvents: this._props.isActive ? 'all' : 'none' }} ref={this._overlayRef} onPointerDown={this.onPointerDown}>
                 {this.showMobileInkOverlay ? <MobileInkOverlay /> : null}
                 {this.elements}
 
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 884c74f9b..69c663a3e 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -1,5 +1,5 @@
 import { Bezier } from 'bezier-js';
-import { action, observable, reaction } from 'mobx';
+import { action, makeObservable, observable, reaction } from 'mobx';
 import { Doc, NumListCast, Opt } from '../../fields/Doc';
 import { InkData, InkField, InkTool, PointData } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -25,6 +25,7 @@ export class InkStrokeProperties {
 
     constructor() {
         InkStrokeProperties._Instance = this;
+        makeObservable(this);
         reaction(
             () => this._controlButton,
             button => button && (Doc.ActiveTool = InkTool.None)
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index de2ce3189..98d1e58e5 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -1,7 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Toggle, ToggleType, Type } from 'browndash-components';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, FieldResult, Opt } from '../../fields/Doc';
@@ -56,6 +56,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
 
     constructor(props: any) {
         super(props);
+        makeObservable(this);
         if (LightboxView.Instance) console.log('SDFSFASFASFSALFKJD:SLFJS:LDFJKS:LFJS:LDJFL:SDFJL:SDJF:LSJ');
         LightboxView.Instance = this;
     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f65675792..636f8761f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -230,7 +230,7 @@ export class MainView extends ObservableReactComponent<{}> {
         document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener);
     }
 
-    constructor(props: Readonly<{}>) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         DocumentViewInternal.addDocTabFunc = MainView.addDocTabFunc_impl;
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 2e27d1f70..285476b14 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -6,7 +6,7 @@ import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData } from '../../field
 import { List } from '../../fields/List';
 import { NumCast } from '../../fields/Types';
 import { GetEffectiveAcl } from '../../fields/util';
-import { copyProps, unimplementedFunction, Utils } from '../../Utils';
+import { unimplementedFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import { DragManager } from '../util/DragManager';
 import { FollowLinkScript } from '../util/LinkFollower';
diff --git a/src/client/views/ObservableReactComponent.tsx b/src/client/views/ObservableReactComponent.tsx
index 2d8dc9af9..9b2b00903 100644
--- a/src/client/views/ObservableReactComponent.tsx
+++ b/src/client/views/ObservableReactComponent.tsx
@@ -1,8 +1,6 @@
-import { makeObservable, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import * as React from 'react';
-import { copyProps } from '../../Utils';
 import './AntimodeMenu.scss';
-export interface AntimodeMenuProps {}
 
 /**
  * This is an abstract class that serves as the base for a PDF-style or Marquee-style
@@ -10,12 +8,14 @@ export interface AntimodeMenuProps {}
  */
 export abstract class ObservableReactComponent<T> extends React.Component<T, {}> {
     @observable _props: React.PropsWithChildren<T>;
-    constructor(props: React.PropsWithChildren<T>) {
+    constructor(props: any) {
         super(props);
         this._props = props;
         makeObservable(this);
     }
     componentDidUpdate(prevProps: Readonly<T>): void {
-        copyProps(this, prevProps);
+        Object.keys(prevProps).forEach(action(pkey =>
+            (prevProps as any)[pkey] !== (this.props as any)[pkey] &&
+                ((this._props as any)[pkey] = (this.props as any)[pkey]))); // prettier-ignore
     }
 }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index c857c2d6b..2fcb5d12a 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -54,7 +54,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     private _widthUndo?: UndoManager.Batch;
 
     public static Instance: PropertiesView | undefined;
-    constructor(props: React.PropsWithChildren<PropertiesViewProps>) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         PropertiesView.Instance = this;
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 416162aeb..086f40e96 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -1,4 +1,4 @@
-import { action, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Opt } from '../../fields/Doc';
@@ -27,6 +27,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
 
     constructor(props: ScriptBoxProps) {
         super(props);
+        makeObservable(this);
         this._scriptText = props.initialText || '';
     }
 
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index 5f20bc745..acf0ecff4 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -35,13 +35,11 @@ export class ScriptingObjectDisplay extends ObservableReactComponent<ReplProps>
         const val = this._props.value;
         const proto = Object.getPrototypeOf(val);
         const name = (proto && proto.constructor && proto.constructor.name) || String(val);
-        const title = this._props.name ? (
+        const title = (
             <>
-                <b>{this._props.name} : </b>
+                {this.props.name ? <b>{this._props.name} : </b> : <></>}
                 {name}
             </>
-        ) : (
-            name
         );
         if (this.collapsed) {
             return (
@@ -86,31 +84,19 @@ export class ScriptingValueDisplay extends ObservableReactComponent<replValuePro
 
     render() {
         const val = this._props.name ? this._props.value[this._props.name] : this._props.value;
+        const title = (name: string) => (
+            <>
+                {this._props.name ? <b>{this._props.name} : </b> : <> </>}
+                {name}
+            </>
+        );
         if (typeof val === 'object') {
             return <ScriptingObjectDisplay scrollToBottom={this._props.scrollToBottom} value={val} name={this._props.name} />;
         } else if (typeof val === 'function') {
             const name = '[Function]';
-            const title = this._props.name ? (
-                <>
-                    <b>{this._props.name} : </b>
-                    {name}
-                </>
-            ) : (
-                name
-            );
-            return <div className="scriptingObject-leaf">{title}</div>;
-        } else {
-            const name = String(val);
-            const title = this._props.name ? (
-                <>
-                    <b>{this._props.name} : </b>
-                    {name}
-                </>
-            ) : (
-                name
-            );
-            return <div className="scriptingObject-leaf">{title}</div>;
+            return <div className="scriptingObject-leaf">{title('[Function]')}</div>;
         }
+        return <div className="scriptingObject-leaf">{title(String(val))}</div>;
     }
 }
 
@@ -254,7 +240,8 @@ export class ScriptingRepl extends ObservableReactComponent<{}> {
         ele && ele.scroll({ behavior: 'auto', top: ele.scrollHeight });
     }
 
-    componentDidUpdate() {
+    componentDidUpdate(prevProps: Readonly<{}>) {
+        super.componentDidUpdate(prevProps);
         if (this.shouldScroll) {
             this.shouldScroll = false;
             this.scrollToBottom();
diff --git a/src/client/views/animationtimeline/Region.tsx b/src/client/views/animationtimeline/Region.tsx
index abbc8203d..b09456cd7 100644
--- a/src/client/views/animationtimeline/Region.tsx
+++ b/src/client/views/animationtimeline/Region.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
@@ -185,6 +185,7 @@ export class Region extends React.Component<IProps> {
 
     constructor(props: any) {
         super(props);
+        makeObservable(this);
     }
     componentDidMount() {
         setTimeout(() => {
diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx
index 1769c41bd..97a571dc4 100644
--- a/src/client/views/animationtimeline/TimelineMenu.tsx
+++ b/src/client/views/animationtimeline/TimelineMenu.tsx
@@ -1,7 +1,7 @@
 import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faChartLine, faClipboard } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Utils } from '../../../Utils';
@@ -16,8 +16,9 @@ export class TimelineMenu extends React.Component {
     @observable private _y = 0;
     @observable private _currentMenu: JSX.Element[] = [];
 
-    constructor(props: Readonly<{}>) {
+    constructor(props: any) {
         super(props);
+        makeObservable(this);
         TimelineMenu.Instance = this;
     }
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 0d3ff8bba..97d5cfc70 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -63,10 +63,10 @@ export class CollectionDockingView extends CollectionSubView() {
     private _goldenLayout: any = null;
     static _highlightStyleSheet: any = addStyleSheet();
 
-    constructor(props: SubCollectionViewProps) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
-        if (this._props.renderDepth < 0) runInAction(() => (CollectionDockingView.Instance = this));
+        if (this._props.renderDepth < 0) CollectionDockingView.Instance = this;
         //Why is this here?
         (window as any).React = React;
         (window as any).ReactDOM = ReactDOM;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 3ade2ab56..443aa3f17 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -2,14 +2,14 @@ import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { Toggle, ToggleType, Type } from 'browndash-components';
-import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx';
+import { action, computed, Lambda, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { RichTextField } from '../../../fields/RichTextField';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -40,10 +40,11 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
 
     constructor(props: any) {
         super(props);
+        makeObservable(this);
         this.FieldKey = '';
         CollectionMenu.Instance = this;
         this._canFade = false; // don't let the inking menu fade away
-        runInAction(() => (this.Pinned = Cast(Doc.UserDoc()['menuCollections-pinned'], 'boolean', true)));
+        this.Pinned = Cast(Doc.UserDoc()['menuCollections-pinned'], 'boolean', true);
         this.jumpTo(300, 300);
     }
 
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index d37a942d0..fb8bc4da2 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -54,7 +54,7 @@ export enum TrimScope {
 }
 
 @observer
-export class CollectionStackedTimeline extends CollectionSubView<SubCollectionViewProps & CollectionStackedTimelineProps>() {
+export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
     @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
     @observable public static CurrentlyPlaying: DocumentView[];
     constructor(props: any) {
@@ -695,7 +695,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
     _lastTimecode: number;
     _disposer: IReactionDisposer | undefined;
 
-    constructor(props: React.PropsWithChildren<StackedTimelineAnchorProps>) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         this._lastTimecode = this._props.currentTimecode();
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 2302bfbc3..f9d575da2 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -58,7 +58,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
     @observable _heading = '';
     @observable _color = '';
 
-    constructor(props: CSVFieldColumnProps) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         this._heading = this._props.headingObject ? this._props.headingObject.heading : this._props.heading;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index f1ef52e60..0673b264b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -121,7 +121,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         switch (type) {
             default:
             case CollectionViewType.Freeform:     return <CollectionFreeFormView    key="collview" {...props} />;
-            case CollectionViewType.Docking:      return <CollectionDockingView     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} />;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 2eb13fd2f..783817b06 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -340,6 +340,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
         runInAction(() => TabDocView._allTabs.add(this));
     }
     componentDidUpdate(prevProps: Readonly<TabDocViewProps>) {
+        super.componentDidUpdate(prevProps);
         this._view && DocumentManager.Instance.AddView(this._view);
     }
 
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 52ecdfbec..a7705ea7e 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -296,6 +296,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
     }
 
     componentDidUpdate(prevProps: Readonly<TreeViewProps>) {
+        super.componentDidUpdate(prevProps);
         this._disposers.opening = reaction(
             () => this.treeViewOpen,
             open => !open && (this._renderCount = 20)
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
index 0d5fcdaeb..66aa29de0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -3,7 +3,6 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
 import './CollectionFreeFormView.scss';
-import { copyProps } from '../../../../Utils';
 
 /**
  * An Fsa Arc.  The first array element is a test condition function that will be observed.
@@ -83,7 +82,7 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent<Collec
         this.initState();
     }
     componentDidUpdate(prevProps: Readonly<CollectionFreeFormInfoStateProps>) {
-        copyProps(this, prevProps);
+        super.componentDidUpdate(prevProps);
         this.clearState();
         this.initState();
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 4d3752c02..5637f9aeb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -18,23 +18,19 @@ export interface CollectionFreeFormInfoUIProps {
 export class CollectionFreeFormInfoUI extends ObservableReactComponent<CollectionFreeFormInfoUIProps> {
     private _disposers: { [name: string]: IReactionDisposer } = {};
 
-    @observable _currState: infoState | undefined = undefined;
-    get currState() {
-        return this._currState!;
-    }
-    set currState(val) {
-        this._currState = val;
-    }
-
     constructor(props: any) {
         super(props);
         makeObservable(this);
         this.currState = this.setupStates();
     }
 
+    @observable _currState: infoState | undefined = undefined;
+    get currState() { return this._currState!; } // prettier-ignore
+    set currState(val) { runInAction(() => (this._currState = val)); } // prettier-ignore
+
     setCurrState = (state: infoState) => {
         if (state) {
-            runInAction(() => (this.currState = state));
+            this.currState = state;
             this.currState[StateEntryFunc]?.();
         }
     };
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 7aa68b0d9..79cc534dc 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { IconButton } from 'browndash-components';
-import { computed } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { unimplementedFunction } from '../../../../Utils';
@@ -18,8 +18,9 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
     public hideMarquee: () => void = unimplementedFunction;
     public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
     public isShown = () => this._opacity > 0;
-    constructor(props: Readonly<{}>) {
+    constructor(props: any) {
         super(props);
+        makeObservable(this);
         MarqueeOptionsMenu.Instance = this;
     }
 
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b951a4b17..9132c0436 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -127,7 +127,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
     private get totalRatioAllocation(): number | undefined {
         const layoutInfoLen = this.resolvedLayoutInformation.widthSpecifiers.length;
         if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
-            return this.props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this.props.Document._xMargin);
+            return this._props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this._props.Document._xMargin);
         }
     }
 
@@ -189,7 +189,7 @@ 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.NativeDimScaling?.() || 1), 0);
+                return this._props.ScreenToLocalTransform().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0);
             }
             offset += this.lookupPixels(candidate) + resizerWidth;
         }
@@ -223,8 +223,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                             if (this.childDocs.includes(d)) {
                                 if (dropInd > this.childDocs.indexOf(d)) dropInd--;
                             }
-                            Doc.RemoveDocFromList(this.dataDoc, this.props.fieldKey, d);
-                            Doc.AddDocToList(this.dataDoc, this.props.fieldKey, d, DocListCast(this.dataDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
+                            Doc.RemoveDocFromList(this.dataDoc, this._props.fieldKey, d);
+                            Doc.AddDocToList(this.dataDoc, this._props.fieldKey, d, DocListCast(this.dataDoc[this._props.fieldKey])[dropInd], undefined, dropInd === -1);
                         }
                     })
                 );
@@ -237,56 +237,56 @@ export class CollectionMulticolumnView extends CollectionSubView() {
     onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
+    isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
     isChildContentActive = () => {
-        const childDocsActive = this.props.childDocumentsActive?.() ?? this.Document.childDocumentsActive;
-        return this.props.isContentActive?.() === false || childDocsActive === false
+        const childDocsActive = this._props.childDocumentsActive?.() ?? this.Document.childDocumentsActive;
+        return this._props.isContentActive?.() === false || childDocsActive === false
             ? false //
-            : this.props.isDocumentActive?.() && childDocsActive
+            : this._props.isDocumentActive?.() && childDocsActive
               ? true
               : undefined;
     };
     getDisplayDoc = (childLayout: Doc) => {
         const width = () => this.lookupPixels(childLayout);
-        const height = () => this.props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
+        const height = () => this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
         const dxf = () =>
             this.lookupIndividualTransform(childLayout)
                 .translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin))
-                .scale(this.props.NativeDimScaling?.() || 1);
-        const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(childLayout.freeform_fitContentsToBox);
+                .scale(this._props.NativeDimScaling?.() || 1);
+        const shouldNotScale = () => this._props.fitContentsToBox?.() || BoolCast(childLayout.freeform_fitContentsToBox);
         return (
             <DocumentView
                 Document={childLayout}
                 TemplateDataDocument={childLayout.resolvedDataDoc as Doc}
-                styleProvider={this.props.styleProvider}
-                docViewPath={this.props.docViewPath}
-                LayoutTemplate={this.props.childLayoutTemplate}
-                LayoutTemplateString={this.props.childLayoutString}
-                renderDepth={this.props.renderDepth + 1}
+                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}
-                dragAction={(this.props.Document.childDragAction ?? this.props.childDragAction) as dropActionType}
+                dragAction={(this._props.Document.childDragAction ?? this._props.childDragAction) 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={childLayout.layout_fitWidth || this.props.childHideResizeHandles ? true : false}
-                hideDecorationTitle={this.props.childHideDecorationTitle}
-                fitContentsToBox={this.props.fitContentsToBox}
-                focus={this.props.focus}
+                isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+                hideResizeHandles={childLayout.layout_fitWidth || this._props.childHideResizeHandles ? true : false}
+                hideDecorationTitle={this._props.childHideDecorationTitle}
+                fitContentsToBox={this._props.fitContentsToBox}
+                focus={this._props.focus}
                 childFilters={this.childDocFilters}
                 childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
-                dontRegisterView={this.props.dontRegisterView}
-                addDocument={this.props.addDocument}
-                moveDocument={this.props.moveDocument}
-                removeDocument={this.props.removeDocument}
-                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
+                dontRegisterView={this._props.dontRegisterView}
+                addDocument={this._props.addDocument}
+                moveDocument={this._props.moveDocument}
+                removeDocument={this._props.removeDocument}
+                whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+                addDocTab={this._props.addDocTab}
+                pinToPres={this._props.pinToPres}
                 bringToFront={returnFalse}
                 dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
             />
@@ -306,16 +306,16 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 <Tooltip title={'Tab: ' + StrCast(layout.title)}>
                     <div className="document-wrapper" key={'wrapper' + i} style={{ flexDirection: 'column', width: this.lookupPixels(layout) }}>
                         {this.getDisplayDoc(layout)}
-                        <Button tooltip="Remove document from header bar" icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={undoable(e => this.props.removeDocument?.(layout), 'close doc')} color={SettingsManager.userColor} />
+                        <Button tooltip="Remove document from header bar" icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={undoable(e => this._props.removeDocument?.(layout), 'close doc')} color={SettingsManager.userColor} />
                         <WidthLabel layout={layout} collectionDoc={this.Document} />
                     </div>
                 </Tooltip>,
                 <ResizeBar
                     width={resizerWidth}
                     key={'resizer' + i}
-                    styleProvider={this.props.styleProvider}
-                    isContentActive={this.props.isContentActive}
-                    select={this.props.select}
+                    styleProvider={this._props.styleProvider}
+                    isContentActive={this._props.isContentActive}
+                    select={this._props.select}
                     columnUnitLength={this.getColumnUnitLength}
                     toLeft={layout}
                     toRight={childLayoutPairs[i + 1]?.layout}
@@ -332,12 +332,12 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 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),
+                    width: `calc(100% - ${2 * NumCast(this.Document._xMargin)}px)`,
+                    height: `calc(100% - ${2 * NumCast(this.Document._yMargin)}px)`,
+                    marginLeft: NumCast(this.Document._xMargin),
+                    marginRight: NumCast(this.Document._xMargin),
+                    marginTop: NumCast(this.Document._yMargin),
+                    marginBottom: NumCast(this.Document._yMargin),
                 }}>
                 {this.contents}
             </div>
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index f1c6c6e1e..cf65bf12a 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -123,7 +123,7 @@ export class CollectionMultirowView extends CollectionSubView() {
     private get totalRatioAllocation(): number | undefined {
         const layoutInfoLen = this.resolvedLayoutInformation.heightSpecifiers.length;
         if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
-            return this.props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this.props.Document._yMargin);
+            return this._props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this._props.Document._yMargin);
         }
     }
 
@@ -185,7 +185,7 @@ 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.NativeDimScaling?.() || 1));
+                return this._props.ScreenToLocalTransform().translate(0, -offset / (this._props.NativeDimScaling?.() || 1));
             }
             offset += this.lookupPixels(candidate) + resizerHeight;
         }
@@ -219,8 +219,8 @@ export class CollectionMultirowView extends CollectionSubView() {
                             if (this.childDocs.includes(d)) {
                                 if (dropInd > this.childDocs.indexOf(d)) dropInd--;
                             }
-                            Doc.RemoveDocFromList(this.dataDoc, this.props.fieldKey, d);
-                            Doc.AddDocToList(this.dataDoc, this.props.fieldKey, d, DocListCast(this.dataDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
+                            Doc.RemoveDocFromList(this.dataDoc, this._props.fieldKey, d);
+                            Doc.AddDocToList(this.dataDoc, this._props.fieldKey, d, DocListCast(this.dataDoc[this._props.fieldKey])[dropInd], undefined, dropInd === -1);
                         }
                     })
                 );
@@ -233,32 +233,32 @@ export class CollectionMultirowView extends CollectionSubView() {
     onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
+    isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
     isChildContentActive = () => {
-        const childDocsActive = this.props.childDocumentsActive?.() ?? this.Document.childDocumentsActive;
-        return this.props.isContentActive?.() === false || childDocsActive === false
+        const childDocsActive = this._props.childDocumentsActive?.() ?? this.Document.childDocumentsActive;
+        return this._props.isContentActive?.() === false || childDocsActive === false
             ? false //
-            : this.props.isDocumentActive?.() && childDocsActive
+            : this._props.isDocumentActive?.() && childDocsActive
               ? true
               : undefined;
     };
     getDisplayDoc = (layout: Doc) => {
         const height = () => this.lookupPixels(layout);
-        const width = () => this.props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
+        const width = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
         const dxf = () =>
             this.lookupIndividualTransform(layout)
                 .translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin))
-                .scale(this.props.NativeDimScaling?.() || 1);
-        const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
+                .scale(this._props.NativeDimScaling?.() || 1);
+        const shouldNotScale = () => this._props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
         return (
             <DocumentView
                 Document={layout}
                 TemplateDataDocument={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}
+                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}
@@ -267,22 +267,22 @@ export class CollectionMultirowView extends CollectionSubView() {
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={dxf}
                 isContentActive={this.isChildContentActive}
-                isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
-                hideResizeHandles={layout.layout_fitWidth || this.props.childHideResizeHandles ? true : false}
-                hideDecorationTitle={this.props.childHideDecorationTitle}
-                fitContentsToBox={this.props.fitContentsToBox}
-                dragAction={this.props.childDragAction}
-                focus={this.props.focus}
+                isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+                hideResizeHandles={layout.layout_fitWidth || this._props.childHideResizeHandles ? true : false}
+                hideDecorationTitle={this._props.childHideDecorationTitle}
+                fitContentsToBox={this._props.fitContentsToBox}
+                dragAction={this._props.childDragAction}
+                focus={this._props.focus}
                 childFilters={this.childDocFilters}
                 childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
-                dontRegisterView={this.props.dontRegisterView}
-                addDocument={this.props.addDocument}
-                moveDocument={this.props.moveDocument}
-                removeDocument={this.props.removeDocument}
-                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
+                dontRegisterView={this._props.dontRegisterView}
+                addDocument={this._props.addDocument}
+                moveDocument={this._props.moveDocument}
+                removeDocument={this._props.removeDocument}
+                whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+                addDocTab={this._props.addDocTab}
+                pinToPres={this._props.pinToPres}
                 bringToFront={returnFalse}
                 dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
             />
@@ -305,8 +305,8 @@ export class CollectionMultirowView extends CollectionSubView() {
                 </div>,
                 <ResizeBar
                     height={resizerHeight}
-                    styleProvider={this.props.styleProvider}
-                    isContentActive={this.props.isContentActive}
+                    styleProvider={this._props.styleProvider}
+                    isContentActive={this._props.isContentActive}
                     key={'resizer' + i}
                     columnUnitLength={this.getRowUnitLength}
                     toTop={layout}
@@ -323,12 +323,12 @@ export class CollectionMultirowView extends CollectionSubView() {
             <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),
+                    width: `calc(100% - ${2 * NumCast(this.Document._xMargin)}px)`,
+                    height: `calc(100% - ${2 * NumCast(this.Document._yMargin)}px)`,
+                    marginLeft: NumCast(this.Document._xMargin),
+                    marginRight: NumCast(this.Document._xMargin),
+                    marginTop: NumCast(this.Document._yMargin),
+                    marginBottom: NumCast(this.Document._yMargin),
                 }}
                 ref={this.createDashEventsTarget}>
                 {this.contents}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c97c879af..548734dab 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -83,7 +83,8 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti
     PanelWidth = ()        => this._props.autoDim ? this._props.PanelWidth?.() : this.Width; // prettier-ignore
     PanelHeight = ()       => this._props.autoDim ? this._props.PanelHeight?.() : this.Height; // prettier-ignore
 
-    componentDidUpdate() {
+    componentDidUpdate(prevProps: Readonly<React.PropsWithChildren<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>>) {
+        super.componentDidUpdate(prevProps);
         this.WrapperKeys.forEach(action(keys => ((this as any)[keys.upper] = (this.props as any)[keys.lower])));
     }
     render() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 343f770d5..ab413e6f2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1334,7 +1334,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
         return this._selected;
     }
     public set SELECTED(val) {
-        this._selected = val;
+        runInAction(() => (this._selected = val));
     }
     @observable public static Interacting = false;
     @observable public static LongPress = false;
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 29bffb583..c26579e66 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -24,7 +24,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
     _plotId = '';
     _plotEle: any;
 
-    constructor(props: React.PropsWithChildren<FieldViewProps>) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         this._plotId = 'graph' + FunctionPlotBox.GraphCount++;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index b1ccd38ba..876f13370 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -55,8 +55,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     private _marqueeref = React.createRef<MarqueeAnnotator>();
     @observable _curSuffix = '';
 
-    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
+    constructor(props: any) {
         super(props);
+        makeObservable(this);
         this._props.setContentView?.(this);
     }
 
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index fa7a55bc7..d0a9f10b4 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -5,7 +5,7 @@ import { observer } from 'mobx-react';
 import wiki from 'wikijs';
 import { Doc, Opt } from '../../../fields/Doc';
 import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
-import { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -82,7 +82,7 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps
         this.updateHref();
     }
     componentDidUpdate(prevProps: Readonly<LinkDocPreviewProps>) {
-        copyProps(this, prevProps);
+        super.componentDidUpdate(prevProps);
         if (prevProps.linkSrc !== this._props.linkSrc || prevProps.linkDoc !== this._props.linkDoc || prevProps.hrefs !== this._props.hrefs) this.init();
     }
     componentDidMount() {
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index b458e5a28..66c4dc7b8 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
+import { IReactionDisposer, ObservableMap, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils';
@@ -34,8 +34,9 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         return this._left > 0;
     }
 
-    constructor(props: Readonly<{}>) {
+    constructor(props: any) {
         super(props);
+        makeObservable(this);
         MapAnchorMenu.Instance = this;
         MapAnchorMenu.Instance._canFade = false;
     }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 213f88177..733febd2d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -58,7 +58,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url;
     }
 
-    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         const nw = Doc.NativeWidth(this.Document, this.dataDoc) || 927;
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
index c498a58d6..e75b1ab6f 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
@@ -202,7 +202,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
         ];
     }
 
-    componentDidUpdate() {
+    componentDidUpdate(prevProps: Readonly<React.PropsWithChildren<FieldViewProps>>) {
+        super.componentDidUpdate(prevProps);
         if (this.xMax !== this.props.PanelWidth() * 0.6 || this.yMax != this.props.PanelHeight()) {
             this.xMax = this.props.PanelWidth() * 0.6;
             this.yMax = this.props.PanelHeight();
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
index 99333991f..f5077a07e 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -1,4 +1,4 @@
-import { computed, IReactionDisposer, reaction } from 'mobx';
+import { computed, IReactionDisposer, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import './PhysicsSimulationBox.scss';
 import * as React from 'react';
@@ -93,6 +93,7 @@ interface IState {
 export default class Weight extends React.Component<IWeightProps, IState> {
     constructor(props: any) {
         super(props);
+        makeObservable(this);
         this.state = {
             angleLabel: 0,
             clickPositionX: 0,
diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx
index 191877cb5..061a46f03 100644
--- a/src/client/views/nodes/RadialMenu.tsx
+++ b/src/client/views/nodes/RadialMenu.tsx
@@ -1,5 +1,5 @@
 import * as React from 'react';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import './RadialMenu.scss';
 import { RadialMenuItem, RadialMenuProps } from './RadialMenuItem';
@@ -9,9 +9,9 @@ export class RadialMenu extends React.Component {
     static Instance: RadialMenu;
     static readonly buffer = 20;
 
-    constructor(props: Readonly<{}>) {
+    constructor(props: any) {
         super(props);
-
+        makeObservable(this);
         RadialMenu.Instance = this;
     }
 
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index a7ff8ff8f..d5d31b407 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 // import { Canvas } from '@react-three/fiber';
-import { computed, observable, runInAction } from 'mobx';
+import { computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 // import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
 import { DateField } from '../../../fields/DateField';
@@ -123,14 +123,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     constructor(props: any) {
         super(props);
-        if (this.dataDoc.videoWall) {
-            this.layoutDoc.nativeWidth = undefined;
-            this.layoutDoc.nativeHeight = undefined;
-            this.layoutDoc.popOff = 0;
-            this.layoutDoc.popOut = 1;
-        } else {
-            this.setupDictation();
-        }
+        makeObservable(this);
+        this.setupDictation();
     }
     getAnchor = (addAsAnnotation: boolean) => {
         const startTime = Cast(this.layoutDoc._layout_currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
@@ -173,7 +167,6 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     };
 
     @computed get content() {
-        if (this.layoutDoc.videoWall) return null;
         return (
             <video
                 className={'videoBox-content'}
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 127edaed7..7e7eaee45 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,5 +1,5 @@
 let ReactTextareaAutocomplete = require('@webscopeio/react-textarea-autocomplete').default;
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../../fields/Doc';
@@ -60,6 +60,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
 
     constructor(props: any) {
         super(props);
+        makeObservable(this);
         if (!this.compileParams.length) {
             const params = ScriptCast(this.dataDoc[this.props.fieldKey])?.script.options.params as { [key: string]: any };
             if (params) {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ebe86318d..df73dffe4 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -68,7 +68,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     private _playRegionTimer: any = null; // timeout for playback
     private _controlsFadeTimer: any = null; // timeout for controls fade
 
-    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         this._props.setContentView?.(this);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2f92f1edb..57045e2af 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -98,7 +98,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return Cast(this.Document[this._props.fieldKey], WebField)?.url;
     }
 
-    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         this._webUrl = this._url; // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index d6b053c8e..b7d2a24c2 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -54,7 +54,7 @@ interface IDashDocCommentViewInternal {
 }
 
 export class DashDocCommentViewInternal extends React.Component<IDashDocCommentViewInternal> {
-    constructor(props: IDashDocCommentViewInternal) {
+    constructor(props: any) {
         super(props);
         this.onPointerLeaveCollapsed = this.onPointerLeaveCollapsed.bind(this);
         this.onPointerEnterCollapsed = this.onPointerEnterCollapsed.bind(this);
diff --git a/src/client/views/nodes/formattedText/EquationEditor.tsx b/src/client/views/nodes/formattedText/EquationEditor.tsx
index 07c70af77..b4102e08e 100644
--- a/src/client/views/nodes/formattedText/EquationEditor.tsx
+++ b/src/client/views/nodes/formattedText/EquationEditor.tsx
@@ -44,7 +44,7 @@ class EquationEditor extends Component<EquationEditorProps> {
     // Element needs to be in the class format and thus requires a constructor. The steps that are run
     // in the constructor is to make sure that React can succesfully communicate with the equation
     // editor.
-    constructor(props: EquationEditorProps) {
+    constructor(props: any) {
         super(props);
 
         this.element = createRef();
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 5ee5d25c3..331ed1980 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -8,6 +8,7 @@ import { StrCast } from '../../../../fields/Types';
 import './DashFieldView.scss';
 import { FormattedTextBox } from './FormattedTextBox';
 import * as React from 'react';
+import { AnyArray } from 'mongoose';
 
 export class EquationView {
     dom: HTMLDivElement; // container for label and value
@@ -63,7 +64,7 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
     _fieldKey: string;
     _ref: React.RefObject<EquationEditor> = React.createRef();
 
-    constructor(props: IEquationViewInternal) {
+    constructor(props: any) {
         super(props);
         this._fieldKey = this.props.fieldKey;
         this._textBoxDoc = this.props.tbox.props.Document;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 997c3f86d..ad2fab8b0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -208,7 +208,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return url.startsWith(document.location.origin) ? new URL(url).pathname.split('doc/').lastElement() : ''; // docId
     }
 
-    constructor(props: React.PropsWithChildren<FieldViewProps & FormattedTextBoxProps>) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         FormattedTextBox.Instance = this;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 4ff816812..17993d88e 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, FieldResult, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
 import { Animation } from '../../../../fields/DocSymbols';
@@ -72,6 +72,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     constructor(props: any) {
         super(props);
+        makeObservable(this);
         if (!PresBox.navigateToDocScript) {
             PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(this.presentation_targetDoc, self)')!;
         }
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index a8c83ded6..d0688c338 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -23,7 +23,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     private _commentRef = React.createRef<HTMLDivElement>();
     private _cropRef = React.createRef<HTMLDivElement>();
 
-    constructor(props: AntimodeMenuProps) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
         AnchorMenu.Instance = this;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 0cda36509..cdd0cb95a 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react';
 import './GPTPopup.scss';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import ReactLoading from 'react-loading';
 import { TypeAnimation } from 'react-type-animation';
@@ -14,6 +14,7 @@ import { AnchorMenu } from '../AnchorMenu';
 import { gptImageCall } from '../../../apis/gpt/GPT';
 import { Networking } from '../../../Network';
 import { Utils } from '../../../../Utils';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
 
 export enum GPTPopupMode {
     SUMMARY,
@@ -24,7 +25,7 @@ export enum GPTPopupMode {
 interface GPTPopupProps {}
 
 @observer
-export class GPTPopup extends React.Component<GPTPopupProps> {
+export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
     static Instance: GPTPopup;
 
     @observable
@@ -177,6 +178,7 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
 
     constructor(props: GPTPopupProps) {
         super(props);
+        makeObservable(this);
         GPTPopup.Instance = this;
     }
 
diff --git a/src/client/views/search/CheckBox.scss b/src/client/views/search/CheckBox.scss
deleted file mode 100644
index 4892facbc..000000000
--- a/src/client/views/search/CheckBox.scss
+++ /dev/null
@@ -1,58 +0,0 @@
-@import '../global/globalCssVariables.module.scss';
-
-.checkboxfilter {
-    display: flex;
-    margin-top: 0px;
-    padding: 3px;
-
-    .outer {
-        display: flex;
-        position: relative;
-        justify-content: center;
-        align-items: center;
-        margin-top: 0px;
-
-        .check-container:hover ~ .check-box {
-            background-color: $medium-blue;
-        }
-
-        .check-container {
-            width: 40px;
-            height: 40px;
-            position: absolute;
-            z-index: 1000;
-
-            .checkmark {
-                z-index: 1000;
-                position: absolute;
-                fill-opacity: 0;
-                stroke-width: 4px;
-                // stroke: white;
-                stroke: gray;
-            }
-        }
-
-        .check-box {
-            z-index: 900;
-            position: relative;
-            height: 20px;
-            width: 20px;
-            overflow: visible;
-            background-color: transparent;
-            border-style: solid;
-            border-color: $medium-gray;
-            border-width: 2px;
-            -webkit-transition: all 0.2s ease-in-out;
-            -moz-transition: all 0.2s ease-in-out;
-            -o-transition: all 0.2s ease-in-out;
-            transition: all 0.2s ease-in-out;
-        }
-    }
-
-    .checkbox-title {
-        display: flex;
-        align-items: center;
-        text-transform: capitalize;
-        margin-left: 15px;
-    }
-}
diff --git a/src/client/views/search/CheckBox.tsx b/src/client/views/search/CheckBox.tsx
deleted file mode 100644
index 0a1e551ec..000000000
--- a/src/client/views/search/CheckBox.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import * as React from 'react';
-import { observer } from 'mobx-react';
-import { observable, action, runInAction, IReactionDisposer, reaction } from 'mobx';
-import "./CheckBox.scss";
-
-interface CheckBoxProps {
-    originalStatus: boolean;
-    updateStatus(newStatus: boolean): void;
-    title: string;
-    parent: any;
-    numCount: number;
-    default: boolean;
-}
-
-@observer
-export class CheckBox extends React.Component<CheckBoxProps>{
-    // true = checked, false = unchecked
-    @observable private _status: boolean;
-    // @observable private uncheckTimeline: anime.AnimeTimelineInstance;
-    // @observable private checkTimeline: anime.AnimeTimelineInstance;
-    @observable private checkRef: any;
-    @observable private _resetReaction?: IReactionDisposer;
-
-
-    constructor(props: CheckBoxProps) {
-        super(props);
-        this._status = this.props.originalStatus;
-        this.checkRef = React.createRef();
-
-        // this.checkTimeline = anime.timeline({
-        //     loop: false,
-        //     autoplay: false,
-        //     direction: "normal",
-        // }); this.uncheckTimeline = anime.timeline({
-        //     loop: false,
-        //     autoplay: false,
-        //     direction: "normal",
-        // });
-    }
-
-    // componentDidMount = () => {
-    //     this.uncheckTimeline.add({
-    //         targets: this.checkRef.current,
-    //         easing: "easeInOutQuad",
-    //         duration: 500,
-    //         opacity: 0,
-    //     });
-    //     this.checkTimeline.add({
-    //         targets: this.checkRef.current,
-    //         easing: "easeInOutQuad",
-    //         duration: 500,
-    //         strokeDashoffset: [anime.setDashoffset, 0],
-    //         opacity: 1
-    //     });
-
-    //     if (this.props.originalStatus) {
-    //         this.checkTimeline.play();
-    //         this.checkTimeline.restart();
-    //     }
-
-    //     this._resetReaction = reaction(
-    //         () => this.props.parent._resetBoolean,
-    //         () => {
-    //             if (this.props.parent._resetBoolean) {
-    //                 runInAction(() => {
-    //                     this.reset();
-    //                     this.props.parent._resetCounter++;
-    //                     if (this.props.parent._resetCounter === this.props.numCount) {
-    //                         this.props.parent._resetCounter = 0;
-    //                         this.props.parent._resetBoolean = false;
-    //                     }
-    //                 });
-    //             }
-    //         },
-    //     );
-    // }
-
-    // @action.bound
-    // reset() {
-    //     if (this.props.default) {
-    //         if (!this._status) {
-    //             this._status = true;
-    //             this.checkTimeline.play();
-    //             this.checkTimeline.restart();
-    //         }
-    //     }
-    //     else {
-    //         if (this._status) {
-    //             this._status = false;
-    //             this.uncheckTimeline.play();
-    //             this.uncheckTimeline.restart();
-    //         }
-    //     }
-
-    //     this.props.updateStatus(this.props.default);
-    // }
-
-    @action.bound
-    onClick = () => {
-        // if (this._status) {
-        //     this.uncheckTimeline.play();
-        //     this.uncheckTimeline.restart();
-        // }
-        // else {
-        //     this.checkTimeline.play();
-        //     this.checkTimeline.restart();
-
-        // }
-        // this._status = !this._status;
-        // this.props.updateStatus(this._status);
-
-    }
-
-    render() {
-        return (
-            <div className="checkboxfilter" onClick={this.onClick}>
-                <div className="outer">
-                    <div className="check-container">
-                        <svg viewBox="0 12 40 40">
-                            <path ref={this.checkRef} className="checkmark" d="M14.1 27.2l7.1 7.2 16.7-18" />
-                        </svg>
-                    </div>
-                    <div className="check-box" />
-                </div>
-                <div className="checkbox-title">{this.props.title}</div>
-            </div>
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/views/search/CollectionFilters.scss b/src/client/views/search/CollectionFilters.scss
deleted file mode 100644
index 6105df570..000000000
--- a/src/client/views/search/CollectionFilters.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-@import '../global/globalCssVariables.module.scss';
-
-.collection-filters {
-    display: flex;
-    flex-direction: row;
-    width: 100%;
-
-    &.main {
-        width: 47%;
-        float: left;
-    }
-
-    &.optional {
-        width: 60%;
-        display: grid;
-        grid-template-columns: 50% 50%;
-        float: right;
-        opacity: 0;
-    }
-}
diff --git a/src/client/views/search/CollectionFilters.tsx b/src/client/views/search/CollectionFilters.tsx
deleted file mode 100644
index 48d0b2ddb..000000000
--- a/src/client/views/search/CollectionFilters.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import * as React from 'react';
-import { observable, action } from 'mobx';
-import { CheckBox } from './CheckBox';
-import "./CollectionFilters.scss";
-import * as anime from 'animejs';
-
-interface CollectionFilterProps {
-    collectionStatus: boolean;
-    updateCollectionStatus(val: boolean): void;
-    collectionSelfStatus: boolean;
-    updateSelfCollectionStatus(val: boolean): void;
-    collectionParentStatus: boolean;
-    updateParentCollectionStatus(val: boolean): void;
-}
-
-export class CollectionFilters extends React.Component<CollectionFilterProps> {
-
-    static Instance: CollectionFilters;
-
-    @observable public _resetBoolean = false;
-    @observable public _resetCounter: number = 0;
-    
-    @observable private _collectionsSelected = this.props.collectionStatus;
-    @observable private _timeline: anime.AnimeTimelineInstance;
-    @observable private _ref: any;
-
-    constructor(props: CollectionFilterProps) {
-        super(props);
-        CollectionFilters.Instance = this;
-        this._ref = React.createRef();
-
-        this._timeline = anime.timeline({
-            loop: false,
-            autoplay: false,
-            direction: "reverse",
-        });
-    }
-
-    componentDidMount = () => {
-        this._timeline.add({
-            targets: this._ref.current,
-            easing: "easeInOutQuad",
-            duration: 500,
-            opacity: 1,
-        });
-
-        if (this._collectionsSelected) {
-            this._timeline.play();
-            this._timeline.reverse();
-        }
-    }
-
-    @action.bound
-    resetCollectionFilters() { this._resetBoolean = true; }
-
-    @action.bound
-    updateColStat(val: boolean) {
-        this.props.updateCollectionStatus(val);
-
-        if (this._collectionsSelected !== val) {
-            this._timeline.play();
-            this._timeline.reverse();
-        }
-
-        this._collectionsSelected = val;
-    }
-
-    render() {
-        return (
-            <div>
-                <div className="collection-filters">
-                    <div className="collection-filters main">
-                        <CheckBox default={false} title={"limit to current collection"} parent={this} numCount={3} updateStatus={this.updateColStat} originalStatus={this.props.collectionStatus} />
-                    </div>
-                    <div className="collection-filters optional" ref={this._ref}>
-                        <CheckBox default={true} title={"Search in self"} parent={this} numCount={3} updateStatus={this.props.updateSelfCollectionStatus} originalStatus={this.props.collectionSelfStatus} />
-                        <CheckBox default={true} title={"Search in parent"} parent={this} numCount={3} updateStatus={this.props.updateParentCollectionStatus} originalStatus={this.props.collectionParentStatus} />
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/search/IconBar.scss b/src/client/views/search/IconBar.scss
deleted file mode 100644
index 05aa099f7..000000000
--- a/src/client/views/search/IconBar.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-@import '../global/globalCssVariables.module.scss';
-
-.icon-bar {
-    display: flex;
-    flex-wrap: wrap;
-    justify-content: space-evenly;
-    height: auto;
-    width: 100%;
-    flex-direction: row-reverse;
-}
diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx
deleted file mode 100644
index 540c1b5e1..000000000
--- a/src/client/views/search/IconBar.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { action, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { DocumentType } from '../../documents/DocumentTypes';
-import './IconBar.scss';
-import { IconButton } from './IconButton';
-import './IconButton.scss';
-
-export interface IconBarProps {
-    setIcons: (icons: string[]) => void;
-}
-
-@observer
-export class IconBar extends React.Component<IconBarProps> {
-    public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB, DocumentType.MAP];
-
-    @observable private _icons: string[] = this._allIcons;
-
-    static Instance: IconBar;
-
-    @observable public _resetClicked: boolean = false;
-    @observable public _selectAllClicked: boolean = false;
-    @observable public _reset: number = 0;
-    @observable public _select: number = 0;
-
-    @action.bound
-    updateIcon(newArray: string[]) {
-        this._icons = newArray;
-        this.props.setIcons?.(this._icons);
-    }
-
-    @action.bound
-    getIcons(): string[] {
-        return this._icons;
-    }
-
-    constructor(props: any) {
-        super(props);
-        IconBar.Instance = this;
-    }
-
-    @action.bound
-    getList(): string[] {
-        return this.getIcons();
-    }
-
-    @action.bound
-    updateList(newList: string[]) {
-        this.updateIcon(newList);
-    }
-
-    @action.bound
-    resetSelf = () => {
-        this._resetClicked = true;
-        this.updateList([]);
-    };
-
-    @action.bound
-    selectAll = () => {
-        this._selectAllClicked = true;
-        this.updateList(this._allIcons);
-    };
-
-    render() {
-        return (
-            <div className="icon-bar">
-                {this._allIcons.map((type: string) => (
-                    <IconButton key={type.toString()} type={type} />
-                ))}
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/search/IconButton.scss b/src/client/views/search/IconButton.scss
deleted file mode 100644
index d2a2ea369..000000000
--- a/src/client/views/search/IconButton.scss
+++ /dev/null
@@ -1,53 +0,0 @@
-@import '../global/globalCssVariables.module.scss';
-
-.type-outer {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    width: 30px;
-
-    .type-icon {
-        height: 30px;
-        width: 30px;
-        color: $white;
-        // background-color: rgb(194, 194, 197);
-        background-color: gray;
-        border-radius: 50%;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        -webkit-transition: all 0.2s ease-in-out;
-        -moz-transition: all 0.2s ease-in-out;
-        -o-transition: all 0.2s ease-in-out;
-        transition: all 0.2s ease-in-out;
-        font-size: 2em;
-
-        .fontawesome-icon {
-            height: 15px;
-            width: 15px;
-        }
-    }
-
-    .filter-description {
-        text-transform: capitalize;
-        font-size: 10;
-        text-align: center;
-        height: 15px;
-        margin-top: 5px;
-        opacity: 1;
-        -webkit-transition: all 0.2s ease-in-out;
-        -moz-transition: all 0.2s ease-in-out;
-        -o-transition: all 0.2s ease-in-out;
-        transition: all 0.2s ease-in-out;
-    }
-
-    .type-icon:hover {
-        transform: scale(1.1);
-        background-color: $medium-blue;
-        opacity: 1;
-
-        + .filter-description {
-            opacity: 1;
-        }
-    }
-}
diff --git a/src/client/views/search/IconButton.tsx b/src/client/views/search/IconButton.tsx
deleted file mode 100644
index 20084b64d..000000000
--- a/src/client/views/search/IconButton.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import * as _ from 'lodash';
-import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { DocumentType } from '../../documents/DocumentTypes';
-import '../global/globalCssVariables.module.scss';
-import { IconBar } from './IconBar';
-import './IconButton.scss';
-import './SearchBox.scss';
-
-interface iconButtonProps {
-    type: string;
-}
-
-@observer
-export class IconButton extends React.Component<iconButtonProps> {
-    @observable private _isSelected: boolean = IconBar.Instance.getIcons().indexOf(this.props.type) !== -1;
-    @observable private _hover = false;
-    private _resetReaction?: IReactionDisposer;
-    private _selectAllReaction?: IReactionDisposer;
-
-    static Instance: IconButton;
-    constructor(props: iconButtonProps) {
-        super(props);
-        IconButton.Instance = this;
-    }
-
-    componentDidMount = () => {
-        this._resetReaction = reaction(
-            () => IconBar.Instance._resetClicked,
-            action(() => {
-                if (IconBar.Instance._resetClicked) {
-                    this._isSelected = false;
-                    IconBar.Instance._reset++;
-                    if (IconBar.Instance._reset === 9) {
-                        IconBar.Instance._reset = 0;
-                        IconBar.Instance._resetClicked = false;
-                    }
-                }
-            })
-        );
-
-        this._selectAllReaction = reaction(
-            () => IconBar.Instance._selectAllClicked,
-            action(() => {
-                if (IconBar.Instance._selectAllClicked) {
-                    this._isSelected = true;
-                    IconBar.Instance._select++;
-                    if (IconBar.Instance._select === 9) {
-                        IconBar.Instance._select = 0;
-                        IconBar.Instance._selectAllClicked = false;
-                    }
-                }
-            })
-        );
-    };
-
-    @action.bound
-    getIcon() {
-        switch (this.props.type) {
-            case DocumentType.NONE:  return 'ban';
-            case DocumentType.AUDIO: return 'music';
-            case DocumentType.COL:   return 'object-group';
-            case DocumentType.IMG:   return 'image';
-            case DocumentType.LINK:  return 'link';
-            case DocumentType.PDF:   return 'file-pdf';
-            case DocumentType.RTF:   return 'sticky-note';
-            case DocumentType.VID:   return 'video';
-            case DocumentType.WEB:   return 'globe-asia';
-            case DocumentType.MAP:   return 'map-marker-alt';
-            default:                 return 'caret-down';
-        } // prettier-ignore
-    }
-
-    @action.bound
-    onClick = () => {
-        const newList: string[] = IconBar.Instance.getIcons();
-
-        if (!this._isSelected) {
-            this._isSelected = true;
-            newList.push(this.props.type);
-        } else {
-            this._isSelected = false;
-            _.pull(newList, this.props.type);
-        }
-
-        IconBar.Instance.updateIcon(newList);
-    };
-
-    selected = {
-        opacity: 1,
-        backgroundColor: '#121721',
-        //backgroundColor: "rgb(128, 128, 128)"
-    };
-
-    notSelected = {
-        opacity: 0.2,
-        backgroundColor: '#121721',
-    };
-
-    hoverStyle = {
-        opacity: 1,
-        backgroundColor: 'rgb(128, 128, 128)',
-        //backgroundColor: "rgb(178, 206, 248)" //$medium-blue
-    };
-
-    render() {
-        return (
-            <div className="type-outer" id={this.props.type + '-filter'} onMouseEnter={() => (this._hover = true)} onMouseLeave={() => (this._hover = false)} onClick={this.onClick}>
-                <div className="type-icon" id={this.props.type + '-icon'} style={this._hover ? this.hoverStyle : this._isSelected ? this.selected : this.notSelected}>
-                    <FontAwesomeIcon className="fontawesome-icon" icon={this.getIcon()} />
-                </div>
-                {/* <div className="filter-description">{this.props.type}</div> */}
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/search/NaviconButton.scss b/src/client/views/search/NaviconButton.scss
deleted file mode 100644
index 917635c0b..000000000
--- a/src/client/views/search/NaviconButton.scss
+++ /dev/null
@@ -1,69 +0,0 @@
-@import '../global/globalCssVariables.module.scss';
-
-$height-icon: 15px;
-$width-line: 30px;
-$height-line: 4px;
-
-$transition-time: 0.4s;
-$rotation: 45deg;
-$translateY: calc($height-icon / 2);
-$translateX: 0;
-
-#hamburger-icon {
-    width: $width-line;
-    height: $height-icon;
-    position: relative;
-    display: block;
-    transition: all $transition-time;
-    -webkit-transition: all $transition-time;
-    -moz-transition: all $transition-time;
-
-    .line {
-        display: block;
-        background: $medium-gray;
-        width: $width-line;
-        height: $height-line;
-        position: absolute;
-        left: 0;
-        border-radius: calc($height-line / 2);
-        transition: all $transition-time;
-        -webkit-transition: all $transition-time;
-        -moz-transition: all $transition-time;
-
-        &.line-1 {
-            top: 0;
-        }
-
-        &.line-2 {
-            top: 50%;
-        }
-
-        &.line-3 {
-            top: 100%;
-        }
-    }
-}
-
-.filter-header.active {
-    .line-1 {
-        transform: translateY($translateY) translateX($translateX) rotate($rotation);
-        -webkit-transform: translateY($translateY) translateX($translateX) rotate($rotation);
-        -moz-transform: translateY($translateY) translateX($translateX) rotate($rotation);
-    }
-
-    .line-2 {
-        opacity: 0;
-    }
-
-    .line-3 {
-        transform: translateY($translateY * -1) translateX($translateX) rotate($rotation * -1);
-        -webkit-transform: translateY($translateY * -1) translateX($translateX) rotate($rotation * -1);
-        -moz-transform: translateY($translateY * -1) translateX($translateX) rotate($rotation * -1);
-    }
-}
-
-.filter-header:hover #hamburger-icon {
-    transform: scale(1.1);
-    -webkit-transform: scale(1.1);
-    -moz-transform: scale(1.1);
-}
diff --git a/src/client/views/search/NaviconButton.tsx b/src/client/views/search/NaviconButton.tsx
deleted file mode 100644
index 0fa4a0fca..000000000
--- a/src/client/views/search/NaviconButton.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import * as React from 'react';
-import { observer } from 'mobx-react';
-import "./NaviconButton.scss";
-import * as $ from 'jquery';
-import { observable } from 'mobx';
-
-export interface NaviconProps {
-    onClick(): void;
-}
-
-export class NaviconButton extends React.Component<NaviconProps> {
-
-    @observable private _ref: React.RefObject<HTMLAnchorElement> = React.createRef();
-
-    componentDidMount = () => {
-        const that = this;
-        if (this._ref.current) {
-            this._ref.current.addEventListener("click", function (e) {
-                e.preventDefault();
-                if (that._ref.current) {
-                    that._ref.current.classList.toggle('active');
-                    return false;
-                }
-            });
-        }
-    }
-
-    render() {
-        return (
-            <a id="hamburger-icon" href="#" ref={this._ref} title="Menu">
-                <span className="line line-1"></span>
-                <span className="line line-2"></span>
-                <span className="line line-3"></span>
-            </a>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index a3c4bd2ed..09e459f7d 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -1,5 +1,4 @@
 @import '../global/globalCssVariables.module.scss';
-@import './NaviconButton.scss';
 
 .searchBox-container {
     width: 100%;
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3172ff9c0..ccfccb771 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,5 +1,5 @@
 import { Tooltip } from '@mui/material';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../../fields/Doc';
@@ -61,6 +61,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      */
     constructor(props: any) {
         super(props);
+        makeObservable(this);
         SearchBox.Instance = this;
     }
 
diff --git a/src/client/views/search/ToggleBar.scss b/src/client/views/search/ToggleBar.scss
deleted file mode 100644
index 9e27db2bc..000000000
--- a/src/client/views/search/ToggleBar.scss
+++ /dev/null
@@ -1,41 +0,0 @@
-@import '../global/globalCssVariables.module.scss';
-
-.toggle-title {
-    display: flex;
-    align-items: center;
-    color: $white;
-    text-transform: uppercase;
-    flex-direction: row;
-    justify-content: space-around;
-    padding: 5px;
-
-    .toggle-option {
-        width: 50%;
-        text-align: center;
-        -webkit-transition: all 0.2s ease-in-out;
-        -moz-transition: all 0.2s ease-in-out;
-        -o-transition: all 0.2s ease-in-out;
-        transition: all 0.2s ease-in-out;
-        color: gray;
-        font-size: 13;
-    }
-}
-
-.toggle-bar {
-    // height: 50px;
-    height: 30px;
-    width: 100px;
-    background-color: $medium-gray;
-    border-radius: 10px;
-    padding: 5px;
-    display: flex;
-    align-items: center;
-
-    .toggle-button {
-        // width: 275px;
-        width: 40px;
-        height: 100%;
-        border-radius: 10px;
-        background-color: $white;
-    }
-}
diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx
deleted file mode 100644
index 466822eba..000000000
--- a/src/client/views/search/ToggleBar.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import * as React from 'react';
-import { observer } from 'mobx-react';
-import { observable, action, runInAction, computed } from 'mobx';
-import "./SearchBox.scss";
-import "./ToggleBar.scss";
-import * as anime from 'animejs';
-
-export interface ToggleBarProps {
-    originalStatus: boolean;
-    optionOne: string;
-    optionTwo: string;
-    handleChange(): void;
-    getStatus(): boolean;
-}
-
-@observer
-export class ToggleBar extends React.Component<ToggleBarProps>{
-    static Instance: ToggleBar;
-
-    @observable private _forwardTimeline: anime.AnimeTimelineInstance;
-    @observable private _toggleButton: React.RefObject<HTMLDivElement>;
-    @observable private _originalStatus: boolean = this.props.originalStatus;
-
-    constructor(props: ToggleBarProps) {
-        super(props);
-        ToggleBar.Instance = this;
-        this._toggleButton = React.createRef();
-        this._forwardTimeline = anime.timeline({
-            loop: false,
-            autoplay: false,
-            direction: "reverse",
-        });
-    }
-
-    componentDidMount = () => {
-        const totalWidth = 265;
-
-        if (this._originalStatus) {
-            this._forwardTimeline.add({
-                targets: this._toggleButton.current,
-                translateX: totalWidth,
-                easing: "easeInOutQuad",
-                duration: 500
-            });
-        }
-        else {
-            this._forwardTimeline.add({
-                targets: this._toggleButton.current,
-                translateX: -totalWidth,
-                easing: "easeInOutQuad",
-                duration: 500
-            });
-        }
-    }
-
-    @action.bound
-    onclick() {
-        this._forwardTimeline.play();
-        this._forwardTimeline.reverse();
-        this.props.handleChange();
-    }
-
-    @action.bound
-    public resetToggle = () => {
-        if (!this.props.getStatus()) {
-            this._forwardTimeline.play();
-            this._forwardTimeline.reverse();
-            this.props.handleChange();
-        }
-    }
-
-    render() {
-        return (
-            <div>
-                <div className="toggle-title">
-                    <div className="toggle-option" style={{ opacity: (this.props.getStatus() ? 1 : .4) }}>{this.props.optionOne}</div>
-                    <div className="toggle-option" style={{ opacity: (this.props.getStatus() ? .4 : 1) }}>{this.props.optionTwo}</div>
-                </div>
-                <div className="toggle-bar" id="toggle-bar" onClick={this.onclick} style={{ flexDirection: (this._originalStatus ? "row" : "row-reverse") }}>
-                    <div className="toggle-button" id="toggle-button" ref={this._toggleButton} />
-                </div>
-            </div>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index 8ac20b1e5..817b62373 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -34,7 +34,7 @@ export abstract class URLField extends ObjectField {
         if (Utils.prepend(this.url?.pathname) === this.url?.href) {
             return `new ${this.constructor.name}("${this.url.pathname}")`;
         }
-        return `new ${this.constructor.name}("${this.url.href}")`;
+        return `new ${this.constructor.name}("${this.url?.href}")`;
     }
     [ToString]() {
         if (Utils.prepend(this.url?.pathname) === this.url?.href) {
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index ab3982cc5..cd9b635bd 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -121,8 +121,8 @@ export namespace DashUploadUtils {
     }
 
     function resolveExistingFile(name: string, pat: string, directory: Directory, mimetype?: string | null, duration?: number, rawText?: string): Upload.FileResponse<Upload.FileInformation> {
-        const data = { size: 0, filepath: pat, name, type: mimetype ?? '', originalFilename: name, newFilename: path.basename(pat), mimetype, hashAlgorithm: false as any };
-        const file = { ...data, toJSON: () => ({ ...data, length: 0, filename: data.filepath.replace(/.*\//, ''), mtime: new Date(), mimetype, toJson: () => undefined as any }) };
+        const data = { size: 0, filepath: pat, name, type: mimetype ?? '', originalFilename: name, newFilename: path.basename(pat), mimetype: mimetype || null, hashAlgorithm: false as any };
+        const file = { ...data, toJSON: () => ({ ...data, length: 0, filename: data.filepath.replace(/.*\//, ''), mtime: new Date(), mimetype: mimetype || null, toJson: () => undefined as any }) };
         return {
             source: file || null,
             result: {
-- 
cgit v1.2.3-70-g09d2

