From 216385c7e84febce8988ef1390845b0c661fb04f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Nov 2023 12:55:59 -0500
Subject: fixed bug where tableBox's didn't render all of the rows they
 receive. lots of code cleanup -- moving things from Doc.ts to better
 locations.  Changed overlays and published docs to be local to their
 dashboard.  changed treeview icons.

---
 src/client/views/DashboardView.tsx | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

(limited to 'src/client/views/DashboardView.tsx')

diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 014a6358f..9e2ed7822 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -12,6 +12,7 @@ import { PrefetchProxy } from '../../fields/Proxy';
 import { listSpec } from '../../fields/Schema';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, ImageCast, StrCast } from '../../fields/Types';
+import { normalizeEmail } from '../../fields/util';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions, DocUtils } from '../documents/Documents';
 import { HistoryUtil } from '../util/History';
@@ -149,7 +150,7 @@ export class DashboardView extends React.Component {
                             : this.getDashboards(this.selectedDashboardGroup).map(dashboard => {
                                   const href = ImageCast(dashboard.thumb)?.url?.href;
                                   const shared = Object.keys(dashboard[DocAcl])
-                                      .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Guest'].includes(key))
+                                      .filter(key => key !== `acl-${normalizeEmail(Doc.CurrentUserEmail)}` && !['acl-Me', 'acl-Guest'].includes(key))
                                       .some(key => dashboard[DocAcl][key] !== AclPrivate);
                                   return (
                                       <div
@@ -372,6 +373,8 @@ export class DashboardView extends React.Component {
         Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc);
         dashboardDoc['pane-count'] = 1;
         freeformDoc.embedContainer = dashboardDoc;
+        dashboardDoc.myOverlayDocs = new List<Doc>();
+        dashboardDoc.myPublishedDocs = new List<Doc>();
 
         Doc.AddDocToList(Doc.MyDashboards, 'data', dashboardDoc);
 
-- 
cgit v1.2.3-70-g09d2


From ba3b3f6f261074bd3f35012bde8730f5d4a36905 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 29 Nov 2023 10:02:34 -0500
Subject: numerous changes to fix bugs and to fix/remove old or hacky code. 
 fixed doc dec resizing.  moving this.rootDoc => this.Document .  fixing
 template artifacts.

---
 src/client/documents/Documents.ts                  |  19 +-
 src/client/util/CurrentUserUtils.ts                |  96 +--
 src/client/util/Scripting.ts                       |   4 +-
 src/client/views/DashboardView.tsx                 |   2 +-
 src/client/views/DocComponent.tsx                  |  16 +-
 src/client/views/DocumentButtonBar.tsx             |  14 +-
 src/client/views/DocumentDecorations.tsx           |  18 +-
 src/client/views/GlobalKeyHandler.ts               |   4 +-
 src/client/views/MainView.tsx                      |   6 +-
 src/client/views/MarqueeAnnotator.tsx              |  26 +-
 src/client/views/PropertiesButtons.tsx             |   5 +-
 src/client/views/PropertiesView.tsx                |  47 +-
 src/client/views/StyleProvider.tsx                 |   9 +-
 .../views/collections/CollectionDockingView.tsx    |  53 +-
 src/client/views/collections/CollectionMenu.tsx    | 824 +--------------------
 .../views/collections/CollectionNoteTakingView.tsx |   4 +-
 .../views/collections/CollectionPileView.tsx       |  16 +-
 .../collections/CollectionStackedTimeline.tsx      |  16 +-
 .../views/collections/CollectionStackingView.scss  |   2 +-
 .../views/collections/CollectionStackingView.tsx   |  96 ++-
 src/client/views/collections/CollectionSubView.tsx |  20 +-
 .../views/collections/CollectionTimeView.tsx       |   4 +-
 .../views/collections/CollectionTreeView.tsx       |  16 +-
 src/client/views/collections/TabDocView.tsx        |   4 +-
 src/client/views/collections/TreeView.tsx          |  13 +-
 .../CollectionFreeFormLayoutEngines.tsx            |   1 +
 .../CollectionFreeFormPannableContents.tsx         |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 199 ++---
 .../collections/collectionFreeForm/MarqueeView.tsx |   2 +-
 .../collectionGrid/CollectionGridView.tsx          |   5 +-
 .../collectionLinear/CollectionLinearView.tsx      |   8 +-
 .../CollectionMulticolumnView.scss                 |   2 +-
 .../CollectionMulticolumnView.tsx                  |  39 +-
 .../CollectionMultirowView.scss                    |   5 -
 .../CollectionMultirowView.tsx                     |  37 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  10 +-
 .../collectionSchema/SchemaTableCell.tsx           |   3 +-
 src/client/views/global/globalScripts.ts           |   8 +-
 src/client/views/nodes/AudioBox.tsx                |   4 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  51 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  23 +-
 src/client/views/nodes/DocumentView.tsx            | 171 +++--
 src/client/views/nodes/FontIconBox/FontIconBox.tsx | 121 ++-
 src/client/views/nodes/FunctionPlotBox.tsx         |   6 +-
 src/client/views/nodes/ImageBox.tsx                |   3 +-
 src/client/views/nodes/KeyValueBox.tsx             |   2 +-
 src/client/views/nodes/LinkBox.tsx                 |  18 +-
 src/client/views/nodes/MapBox/MapPushpinBox.tsx    |   6 +-
 src/client/views/nodes/PDFBox.tsx                  |   1 -
 .../views/nodes/RecordingBox/RecordingBox.tsx      |  12 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   4 +-
 src/client/views/nodes/ScriptingBox.tsx            |   6 +-
 src/client/views/nodes/VideoBox.tsx                |   6 +-
 src/client/views/nodes/WebBox.tsx                  |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  53 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |  46 +-
 .../views/nodes/formattedText/RichTextRules.ts     |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  97 ++-
 src/client/views/nodes/trails/PresElementBox.tsx   |  64 +-
 src/client/views/pdf/PDFViewer.tsx                 |   5 +-
 src/client/views/search/SearchBox.tsx              |   2 +-
 src/fields/Doc.ts                                  |  15 +-
 src/fields/ScriptField.ts                          |  26 +-
 63 files changed, 798 insertions(+), 1605 deletions(-)

(limited to 'src/client/views/DashboardView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 029653204..4def19384 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -292,6 +292,7 @@ export class DocumentOptions {
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
     childDocumentsActive?: BOOLt = new BoolInfo('whether child documents are active when parent is document active');
+    childLayoutFitWidth?: BOOLt = new BoolInfo("whether a child doc's fitWith should be overriden by collection");
     childDontRegisterViews?: BOOLt = new BoolInfo('whether child document views should be registered so that they can be found when following links, etc');
     childHideLinkButton?: BOOLt = new BoolInfo('hide link buttons on all children');
     childContextMenuFilters?: List<ScriptField>;
@@ -1134,7 +1135,7 @@ export namespace Docs {
         // }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _type_collection: CollectionViewType.Freeform }, id);
+            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Freeform }, id);
             documents.forEach(d => Doc.SetContainer(d, inst));
             return inst;
         }
@@ -1217,7 +1218,7 @@ export namespace Docs {
         }
 
         export function FunctionPlotDocument(documents: Array<Doc>, options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.FUNCPLOT), new List(documents), { ...(options || {}) });
+            return InstanceFromProto(Prototypes.get(DocumentType.FUNCPLOT), new List(documents), { title: 'func plot', ...(options || {}) });
         }
 
         export function ButtonDocument(options?: DocumentOptions) {
@@ -1487,7 +1488,7 @@ export namespace DocUtils {
             Object.keys(scripts).map(key => {
                 const script = scripts[key];
                 if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) {
-                    doc[key] = ScriptField.MakeScript(script, {
+                    (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, {
                         self: Doc.name,
                         this: Doc.name,
                         dragData: DragManager.DocumentDragData.name,
@@ -1511,7 +1512,7 @@ export namespace DocUtils {
                     const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
                     if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) {
                         const setFunc = Cast(funcs[key + '-setter'], 'string', null);
-                        doc[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true }, setFunc) : undefined;
+                        (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true }, setFunc) : undefined;
                     }
                 });
         return doc;
@@ -1717,7 +1718,7 @@ export namespace DocUtils {
     }
     export function createCustomView(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
         const templateName = templateSignature.replace(/\(.*\)/, '');
-        docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc._isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature);
+        docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature);
 
         const customName = 'layout_' + templateSignature;
         const _width = NumCast(doc._width);
@@ -1987,9 +1988,9 @@ ScriptingGlobals.add(function makeDelegate(proto: any) {
     const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title });
     return d;
 });
-ScriptingGlobals.add(function generateLinkTitle(self: Doc) {
-    const link_anchor_1title = self.link_anchor_1 && self.link_anchor_1 !== self ? Cast(self.link_anchor_1, Doc, null)?.title : '<?>';
-    const link_anchor_2title = self.link_anchor_2 && self.link_anchor_2 !== self ? Cast(self.link_anchor_2, Doc, null)?.title : '<?>';
-    const relation = self.link_relationship || 'to';
+ScriptingGlobals.add(function generateLinkTitle(link: Doc) {
+    const link_anchor_1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : '<?>';
+    const link_anchor_2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : '<?>';
+    const relation = link.link_relationship || 'to';
     return `${link_anchor_1title} (${relation}) ${link_anchor_2title}`;
 });
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 18c7a1fb6..2f2ac2a71 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -284,7 +284,7 @@ export class CurrentUserUtils {
                                                                                                 treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
                                                                                                 dropAction:'move', treeView_Type: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
-                                                                                            }, funcs: {title: 'self.text?.Text'}},
+                                                                                            }, funcs: {title: 'this.text?.Text'}},
             ];
 
         emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs));
@@ -339,7 +339,7 @@ export class CurrentUserUtils {
 
     /// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
     static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}, hidden?: boolean}[] {
-      const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target?.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())"; 
+      const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(this.target?.data).filter(doc => !docList(this.target.viewed).includes(doc)).length.toString())"; 
       const getActiveDashTrails = "Doc.ActiveDashboard?.myTrails";
         return [
             { title: "Dashboards",      toolTip: "Dashboards",      target: this.setupDashboards(doc, "myDashboards"),         ignoreClick: true, icon: "desktop",     funcs: {hidden: "IsNoviceMode()"} },
@@ -467,6 +467,7 @@ export class CurrentUserUtils {
         const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
         const reqdToolOps:DocumentOptions = {
             title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
+            layout_explainer: "This is a palette of documents that can be created.",
             _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, 
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
@@ -558,7 +559,7 @@ export class CurrentUserUtils {
         const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, 
             title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, color: Colors.BLACK, buttonText: "Empty", icon: "trash", isSystem: true,
             toolTip: "Empty recently closed",};
-        DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
+        DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("this.target")});
 
         if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
             recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
@@ -628,42 +629,42 @@ export class CurrentUserUtils {
     }
     static stackTools(): Button[] {
         return [
-            { title: "Center",    icon: "align-center",        toolTip: "Center Align Stack",  btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"center",     funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Center",    icon: "align-center",        toolTip: "Center Align Stack",  btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"center",     funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
         ]
     }
     static viewTools(): Button[] {
         return [
-            { title: "Snap",      icon: "th",                  toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines",  funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
-            { title: "Grid",      icon: "border-all",          toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
-            { title: "View All",  icon: "object-group",        toolTip: "Keep all Docs in View",btnType: ButtonType.ToggleButton, ignoreClick:true, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Snap",      icon: "th",                  toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines",  funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Grid",      icon: "border-all",          toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "View All",  icon: "object-group",        toolTip: "Keep all Docs in View",btnType: ButtonType.ToggleButton, ignoreClick:true, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
             // want the same style as toggle button, but don't want it to act as an actual toggle, so set disableToggle to true,
-            { title: "Fit All",   icon: "arrows-left-right",   toolTip: "Fit Docs to View (once)",btnType: ButtonType.ClickButton,ignoreClick:false,expertMode: false, toolType:"fitOnce",    funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
-            { title: "Clusters",  icon: "braille",             toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
-            { title: "Cards",     icon: "brain",               toolTip: "Flashcards",          btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
-            { title: "Arrange",   icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange",  btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange",    funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Fit All",   icon: "arrows-left-right",   toolTip: "Fit Docs to View (once)",btnType: ButtonType.ClickButton,ignoreClick:false,expertMode: false, toolType:"fitOnce",    funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Clusters",  icon: "braille",             toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Cards",     icon: "brain",               toolTip: "Flashcards",          btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Arrange",   icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange",  btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange",    funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
         ]
     }
     static textTools():Button[] {
         return [
-            { title: "Font",     toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},
+            { title: "Font",     toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'},
               btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
-            { title: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton,              toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 6 },
-            { title: "Color",    toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}},
-            { title: "Highlight",toolTip: "Font highlight",       btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
-            { title: "Bold",     toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",     ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Italic",   toolTip: "Italic (Ctrl+I)",      btnType: ButtonType.ToggleButton, icon: "italic",      toolType:"italics",  ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Under",    toolTip: "Underline (Ctrl+U)",   btnType: ButtonType.ToggleButton, icon: "underline",   toolType:"underline",ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Bullets",  toolTip: "Bullet List",          btnType: ButtonType.ToggleButton, icon: "list",        toolType:"bullet",   ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "#",        toolTip: "Number List",          btnType: ButtonType.ToggleButton, icon: "list-ol",     toolType:"decimal",  ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Vcenter",  toolTip: "Vertical center",      btnType: ButtonType.ToggleButton, icon: "pallet",      toolType:"vcent",    ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton,              toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 6 },
+            { title: "Color",    toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'}},
+            { title: "Highlight",toolTip: "Font highlight",       btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
+            { title: "Bold",     toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",     ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
+            { title: "Italic",   toolTip: "Italic (Ctrl+I)",      btnType: ButtonType.ToggleButton, icon: "italic",      toolType:"italics",  ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
+            { title: "Under",    toolTip: "Underline (Ctrl+U)",   btnType: ButtonType.ToggleButton, icon: "underline",   toolType:"underline",ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
+            { title: "Bullets",  toolTip: "Bullet List",          btnType: ButtonType.ToggleButton, icon: "list",        toolType:"bullet",   ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
+            { title: "#",        toolTip: "Number List",          btnType: ButtonType.ToggleButton, icon: "list-ol",     toolType:"decimal",  ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
+            { title: "Vcenter",  toolTip: "Vertical center",      btnType: ButtonType.ToggleButton, icon: "pallet",      toolType:"vcent",    ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "Align",    toolTip: "Alignment",            btnType: ButtonType.MultiToggleButton,                 toolType:"alignment", ignoreClick: true,
               subMenu: [
-                { title: "Left",     toolTip: "Left align (Cmd-[)",   btnType: ButtonType.ToggleButton, icon: "align-left",  toolType:"left",  ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }},
-                { title: "Center",   toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-                { title: "Right",    toolTip: "Right align (Cmd-])",  btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+                { title: "Left",     toolTip: "Left align (Cmd-[)",   btnType: ButtonType.ToggleButton, icon: "align-left",  toolType:"left",  ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}' }},
+                { title: "Center",   toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
+                { title: "Right",    toolTip: "Right align (Cmd-])",  btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             ]},
-            { title: "Dictate",  toolTip: "Dictate",              btnType: ButtonType.ToggleButton, icon: "microphone",  toolType:"dictation", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}},
-            { title: "NoLink",   toolTip: "Auto Link",            btnType: ButtonType.ToggleButton, icon: "link",        toolType:"noAutoLink", expertMode:true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
+            { title: "Dictate",  toolTip: "Dictate",              btnType: ButtonType.ToggleButton, icon: "microphone",  toolType:"dictation", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}},
+            { title: "NoLink",   toolTip: "Auto Link",            btnType: ButtonType.ToggleButton, icon: "link",        toolType:"noAutoLink", expertMode:true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
             // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}},
             // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", scripts: {onClick:: 'toggleSuperscript()'}},
             // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", scripts: {onClick:: 'toggleSubscript()'}},
@@ -672,15 +673,15 @@ export class CurrentUserUtils {
 
     static inkTools():Button[] {
         return [
-            { title: "Pen",    toolTip: "Pen (Ctrl+P)",                     btnType: ButtonType.ToggleButton, icon: "pen-nib",    toolType: "pen",                            scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}' }},
-            { title: "Write",  toolTip: "Write (Ctrl+Shift+P)",             btnType: ButtonType.ToggleButton, icon: "pen",        toolType: "write",                          scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
-            { title: "Eraser", toolTip: "Eraser (Ctrl+E)",                  btnType: ButtonType.ToggleButton, icon: "eraser",     toolType: "eraser",                         scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
-            { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle",     toolType:GestureUtils.Gestures.Circle,      scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} },
-            { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square",     toolType:GestureUtils.Gestures.Rectangle,   scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} },
-            { title: "Line",   toolTip: "Line (double tap to lock mode)",   btnType: ButtonType.ToggleButton, icon: "minus",      toolType:GestureUtils.Gestures.Line,        scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} },
-            { title: "Mask",   toolTip: "Mask",                             btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask",                        scripts: {onClick:'{ return setInkProperty(self.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } },
-            { title: "Width",  toolTip: "Stroke width",                     btnType: ButtonType.NumberSliderButton,               toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnMin: 1},
-            { title: "Ink",    toolTip: "Stroke color",                     btnType: ButtonType.ColorButton,  icon: "pen",        toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'} },
+            { title: "Pen",    toolTip: "Pen (Ctrl+P)",                     btnType: ButtonType.ToggleButton, icon: "pen-nib",    toolType: "pen",                            scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }},
+            { title: "Write",  toolTip: "Write (Ctrl+Shift+P)",             btnType: ButtonType.ToggleButton, icon: "pen",        toolType: "write",                          scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
+            { title: "Eraser", toolTip: "Eraser (Ctrl+E)",                  btnType: ButtonType.ToggleButton, icon: "eraser",     toolType: "eraser",                         scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
+            { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle",     toolType:GestureUtils.Gestures.Circle,      scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+            { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square",     toolType:GestureUtils.Gestures.Rectangle,   scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+            { title: "Line",   toolTip: "Line (double tap to lock mode)",   btnType: ButtonType.ToggleButton, icon: "minus",      toolType:GestureUtils.Gestures.Line,        scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+            { title: "Mask",   toolTip: "Mask",                             btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask",                        scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } },
+            { title: "Width",  toolTip: "Stroke width",                     btnType: ButtonType.NumberSliderButton,               toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1},
+            { title: "Ink",    toolTip: "Stroke color",                     btnType: ButtonType.ColorButton,  icon: "pen",        toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'} },
         ];
     }
 
@@ -709,17 +710,17 @@ export class CurrentUserUtils {
             { title: "Pin",     icon: "map-pin",       toolTip: "Pin View to Trail",    btnType: ButtonType.ClickButton,         expertMode: false,                                                                                                                             width: 30,  scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}},
             { title: "Header",  icon: "heading",       toolTip: "Doc Titlebar Color",   btnType: ButtonType.ColorButton,         expertMode: true,                                                                                                           ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'} },
             { title: "Fill",    icon: "fill-drip",     toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton,         expertMode: false,                                                                                                          ignoreClick: true, width: 30,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected
-            { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'},       scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
-            { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
-            { title: "Num",     icon:"",               toolTip: "Frame Number (click to toggle edit mode)",    btnType: ButtonType.TextButton,          expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
-            { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
-            { title: "Text",    icon: "Text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   expertMode: false, toolType:DocumentType.RTF,            funcs: {                                                                             linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "Ink",     icon: "Ink",           toolTip: "Ink functions",        subMenu: CurrentUserUtils.inkTools(),    expertMode: false, toolType:DocumentType.INK,            funcs: {hidden: `IsExploreMode()`,                                                   linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
-            { title: "Doc",     icon: "Doc",           toolTip: "Freeform Doc tools",   subMenu: CurrentUserUtils.freeTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "Stack",   icon: "View",          toolTip: "Stacking tools",       subMenu: CurrentUserUtils.stackTools(),  expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
-            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(),expertMode: false,toolType:CollectionViewType.Schema,funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected
+            { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(this.toolType, this.expertMode, true)'},       scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
+            { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(this.toolType, this.expertMode)'}, width: 30,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
+            { title: "Num",     icon:"", toolTip: "Frame # (click to toggle edit mode)",btnType: ButtonType.TextButton,          expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(this.toolType, this.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
+            { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(this.toolType, this.expertMode)'}, width: 30,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
+            { title: "Text",    icon: "Text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   expertMode: false, toolType:DocumentType.RTF,            funcs: {                                                                             linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
+            { title: "Ink",     icon: "Ink",           toolTip: "Ink functions",        subMenu: CurrentUserUtils.inkTools(),    expertMode: false, toolType:DocumentType.INK,            funcs: {hidden: `IsExploreMode()`,                                                   linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
+            { title: "Doc",     icon: "Doc",           toolTip: "Freeform Doc tools",   subMenu: CurrentUserUtils.freeTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode, true)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
+            { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
+            { title: "Stack",   icon: "View",          toolTip: "Stacking tools",       subMenu: CurrentUserUtils.stackTools(),  expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
+            { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected
+            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(),expertMode: false,toolType:CollectionViewType.Schema,funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when Schema is selected
            ];
     }
 
@@ -904,7 +905,7 @@ export class CurrentUserUtils {
         this.setupDocTemplates(doc); // sets up the template menu of templates
         //this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
         DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
         
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards)
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
@@ -976,6 +977,7 @@ export class CurrentUserUtils {
                     DashboardView.createNewDashboard(undefined, "guest dashboard");
                 } else {
                     userDoc.activePage = "home";
+                    userDoc.noviceMode = true;
                 }
             }
             return userDoc;
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 70c2e3842..400b63a1c 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -264,6 +264,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
 }
 
 ScriptingGlobals.add(CompileScript);
-ScriptingGlobals.add(function runScript(self: Doc, script: ScriptField) {
-    return script?.script.run({ this: self, self: self }).result;
+ScriptingGlobals.add(function runScript(doc: Doc, script: ScriptField) {
+    return script?.script.run({ this: doc }).result;
 });
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 9e2ed7822..2765e95e6 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -370,7 +370,7 @@ export class DashboardView extends React.Component {
         const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row');
 
-        Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc);
+        Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc, undefined, undefined, true);
         dashboardDoc['pane-count'] = 1;
         freeformDoc.embedContainer = dashboardDoc;
         dashboardDoc.myOverlayDocs = new List<Doc>();
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index fdaca8056..61b7a3bff 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -10,6 +10,8 @@ import { DocUtils } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentView } from './nodes/DocumentView';
 import * as React from 'react';
+import { DocumentManager } from '../util/DocumentManager';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 export interface DocComponentProps {
@@ -26,7 +28,7 @@ export function DocComponent<P extends DocComponentProps>() {
         }
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
         @computed get rootDoc() {
-            return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document;
+            return this.props.Document;
         }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
         @computed get layoutDoc() {
@@ -58,7 +60,9 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
     class Component extends React.Component<React.PropsWithChildren<P>> {
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         //@computed get Document(): T { return schemaCtor(this.props.Document); }
-
+        @computed get Document() {
+            return this.props.Document;
+        }
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
         @computed get rootDoc() {
             return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document;
@@ -146,7 +150,11 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                         doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
                     }
                 });
-                this.isAnyChildContentActive() && this.props.select(false);
+                if (targetDataDoc.isGroup && DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]).length < 2) {
+                    (DocumentManager.Instance.getFirstDocumentView(targetDataDoc)?.ComponentView as CollectionFreeFormView)?.promoteCollection();
+                } else {
+                    this.isAnyChildContentActive() && this.props.select(false);
+                }
                 return true;
             }
 
@@ -173,7 +181,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             if (this.props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
                 return false;
             }
-            const targetDataDoc = this.rootDoc[DocData];
+            const targetDataDoc = this.dataDoc;
             const effectiveAcl = GetEffectiveAcl(targetDataDoc);
 
             if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index c1ec5b4a4..bd82f7782 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -574,12 +574,12 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         e.stopPropagation();
     };
     render() {
-        if (!this.view0) return null;
-
-        const isText = this.view0.props.Document[this.view0.LayoutFieldKey] instanceof RichTextField;
         const doc = this.view0?.props.Document;
-        const considerPull = isText && this.considerGoogleDocsPull;
-        const considerPush = isText && this.considerGoogleDocsPush;
+        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,11 +608,11 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                 <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' : '' }}>
+                    <div className="documentButtonBar-button" style={{ display: !considerPush() ? 'none' : '' }}>
                         {this.considerGoogleDocsPush}
                     </div>
                 )}
-                <div className="documentButtonBar-button" style={{ display: !considerPull ? 'none' : '' }}>
+                <div className="documentButtonBar-button" style={{ display: !considerPull() ? 'none' : '' }}>
                     {this.considerGoogleDocsPull}
                 </div>
                 <div className="documentButtonBar-button">{this.menuButton}</div>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d4b474de9..4ede2e2bb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
@@ -71,10 +71,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                           e.clientY - center.y, 
                                           NumCast(SelectionManager.Views().lastElement()?.screenToLocalTransform().Rotate));
                 (this._showNothing = !(this.Bounds.x !== Number.MAX_VALUE && //
-                    (this.Bounds.x > center.x+x + this._resizeBorderWidth / 2 || 
-                     this.Bounds.r < center.x+x - this._resizeBorderWidth / 2 || 
-                     this.Bounds.y > center.y+y + this._resizeBorderWidth / 2 || 
-                     this.Bounds.b < center.y+y - this._resizeBorderWidth / 2)));
+                    (this.Bounds.x > center.x+x || this.Bounds.r < center.x+x || 
+                     this.Bounds.y > center.y+y || this.Bounds.b < center.y+y )));
             })); // prettier-ignore
     }
 
@@ -551,8 +549,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 if (setData) Doc.SetNativeHeight(Doc.GetProto(doc), NumCast(doc._nativeHeight));
             }
 
-            doc._width = NumCast(doc._width) * scale.x;
-            doc._height = NumCast(doc._height) * scale.y;
+            doc._width = Math.max(1, NumCast(doc._width) * scale.x);
+            doc._height = Math.max(1, NumCast(doc._height) * scale.y);
             const { deltaX, deltaY } = this.realignRefPt(doc, refCent, initWidth, initHeight);
             doc.x = NumCast(doc.x) + deltaX;
             doc.y = NumCast(doc.y) + deltaY;
@@ -675,7 +673,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.props.hideDeleteButton ||
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
-                const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit;
+                const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().dataDoc) : AclEdit;
                 return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin;
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
@@ -801,11 +799,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                     <div key="bl" className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} />
                                     <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} />
                                     <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} />
-                                    {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement()
-                                        ? null
-                                        : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectContainerDocClick, 'tap to select containing document')}
                                 </>
                             )}
+                            {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement() ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectContainerDocClick, 'tap to select containing document')}
                             {useRounding && (
                                 <div
                                     className="documentDecorations-borderRadius"
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 87f81fe76..cc9039ed1 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -1,5 +1,5 @@
 import { random } from 'lodash';
-import { action, runInAction } from 'mobx';
+import { action } from 'mobx';
 import { Doc, DocListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { InkTool } from '../../fields/InkField';
@@ -15,7 +15,6 @@ import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
-import { CollectionFreeFormViewChrome } from './collections/CollectionMenu';
 import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
 import { ContextMenu } from './ContextMenu';
 import { DocumentDecorations } from './DocumentDecorations';
@@ -146,7 +145,6 @@ export class KeyManager {
                 SharingManager.Instance.close();
                 if (!GroupManager.Instance.isOpen) SettingsManager.Instance.close();
                 GroupManager.Instance.close();
-                CollectionFreeFormViewChrome.Instance?.clearKeepPrimitiveMode();
                 window.getSelection()?.empty();
                 document.body.focus();
                 break;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f42c17694..ac0cc3f8c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -937,14 +937,14 @@ export class MainView extends React.Component {
         SnappingManager.GetIsResizing();
         const dragged = DragManager.docsBeingDragged.lastElement() ?? SelectionManager.Docs().lastElement();
         const dragPar = dragged ? DocumentManager.Instance.getDocumentView(dragged)?.CollectionFreeFormView : undefined;
-        return !dragPar?.rootDoc.freeform_snapLines ? null : (
+        return !dragPar?.layoutDoc.freeform_snapLines ? null : (
             <div className="mainView-snapLines">
                 <svg style={{ width: '100%', height: '100%' }}>
                     {SnappingManager.horizSnapLines().map((l, i) => (
-                        <line key={i} x1="0" y1={l} x2="2000" y2={l} stroke={lightOrDark(dragPar.rootDoc.backgroundColor ?? 'gray')} opacity={0.3} strokeWidth={1} strokeDasharray={'2 2'} />
+                        <line key={i} x1="0" y1={l} x2="2000" y2={l} stroke={lightOrDark(dragPar.layoutDoc.backgroundColor ?? 'gray')} opacity={0.3} strokeWidth={1} strokeDasharray={'2 2'} />
                     ))}
                     {SnappingManager.vertSnapLines().map((l, i) => (
-                        <line key={i} y1={this.topOfMainDocContent.toString()} x1={l} y2="2000" x2={l} stroke={lightOrDark(dragPar.rootDoc.backgroundColor ?? 'gray')} opacity={0.3} strokeWidth={1} strokeDasharray={'2 2'} />
+                        <line key={i} y1={this.topOfMainDocContent.toString()} x1={l} y2="2000" x2={l} stroke={lightOrDark(dragPar.layoutDoc.backgroundColor ?? 'gray')} opacity={0.3} strokeWidth={1} strokeDasharray={'2 2'} />
                     ))}
                 </svg>
             </div>
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 20c7a08fa..8b8838464 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, ObservableMap, runInAction, trace } from 'mobx';
+import { action, computed, observable, ObservableMap } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData } from '../../fields/DocSymbols';
@@ -19,7 +19,7 @@ import React = require('react');
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
-    rootDoc: Doc;
+    Document: Doc;
     down?: number[];
     scrollTop: number;
     scaling?: () => number;
@@ -69,8 +69,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             const marqueeAnno = Docs.Create.FreeformDocument([], {
                 onClick: isLinkButton ? FollowLinkScript() : undefined,
                 backgroundColor: color,
-                annotationOn: this.props.rootDoc,
-                title: 'Annotation on ' + this.props.rootDoc.title,
+                annotationOn: this.props.Document,
+                title: 'Annotation on ' + this.props.Document.title,
             });
             marqueeAnno.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale;
             marqueeAnno.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale;
@@ -82,13 +82,13 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         }
 
         const textRegionAnno = Docs.Create.HTMLMarkerDocument([], {
-            annotationOn: this.props.rootDoc,
+            annotationOn: this.props.Document,
             text: this.props.selectionText(),
             backgroundColor: 'transparent',
             presentation_duration: 2100,
             presentation_transition: 500,
             presentation_zoomText: true,
-            title: 'Selection on ' + this.props.rootDoc.title,
+            title: 'Selection on ' + this.props.Document.title,
         });
         let minX = Number.MAX_VALUE;
         let maxX = -Number.MAX_VALUE;
@@ -126,7 +126,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     @action
     highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean, summarize?: boolean) => {
         // creates annotation documents for current highlights
-        const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DocData]);
+        const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]);
         const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
         addAsAnnotation && annotationDoc && this.props.addDocument(annotationDoc);
         return annotationDoc as Doc;
@@ -146,11 +146,11 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         const center = { x: boundingRect.x + boundingRect.width / 2, y: boundingRect.y + boundingRect.height / 2 };
         const downPt = Utils.rotPt(down[0] - center.x, down[1] - center.y, NumCast(this.props.docView().screenToLocalTransform().Rotate));
         const scale = this.props.docView().props.ScreenToLocalTransform().Scale;
-        const scalex = this.props.mainCont.offsetWidth / NumCast(this.props.rootDoc.width);
-        const scaley = this.props.mainCont.offsetHeight / NumCast(this.props.rootDoc.height);
+        const scalex = this.props.mainCont.offsetWidth / NumCast(this.props.Document.width);
+        const scaley = this.props.mainCont.offsetHeight / NumCast(this.props.Document.height);
         // set marquee x and y positions to the spatially transformed position
-        return { x: scalex * (downPt.x + NumCast(this.props.rootDoc.width) / scale / 2) * scale, 
-                 y: scaley * (downPt.y + NumCast(this.props.rootDoc.height) / scale / 2) * scale + this.props.annotationLayerScrollTop }; // prettier-ignore
+        return { x: scalex * (downPt.x + NumCast(this.props.Document.width) / scale / 2) * scale, 
+                 y: scaley * (downPt.y + NumCast(this.props.Document.height) / scale / 2) * scale + this.props.annotationLayerScrollTop }; // prettier-ignore
     };
 
     @action
@@ -184,14 +184,14 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color
 
             const targetCreator = (annotationOn: Doc | undefined) => {
-                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
                 FormattedTextBox.SelectOnLoad = target[Id];
                 return target;
             };
             DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                 dragComplete: e => {
                     if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
-                        e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
+                        e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.Document;
                         e.annoDragData.linkSourceDoc.followLinkZoom = false;
                     }
                 },
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 18f53b8e7..84b1bf038 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -42,12 +42,15 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get selectedDoc() {
         return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.rootDoc;
     }
+    @computed get selectedLayoutDoc() {
+        return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.layoutDoc;
+    }
     @computed get selectedTabView() {
         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) => {
-        const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedDoc;
+        const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedLayoutDoc;
         const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => ((dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true);
         return !targetDoc ? null : (
             <Toggle
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 1b9b26111..8f37a0564 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -63,7 +63,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get selectedDoc() {
-        return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc || Doc.ActiveDashboard;
+        return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.Document || Doc.ActiveDashboard;
+    }
+
+    @computed get selectedLayoutDoc() {
+        return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.layoutDoc || Doc.ActiveDashboard;
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
@@ -126,13 +130,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return [CollectionViewType.Stacking, CollectionViewType.NoteTaking].includes(this.selectedDoc?.type_collection as any);
     }
 
-    rtfWidth = () => (!this.selectedDoc ? 0 : Math.min(NumCast(this.selectedDoc?._width), this.props.width - 20));
-    rtfHeight = () => (!this.selectedDoc ? 0 : this.rtfWidth() <= NumCast(this.selectedDoc?._width) ? Math.min(NumCast(this.selectedDoc?._height), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT);
+    rtfWidth = () => (!this.selectedLayoutDoc ? 0 : Math.min(NumCast(this.selectedLayoutDoc?._width), this.props.width - 20));
+    rtfHeight = () => (!this.selectedLayoutDoc ? 0 : this.rtfWidth() <= NumCast(this.selectedLayoutDoc?._width) ? Math.min(NumCast(this.selectedLayoutDoc?._height), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT);
 
     @action
     docWidth = () => {
-        if (this.selectedDoc) {
-            const layoutDoc = this.selectedDoc;
+        const layoutDoc = this.selectedLayoutDoc;
+        if (layoutDoc) {
             const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._layout_fitWidth);
             if (aspect) return Math.min(NumCast(layoutDoc._width), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.width - 20));
             return Doc.NativeWidth(layoutDoc) ? Math.min(NumCast(layoutDoc._width), this.props.width - 20) : this.props.width - 20;
@@ -142,8 +146,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @action
     docHeight = () => {
-        if (this.selectedDoc && this.dataDoc) {
-            const layoutDoc = this.selectedDoc;
+        const layoutDoc = this.selectedLayoutDoc;
+        if (layoutDoc && this.dataDoc) {
             return Math.max(
                 70,
                 Math.min(
@@ -288,7 +292,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             return (
                 <div ref={this.propertiesDocViewRef} style={{ pointerEvents: 'none', display: 'inline-block', height: panelHeight() }} key={this.selectedDoc[Id]}>
                     <DocumentView
-                        Document={layoutDoc}
+                        Document={this.selectedDoc}
                         DataDoc={this.dataDoc}
                         renderDepth={1}
                         fitContentsToBox={returnTrue}
@@ -328,7 +332,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 => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc! : DocCast(doc)[DocData]));
+        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);
     };
 
@@ -453,7 +457,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
-        const seldoc = this.layoutDocAcls || !this.selectedDoc ? this.selectedDoc : Doc.GetProto(this.selectedDoc);
+        const seldoc = this.layoutDocAcls ? this.selectedLayoutDoc : this.selectedDoc?.[DocData];
         // adds each user to usersAdded
         SharingManager.Instance.users.forEach(eachUser => {
             var userOnDoc = true;
@@ -630,10 +634,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @action
     rotate = (angle: number) => {
         const _centerPoints: { X: number; Y: number }[] = [];
-        if (this.selectedDoc) {
-            const doc = this.selectedDoc;
-            if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
-                const ink = Cast(doc.data, InkField)?.inkData;
+        const doc = this.selectedDoc;
+        const layout = this.selectedLayoutDoc;
+        if (doc && layout) {
+            if (doc.type === DocumentType.INK && doc.x && doc.y && layout._width && layout._height && doc.data) {
+                const ink = Cast(doc.stroke, InkField)?.inkData;
                 if (ink) {
                     const xs = ink.map(p => p.X);
                     const ys = ink.map(p => p.Y);
@@ -646,9 +651,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             }
 
             var index = 0;
-            if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
-                doc.rotation = NumCast(doc.rotation) + angle;
-                const inks = Cast(doc.data, InkField)?.inkData;
+            if (doc.type === DocumentType.INK && doc.x && doc.y && layout._width && layout._height && doc.data) {
+                layout.rotation = NumCast(layout.rotation) + angle;
+                const inks = Cast(doc.stroke, InkField)?.inkData;
                 if (inks) {
                     const newPoints: { X: number; Y: number }[] = [];
                     inks.forEach(ink => {
@@ -656,7 +661,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         const newY = Math.sin(angle) * (ink.X - _centerPoints[index].X) + Math.cos(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].Y;
                         newPoints.push({ X: newX, Y: newY });
                     });
-                    doc.data = new InkField(newPoints);
+                    doc.stroke = new InkField(newPoints);
                     const xs = newPoints.map(p => p.X);
                     const ys = newPoints.map(p => p.Y);
                     const left = Math.min(...xs);
@@ -664,8 +669,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     const right = Math.max(...xs);
                     const bottom = Math.max(...ys);
 
-                    doc._height = bottom - top;
-                    doc._width = right - left;
+                    layout._height = bottom - top;
+                    layout._width = right - left;
                 }
                 index++;
             }
@@ -1178,7 +1183,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return (
             <>
                 <PropertiesSection title="Appearance" isOpen={this.openAppearance} setIsOpen={bool => (this.openAppearance = bool)} onDoubleClick={() => this.CloseAll()}>
-                    {this.selectedDoc?.layout_isSvg ? this.appearanceEditor : null}
+                    {this.selectedLayoutDoc?.layout_isSvg ? this.appearanceEditor : null}
                 </PropertiesSection>
                 <PropertiesSection title="Transform" isOpen={this.openTransform} setIsOpen={bool => (this.openTransform = bool)} onDoubleClick={() => this.CloseAll()}>
                     {this.transformEditor}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 2162d8878..5399d38b4 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -122,7 +122,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 const highlightStyle = ['solid', 'dashed', 'solid', 'solid', 'solid'][highlightIndex];
                 if (highlightIndex) {
                     return {
-                        highlightStyle: doc._isGroup ? "dotted": highlightStyle,
+                        highlightStyle: doc.isGroup ? "dotted": highlightStyle,
                         highlightColor,
                         highlightIndex,
                         highlightStroke: doc.layout_isSvg,
@@ -218,7 +218,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                             ? SettingsManager.userBackgroundColor
                             : doc.annotationOn
                             ? '#00000010' // faint interior for collections on PDFs, images, etc
-                            : doc?._isGroup
+                            : doc?.isGroup
                             ? undefined
                             : doc._type_collection === CollectionViewType.Stacking ?
                             (Colors.DARK_GRAY)
@@ -239,7 +239,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                         doc?.layout_boxShadow,
                         doc?._type_collection === CollectionViewType.Pile
                             ? '4px 4px 10px 2px'
-                            : lockedPosition() || doc?._isGroup || docProps?.LayoutTemplateString
+                            : lockedPosition() || doc?.isGroup || docProps?.LayoutTemplateString
                             ? undefined // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
                             : `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
                     );
@@ -264,7 +264,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             if (DocumentView.ExploreMode || doc?.layout_unrendered) return isInk() ? 'visiblePainted' : 'all';
             if (props?.pointerEvents?.() === 'none') return 'none';
             if (opacity() === 0) return 'none';
-            if (props?.isGroupActive?.() ) return isInk()  ? 'visiblePainted':  (doc?._isGroup )? undefined: 'all' 
+            if (props?.isGroupActive?.() ) return isInk()  ? 'visiblePainted':  (doc?.
+                isGroup )? undefined: 'all' 
             if (props?.isDocumentActive?.() && !props.treeViewDoc) return isInk() ? 'visiblePainted' : 'all';
             return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
         case StyleProp.Decorations:
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f155e64b5..2e8047309 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -19,7 +19,7 @@ import { DragManager } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
 import { DashboardView } from '../DashboardView';
 import { LightboxView } from '../LightboxView';
 import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
@@ -69,7 +69,7 @@ export class CollectionDockingView extends CollectionSubView() {
         (window as any).React = React;
         (window as any).ReactDOM = ReactDOM;
         DragManager.StartWindowDrag = this.StartOtherDrag;
-        this.rootDoc.myTrails; // this is equivalent to having a prefetchProxy for myTrails which is needed for the My Trails button in the UI which assumes that Doc.ActiveDashboard.myTrails is legit...
+        this.Document.myTrails; // this is equivalent to having a prefetchProxy for myTrails which is needed for the My Trails button in the UI which assumes that Doc.ActiveDashboard.myTrails is legit...
     }
 
     /**
@@ -381,8 +381,8 @@ export class CollectionDockingView extends CollectionSubView() {
                 this.layoutDoc.dockingConfig = json;
                 this.layoutDoc.data = new List<Doc>(docs);
             } else {
-                Doc.SetInPlace(this.rootDoc, 'dockingConfig', json, true);
-                Doc.SetInPlace(this.rootDoc, 'data', new List<Doc>(docs), true);
+                Doc.SetInPlace(this.Document, 'dockingConfig', json, true);
+                Doc.SetInPlace(this.Document, 'data', new List<Doc>(docs), true);
             }
         }
         this._flush?.end();
@@ -479,9 +479,9 @@ export class CollectionDockingView extends CollectionSubView() {
     tabDestroyed = (tab: any) => {
         this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
         if (tab.DashDoc && ![DocumentType.PRES].includes(tab.DashDoc?.type) && !tab.contentItem.config.props.keyValue) {
-            Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
+            Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc, undefined, undefined, true);
             // if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
-            if (tab.DashDoc.embedContainer === this.rootDoc) tab.DashDoc.embedContainer = undefined;
+            if (tab.DashDoc.embedContainer === this.Document) tab.DashDoc.embedContainer = undefined;
             if (!tab.DashDoc.embedContainer) Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
             Doc.RemoveDocFromList(Doc.GetProto(tab.DashDoc), 'proto_embeddings', tab.DashDoc);
         }
@@ -512,28 +512,31 @@ export class CollectionDockingView extends CollectionSubView() {
                     _layout_fitWidth: true,
                     title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                 });
-                Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
-                inheritParentAcls(this.rootDoc, docToAdd, false);
+                Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+                inheritParentAcls(this.Document, docToAdd, false);
                 CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
             }
         });
 
-        let addNewDoc = action(() => {
-            const dashboard = Doc.ActiveDashboard;
-            if (dashboard) {
-                dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
-                const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(),
-                    _height: this.props.PanelHeight(),
-                    _layout_fitWidth: true,
-                    _freeform_backgroundGrid: true,
-                    title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
-                });
-                Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
-                inheritParentAcls(this.dataDoc, docToAdd, false);
-                CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
-            }
-        });
+        let addNewDoc = undoable(
+            action(() => {
+                const dashboard = Doc.ActiveDashboard;
+                if (dashboard) {
+                    dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
+                    const docToAdd = Docs.Create.FreeformDocument([], {
+                        _width: this.props.PanelWidth(),
+                        _height: this.props.PanelHeight(),
+                        _layout_fitWidth: true,
+                        _freeform_backgroundGrid: true,
+                        title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
+                    });
+                    Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+                    inheritParentAcls(this.dataDoc, docToAdd, false);
+                    CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
+                }
+            }),
+            'add new tab'
+        );
 
         stack.header?.controlsContainer
             .find('.lm_close') //get the close icon
@@ -569,7 +572,7 @@ export class CollectionDockingView extends CollectionSubView() {
     };
 
     render() {
-        const href = ImageCast(this.rootDoc.thumb)?.url?.href;
+        const href = ImageCast(this.Document.thumb)?.url?.href;
         return this.props.renderDepth > -1 ? (
             <div>
                 {href ? (
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index b190eb23d..3c555e731 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,43 +1,26 @@
 import React = require('react');
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { Toggle, ToggleType, Type } from 'browndash-components';
 import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { ColorState } from 'react-color';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Document } from '../../../fields/documentSchemas';
-import { Id } from '../../../fields/FieldSymbols';
-import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { RichTextField } from '../../../fields/RichTextField';
-import { listSpec } from '../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { GestureUtils } from '../../../pen-gestures/GestureUtils';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
-import { Docs } from '../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
 import { AntimodeMenu } from '../AntimodeMenu';
 import { EditableView } from '../EditableView';
-import { GestureOverlay } from '../GestureOverlay';
-import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
-import { LightboxView } from '../LightboxView';
 import { MainView } from '../MainView';
-import { media_state } from '../nodes/AudioBox';
-import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
-import { DocumentView, DocumentViewInternal, OpenWhereMod } from '../nodes/DocumentView';
-import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
 import { DefaultStyleProvider } from '../StyleProvider';
-import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionLinearView } from './collectionLinear';
 import './CollectionMenu.scss';
 
@@ -66,7 +49,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
 
     componentDidMount() {
         reaction(
-            () => SelectionManager.Views().length && SelectionManager.Views()[0],
+            () => SelectionManager.Views().lastElement(),
             view => view && this.SetSelection(view)
         );
     }
@@ -174,7 +157,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
             </div>
         );
 
-        // NEW BUTTONS
         //dash col linear view buttons
         const contMenuButtons = (
             <div
@@ -189,21 +171,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
         );
 
         return contMenuButtons;
-
-        // const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
-        //     <button className="antimodeMenu-button" onClick={this.toggleMenuPin} style={{ backgroundColor: "#121721" }}>
-        //         <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
-        //     </button>
-        // </Tooltip>;
-
-        // //OLD BUTTONS
-        // return this.getElement(!this.SelectedCollection ? [/*button*/] :
-        //     [<CollectionViewBaseChrome key="chrome"
-        //         docView={this.SelectedCollection}
-        //         fieldKey={this.SelectedCollection.LayoutFieldKey}
-        //         type={StrCast(this.SelectedCollection?.props.Document._type_collection, CollectionViewType.Invalid) as CollectionViewType} />,
-        //         prop,
-        //         /*button*/]);
     }
 }
 
@@ -228,7 +195,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     _templateCommand = {
         params: ['target', 'source'],
         title: 'item view',
-        script: 'self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])',
+        script: 'this.target.childLayoutTemplate = getDocTemplate(this.source?.[0])',
         immediate: undoBatch((source: Doc[]) => {
             let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
             try {
@@ -250,24 +217,24 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     _narrativeCommand = {
         params: ['target', 'source'],
         title: 'child click view',
-        script: 'self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])',
+        script: 'this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])',
         immediate: undoBatch((source: Doc[]) => source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]))),
         initialize: emptyFunction,
     };
     _contentCommand = {
         params: ['target', 'source'],
         title: 'set content',
-        script: 'getProto(self.target).data = copyField(self.source);',
+        script: 'getProto(this.target).data = copyField(this.source);',
         immediate: undoBatch((source: Doc[]) => (Doc.GetProto(this.target).data = new List<Doc>(source))),
         initialize: emptyFunction,
     };
     _onClickCommand = {
         params: ['target', 'proxy'],
         title: 'copy onClick',
-        script: `{ if (self.proxy?.[0]) {
-             getProto(self.proxy[0]).onClick = copyField(self.target.onClick); 
-             getProto(self.proxy[0]).target = self.target.target;
-             getProto(self.proxy[0]).source = copyField(self.target.source); 
+        script: `{ if (this.proxy?.[0]) {
+             getProto(this.proxy[0]).onClick = copyField(this.target.onClick); 
+             getProto(this.proxy[0]).target = this.target.target;
+             getProto(this.proxy[0]).source = copyField(this.target.source); 
             }}`,
         immediate: undoBatch((source: Doc[]) => {}),
         initialize: emptyFunction,
@@ -275,7 +242,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     _viewCommand = {
         params: ['target'],
         title: 'bookmark view',
-        script: "self.target._freeform_panX = self['target-freeform_panX']; self.target._freeform_panY = self['target-freeform_panY']; self.target._freeform_scale = self['target_freeform_scale']; gotoFrame(self.target, self['target-currentFrame']);",
+        script: "this.target._freeform_panX = self['target-freeform_panX']; this.target._freeform_panY = this['target-freeform_panY']; this.target._freeform_scale = this['target_freeform_scale']; gotoFrame(this.target, this['target-currentFrame']);",
         immediate: undoBatch((source: Doc[]) => {
             this.target._freeform_panX = 0;
             this.target._freeform_panY = 0;
@@ -292,22 +259,22 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     _clusterCommand = {
         params: ['target'],
         title: 'fit content',
-        script: 'self.target._freeform_fitContentsToBox = !self.target._freeform_fitContentsToBox;',
+        script: 'this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox;',
         immediate: undoBatch((source: Doc[]) => (this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox)),
         initialize: emptyFunction,
     };
     _fitContentCommand = {
         params: ['target'],
         title: 'toggle clusters',
-        script: 'self.target._freeform_useClusters = !self.target._freeform_useClusters;',
+        script: 'this.target._freeform_useClusters = !this.target._freeform_useClusters;',
         immediate: undoBatch((source: Doc[]) => (this.target._freeform_useClusters = !this.target._freeform_useClusters)),
         initialize: emptyFunction,
     };
     _saveFilterCommand = {
         params: ['target'],
         title: 'save filter',
-        script: `self.target._childFilters = compareLists(self['target-childFilters'],self.target._childFilters) ? undefined : copyField(self['target-childFilters']); 
-                 self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
+        script: `this.target._childFilters = compareLists(this['target-childFilters'],this.target._childFilters) ? undefined : copyField(this['target-childFilters']); 
+        this.target._searchFilterDocs = compareLists(this['target-searchFilterDocs'],this.target._searchFilterDocs) ? undefined: copyField(this['target-searchFilterDocs']);`,
         immediate: undoBatch((source: Doc[]) => {
             this.target._childFilters = undefined;
             this.target._searchFilterDocs = undefined;
@@ -390,57 +357,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         this.document._facetWidth = 0;
     };
 
-    @computed get subChrome() {
-        switch (
-            this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type // bcz: ARgh!  hack to get menu for tree view outline items
-        ) {
-            default:
-                return this.otherSubChrome;
-            case CollectionViewType.Invalid:
-            case CollectionViewType.Freeform:
-                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />;
-            case CollectionViewType.Stacking:
-                return <CollectionStackingViewChrome key="collchrome" {...this.props} />;
-            case CollectionViewType.NoteTaking:
-                return <CollectionNoteTakingViewChrome key="collchrome" {...this.props} />;
-            case CollectionViewType.Schema:
-                return <CollectionSchemaViewChrome key="collchrome" {...this.props} />;
-            case CollectionViewType.Tree:
-                return <CollectionTreeViewChrome key="collchrome" {...this.props} />;
-            case CollectionViewType.Masonry:
-                return <CollectionStackingViewChrome key="collchrome" {...this.props} />;
-            case CollectionViewType.Carousel:
-            case CollectionViewType.Carousel3D:
-                return <Collection3DCarouselViewChrome key="collchrome" {...this.props} />;
-            case CollectionViewType.Grid:
-                return <CollectionGridViewChrome key="collchrome" {...this.props} />;
-            case CollectionViewType.Docking:
-                return <CollectionDockingChrome key="collchrome" {...this.props} />;
-        }
-    }
-
-    @computed get otherSubChrome() {
-        const docType = this.props.docView.Document.type;
-        switch (docType) {
-            default:
-                return null;
-            case DocumentType.IMG:
-                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
-            case DocumentType.PDF:
-                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
-            case DocumentType.INK:
-                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
-            case DocumentType.WEB:
-                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
-            case DocumentType.VID:
-                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
-            case DocumentType.RTF:
-                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} isDoc={true} />;
-            case DocumentType.MAP:
-                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
-        }
-    }
-
     private dropDisposer?: DragManager.DragDropDisposer;
     protected createDropTarget = (ele: HTMLDivElement) => {
         this.dropDisposer?.();
@@ -517,593 +433,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
             </div>
         );
     }
-
-    @computed get viewModes() {
-        const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear];
-        const isPres: boolean = this.document && this.document.type === DocumentType.PRES;
-        return isPres ? null : (
-            <div className="collectionViewBaseChrome-viewModes">
-                <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
-                    <div className="commandEntry-outerDiv" ref={this._viewRef} onPointerDown={this.dragViewDown}>
-                        <button className={'antimodeMenu-button'}>
-                            <FontAwesomeIcon icon="bullseye" size="lg" />
-                        </button>
-                        <select className="collectionViewBaseChrome-viewPicker" onPointerDown={stopPropagation} onChange={this.viewChanged} value={StrCast(this.props.type)}>
-                            {Object.values(CollectionViewType)
-                                .filter(type => !excludedViewTypes.includes(type))
-                                .map(type => (
-                                    <option key={Utils.GenerateGuid()} className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value={type}>
-                                        {type[0].toUpperCase() + type.substring(1)}
-                                    </option>
-                                ))}
-                        </select>
-                    </div>
-                </Tooltip>
-            </div>
-        );
-    }
-
-    @computed get selectedDocumentView() {
-        return SelectionManager.Views().lastElement();
-    }
-    @computed get selectedDoc() {
-        return SelectionManager.Docs().lastElement();
-    }
-    @computed get notACollection() {
-        if (this.selectedDoc) {
-            const layoutField = Doc.LayoutField(this.selectedDoc);
-            return this.props.type === CollectionViewType.Docking || (typeof layoutField === 'string' && !layoutField?.includes('CollectionView'));
-        } else return false;
-    }
-
-    @undoBatch
-    @action
-    startRecording = () => {
-        const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _layout_fitWidth: true, _width: 400, _height: 200, mediaState: media_state.PendingRecording });
-        CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
-    };
-
-    @computed
-    get recordButton() {
-        const targetDoc = this.selectedDoc;
-        return (
-            <Tooltip key="record" title={<div className="dash-tooltip">{'Capture screen'}</div>} placement="top">
-                <button className="antimodeMenu-button" onClick={e => this.startRecording()}>
-                    <div className="recordButtonOutline" style={{}}>
-                        <div className="recordButtonInner" style={{}}></div>
-                    </div>
-                </button>
-            </Tooltip>
-        );
-    }
-
-    @undoBatch
-    onEmbed = () => {
-        if (this.selectedDoc && this.selectedDocumentView) {
-            // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
-            // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
-            // copy.y = NumCast(this.selectedDoc.y) + 30;
-            // this.selectedDocumentView.props.addDocument?.(copy);
-            const embedding = Doc.MakeEmbedding(this.selectedDoc);
-            embedding.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
-            embedding.y = NumCast(this.selectedDoc.y) + 30;
-            this.selectedDocumentView.props.addDocument?.(embedding);
-        }
-    };
-    onEmbedButtonDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction);
-    };
-
-    @undoBatch
-    onEmbedButtonMoved = (e: PointerEvent) => {
-        const contentDiv = this.selectedDocumentView?.ContentDiv;
-        if (contentDiv && this.selectedDoc) {
-            const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
-            const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
-            dragData.defaultDropAction = 'embed';
-            dragData.canEmbed = true;
-            DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, {
-                offsetX: offset[0],
-                offsetY: offset[1],
-                hideSource: false,
-            });
-            return true;
-        }
-        return false;
-    };
-
-    @computed
-    get embedButton() {
-        const targetDoc = this.selectedDoc;
-        return !targetDoc || targetDoc.type === DocumentType.PRES ? null : (
-            <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an embedding'}</div>} placement="top">
-                <button className="antimodeMenu-button" onPointerDown={this.onEmbedButtonDown} onClick={this.onEmbed} style={{ cursor: 'drag' }}>
-                    <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
-                </button>
-            </Tooltip>
-        );
-    }
-
-    @computed get lightboxButton() {
-        const targetDoc = this.selectedDoc;
-        return !targetDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">{'View in Lightbox'}</div>} placement="top">
-                <button
-                    className="antimodeMenu-button"
-                    onPointerDown={() => {
-                        const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
-                        LightboxView.Instance.SetLightboxDoc(targetDoc, undefined, docs);
-                    }}>
-                    <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
-                </button>
-            </Tooltip>
-        );
-    }
-
-    @computed get toggleOverlayButton() {
-        return (
-            <>
-                <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
-                    <button
-                        className={'antimodeMenu-button'}
-                        key="float"
-                        style={{
-                            backgroundColor: this.props.docView.layoutDoc.z ? '121212' : undefined,
-                            pointerEvents: this.props.docView.props.docViewPath().lastElement()?.rootDoc?._type_collection !== CollectionViewType.Freeform ? 'none' : undefined,
-                            color: this.props.docView.props.docViewPath().lastElement()?.rootDoc?._type_collection !== CollectionViewType.Freeform ? 'dimgrey' : undefined,
-                        }}
-                        onClick={undoBatch(() => this.props.docView.CollectionFreeFormDocumentView?.float())}>
-                        <FontAwesomeIcon icon={['fab', 'buffer']} size={'lg'} />
-                    </button>
-                </Tooltip>
-            </>
-        );
-    }
-
     render() {
         return (
             <div className="collectionMenu-cont">
                 <div className="collectionMenu">
-                    <div className="collectionViewBaseChrome">
-                        {this.notACollection || this.props.type === CollectionViewType.Invalid ? null : this.viewModes}
-                        <div className="collectionMenu-divider" key="divider1"></div>
-                        {this.embedButton}
-                        {/* {this.pinButton} */}
-                        {this.toggleOverlayButton}
-                        <div className="collectionMenu-divider" key="divider2"></div>
-                        {this.subChrome}
-                        <div className="collectionMenu-divider" key="divider3"></div>
-                        {this.lightboxButton}
-                        {this.recordButton}
-                        {!this._buttonizableCommands ? null : this.templateChrome}
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
-
-@observer
-export class CollectionDockingChrome extends React.Component<CollectionViewMenuProps> {
-    render() {
-        return null;
-    }
-}
-
-@observer
-export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean; isDoc?: boolean }> {
-    public static Instance: CollectionFreeFormViewChrome;
-    constructor(props: any) {
-        super(props);
-        CollectionFreeFormViewChrome.Instance = this;
-    }
-    get document() {
-        return this.props.docView.props.Document;
-    }
-    @computed get dataField() {
-        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? '_annotations' : '')];
-    }
-    @computed get childDocs() {
-        return DocListCast(this.dataField);
-    }
-    @computed get selectedDocumentView() {
-        return SelectionManager.Views().lastElement();
-    }
-    @computed get selectedDoc() {
-        return SelectionManager.Docs().lastElement();
-    }
-    @computed get isText() {
-        return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false;
-    }
-
-    public static gotoKeyFrame(doc: Doc, newFrame: number) {
-        if (doc) {
-            const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]);
-            const currentFrame = Cast(doc._currentFrame, 'number', null);
-            if (currentFrame === undefined) {
-                doc._currentFrame = 0;
-                CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
-            }
-            CollectionFreeFormView.updateKeyframe(undefined, [...childDocs, doc], currentFrame || 0);
-            doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
-        }
-    }
-
-    _keyTimer: NodeJS.Timeout | undefined;
-    @undoBatch
-    @action
-    nextKeyframe = (): void => {
-        const currentFrame = Cast(this.document._currentFrame, 'number', null);
-        if (currentFrame === undefined) {
-            this.document._currentFrame = 0;
-            CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
-        }
-        this._keyTimer = CollectionFreeFormView.updateKeyframe(this._keyTimer, [...this.childDocs, this.document], currentFrame || 0);
-        this.document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
-        this.document.lastFrame = Math.max(NumCast(this.document._currentFrame), NumCast(this.document.lastFrame));
-    };
-    @undoBatch
-    @action
-    prevKeyframe = (): void => {
-        const currentFrame = Cast(this.document._currentFrame, 'number', null);
-        if (currentFrame === undefined) {
-            this.document._currentFrame = 0;
-            CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
-        }
-        this._keyTimer = CollectionFreeFormView.gotoKeyframe(this._keyTimer, [...this.childDocs, this.document], 1000);
-        this.document._currentFrame = Math.max(0, (currentFrame || 0) - 1);
-    };
-
-    private _palette = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', ''];
-    private _width = ['1', '5', '10', '100'];
-    private _dotsize = [10, 20, 30, 40];
-    private _draw = ['∿', '=', '⎯', '→', '↔︎', 'ロ', 'O'];
-    private _head = ['', '', '', '', 'arrow', '', ''];
-    private _end = ['', '', '', 'arrow', 'arrow', '', ''];
-    private _shapePrims = ['', '', 'line', 'line', 'line', 'rectangle', 'circle'] as GestureUtils.Gestures[];
-    private _title = ['pen', 'highlighter', 'line', 'line with arrow', 'line with double arrows', 'square', 'circle'];
-    private _faName = ['pen-fancy', 'highlighter', 'minus', 'long-arrow-alt-right', 'arrows-alt-h', 'square', 'circle'];
-    @observable _selectedPrimitive = this._shapePrims.length;
-    @observable _keepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
-    @observable _colorBtn = false;
-    @observable _widthBtn = false;
-    @observable _fillBtn = false;
-
-    @action clearKeepPrimitiveMode() {
-        this._selectedPrimitive = this._shapePrims.length;
-    }
-    @action primCreated() {
-        if (!this._keepPrimitiveMode) {
-            //get out of ink mode after each stroke=
-            if (Doc.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
-            Doc.ActiveTool = InkTool.None;
-            this._selectedPrimitive = this._shapePrims.length;
-            SetActiveArrowStart('none');
-            SetActiveArrowEnd('none');
-        }
-    }
-
-    @action
-    changeColor = (color: string, type: string) => {
-        const col: ColorState = {
-            hex: color,
-            hsl: { a: 0, h: 0, s: 0, l: 0, source: '' },
-            hsv: { a: 0, h: 0, s: 0, v: 0, source: '' },
-            rgb: { a: 0, r: 0, b: 0, g: 0, source: '' },
-            oldHue: 0,
-            source: '',
-        };
-        if (type === 'color') {
-            SetActiveInkColor(Utils.colorString(col));
-        } else if (type === 'fill') {
-            SetActiveFillColor(Utils.colorString(col));
-        }
-    };
-
-    @action
-    editProperties = (value: any, field: string) => {
-        SelectionManager.Views().forEach(
-            action((element: DocumentView) => {
-                const doc = Document(element.rootDoc);
-                if (doc.type === DocumentType.INK) {
-                    switch (field) {
-                        case 'width':
-                            doc.stroke_width = Number(value);
-                            break;
-                        case 'color':
-                            doc.color = String(value);
-                            break;
-                        case 'fill':
-                            doc.fillColor = String(value);
-                            break;
-                        case 'dash':
-                            doc.stroke_dash = value;
-                    }
-                }
-            })
-        );
-    };
-
-    @computed get drawButtons() {
-        const func = action((e: React.MouseEvent | React.PointerEvent, i: number, keep: boolean) => {
-            this._keepPrimitiveMode = keep;
-            // these are for shapes
-            if (this._selectedPrimitive !== i) {
-                this._selectedPrimitive = i;
-                if (this._title[i] === 'highlighter') {
-                    Doc.ActiveTool = InkTool.Highlighter;
-                    GestureOverlay.Instance.SavedColor = ActiveInkColor();
-                    SetActiveInkColor('rgba(245, 230, 95, 0.75)');
-                } else {
-                    Doc.ActiveTool = InkTool.Pen;
-                }
-                SetActiveArrowStart(this._head[i]);
-                SetActiveArrowEnd(this._end[i]);
-                SetActiveBezierApprox('300');
-
-                if (GestureOverlay.Instance) GestureOverlay.Instance.InkShape = this._shapePrims[i];
-            } else {
-                this._selectedPrimitive = this._shapePrims.length;
-                Doc.ActiveTool = InkTool.None;
-                SetActiveArrowStart('');
-                SetActiveArrowEnd('');
-                if (GestureOverlay.Instance) GestureOverlay.Instance.InkShape = undefined;
-                SetActiveBezierApprox('0');
-            }
-            e.stopPropagation();
-        });
-        return (
-            <div className="btn-draw" key="draw">
-                {this._draw.map((icon, i) => (
-                    <Tooltip key={icon} title={<div className="dash-tooltip">{this._title[i]}</div>} placement="bottom">
-                        <button className="antimodeMenu-button" onPointerDown={e => func(e, i, false)} onDoubleClick={e => func(e, i, true)} style={{ backgroundColor: i === this._selectedPrimitive ? '525252' : '', fontSize: '20' }}>
-                            <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
-                        </button>
-                    </Tooltip>
-                ))}
-            </div>
-        );
-    }
-
-    toggleButton = (key: string, value: boolean, setter: () => {}, icon: FontAwesomeIconProps['icon'], ele: JSX.Element | null) => {
-        return (
-            <Tooltip title={<div className="dash-tooltip">{key}</div>} placement="bottom">
-                <button className="antimodeMenu-button" key={key} onPointerDown={action(e => setter())} style={{ backgroundColor: value ? '121212' : '' }}>
-                    <FontAwesomeIcon icon={icon} size="lg" />
-                    {ele}
-                </button>
-            </Tooltip>
-        );
-    };
-
-    @computed get widthPicker() {
-        const widthPicker = this.toggleButton('stroke width', this._widthBtn, () => (this._widthBtn = !this._widthBtn), 'bars', null);
-        return !this._widthBtn ? (
-            widthPicker
-        ) : (
-            <div className="btn2-group" key="width">
-                {widthPicker}
-                {this._width.map((wid, i) => (
-                    <Tooltip title={<div className="dash-tooltip">change width</div>} placement="bottom">
-                        <button
-                            className="antimodeMenu-button"
-                            key={wid}
-                            onPointerDown={action(() => {
-                                SetActiveInkWidth(wid);
-                                this._widthBtn = false;
-                                this.editProperties(wid, 'width');
-                            })}
-                            style={{ backgroundColor: this._widthBtn ? '121212' : '', zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: 'center' }}>
-                            •
-                        </button>
-                    </Tooltip>
-                ))}
-            </div>
-        );
-    }
-
-    @computed get colorPicker() {
-        const colorPicker = this.toggleButton('stroke color', this._colorBtn, () => (this._colorBtn = !this._colorBtn), 'pen-nib', <div className="color-previewI" style={{ backgroundColor: ActiveInkColor() ?? '121212' }} />);
-        return !this._colorBtn ? (
-            colorPicker
-        ) : (
-            <div className="btn-group" key="color">
-                {colorPicker}
-                {this._palette.map(color => (
-                    <button
-                        className="antimodeMenu-button"
-                        key={color}
-                        onPointerDown={action(() => {
-                            this.changeColor(color, 'color');
-                            this._colorBtn = false;
-                            this.editProperties(color, 'color');
-                        })}
-                        style={{ backgroundColor: this._colorBtn ? '121212' : '', zIndex: 1001 }}>
-                        {/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */}
-                        <div className="color-previewII" style={{ backgroundColor: color }}>
-                            {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
-                        </div>
-                    </button>
-                ))}
-            </div>
-        );
-    }
-    @computed get fillPicker() {
-        const fillPicker = this.toggleButton('shape fill color', this._fillBtn, () => (this._fillBtn = !this._fillBtn), 'fill-drip', <div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? '121212' }} />);
-        return !this._fillBtn ? (
-            fillPicker
-        ) : (
-            <div className="btn-group" key="fill">
-                {fillPicker}
-                {this._palette.map(color => (
-                    <button
-                        className="antimodeMenu-button"
-                        key={color}
-                        onPointerDown={action(() => {
-                            this.changeColor(color, 'fill');
-                            this._fillBtn = false;
-                            this.editProperties(color, 'fill');
-                        })}
-                        style={{ backgroundColor: this._fillBtn ? '121212' : '', zIndex: 1001 }}>
-                        <div className="color-previewII" style={{ backgroundColor: color }}>
-                            {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
-                        </div>
-                    </button>
-                ))}
-            </div>
-        );
-    }
-
-    render() {
-        return !this.props.docView.layoutDoc ? null : (
-            <div className="collectionFreeFormMenu-cont">
-                <RichTextMenu key="rich" />
-                {!this.isText ? (
-                    <>
-                        {this.drawButtons}
-                        {this.widthPicker}
-                        {this.colorPicker}
-                        {this.fillPicker}
-                        {Doc.noviceMode || this.props.isDoc ? null : (
-                            <>
-                                <Tooltip key="back" title={<div className="dash-tooltip">Back Frame</div>} placement="bottom">
-                                    <div className="backKeyframe" onClick={this.prevKeyframe}>
-                                        <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
-                                    </div>
-                                </Tooltip>
-                                <Tooltip key="num" title={<div className="dash-tooltip">Frame number</div>} placement="bottom">
-                                    <div
-                                        className="numKeyframe"
-                                        style={{ color: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? 'white' : 'black', backgroundColor: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? '#5B9FDD' : '#AEDDF8' }}
-                                        onClick={action(() => this.props.docView.ComponentView?.setKeyFrameEditing?.(!this.props.docView.ComponentView?.getKeyFrameEditing?.()))}>
-                                        {NumCast(this.document._currentFrame)}
-                                    </div>
-                                </Tooltip>
-                                <Tooltip key="fwd" title={<div className="dash-tooltip">Forward Frame</div>} placement="bottom">
-                                    <div className="fwdKeyframe" onClick={this.nextKeyframe}>
-                                        <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
-                                    </div>
-                                </Tooltip>
-                            </>
-                        )}
-                    </>
-                ) : null}
-                {!this.selectedDocumentView?.ComponentView?.menuControls ? null : this.selectedDocumentView?.ComponentView?.menuControls?.()}
-            </div>
-        );
-    }
-}
-@observer
-export class CollectionStackingViewChrome extends React.Component<CollectionViewMenuProps> {
-    @observable private _currentKey: string = '';
-    @observable private suggestions: string[] = [];
-
-    get document() {
-        return this.props.docView.props.Document;
-    }
-
-    @computed private get descending() {
-        return StrCast(this.document._columnsSort) === 'descending';
-    }
-    @computed get pivotField() {
-        return StrCast(this.document._pivotField);
-    }
-
-    getKeySuggestions = async (value: string): Promise<string[]> => {
-        const val = value.toLowerCase();
-        const docs = DocListCast(this.document[this.props.fieldKey]);
-
-        if (Doc.noviceMode) {
-            if (docs instanceof Doc) {
-                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
-                return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
-            }
-            const keys = new Set<string>();
-            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-            const noviceKeys = Array.from(keys).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_'));
-            return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
-        }
-
-        if (docs instanceof Doc) {
-            return Object.keys(docs).filter(key => key.toLowerCase().indexOf(val) > -1);
-        } else {
-            const keys = new Set<string>();
-            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-            return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
-        }
-    };
-
-    @action
-    onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
-        this._currentKey = newValue;
-    };
-
-    getSuggestionValue = (suggestion: string) => suggestion;
-
-    renderSuggestion = (suggestion: string) => {
-        return <p>{suggestion}</p>;
-    };
-
-    onSuggestionFetch = async ({ value }: { value: string }) => {
-        const sugg = await this.getKeySuggestions(value);
-        runInAction(() => {
-            this.suggestions = sugg;
-        });
-    };
-
-    @action
-    onSuggestionClear = () => {
-        this.suggestions = [];
-    };
-
-    @action
-    setValue = (value: string) => {
-        this.document._pivotField = value;
-        return true;
-    };
-
-    @action toggleSort = () => {
-        this.document._columnsSort = this.document._columnsSort === 'descending' ? 'ascending' : this.document._columnsSort === 'ascending' ? undefined : 'descending';
-    };
-    @action resetValue = () => {
-        this._currentKey = this.pivotField;
-    };
-
-    render() {
-        const doctype = this.props.docView.Document.type;
-        const isPres: boolean = doctype === DocumentType.PRES;
-        return isPres ? null : (
-            <div className="collectionStackingViewChrome-cont">
-                <div className="collectionStackingViewChrome-pivotField-cont">
-                    <div className="collectionStackingViewChrome-pivotField-label">GROUP BY:</div>
-                    <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? '180' : '0'}deg)` }}>
-                        <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
-                    </div>
-                    <div className="collectionStackingViewChrome-pivotField">
-                        <EditableView
-                            GetValue={() => this.pivotField}
-                            autosuggestProps={{
-                                resetValue: this.resetValue,
-                                value: this._currentKey,
-                                onChange: this.onKeyChange,
-                                autosuggestProps: {
-                                    inputProps: {
-                                        value: this._currentKey,
-                                        onChange: this.onKeyChange,
-                                    },
-                                    getSuggestionValue: this.getSuggestionValue,
-                                    suggestions: this.suggestions,
-                                    alwaysRenderSuggestions: true,
-                                    renderSuggestion: this.renderSuggestion,
-                                    onSuggestionsFetchRequested: this.onSuggestionFetch,
-                                    onSuggestionsClearRequested: this.onSuggestionClear,
-                                },
-                            }}
-                            oneLine
-                            SetValue={this.setValue}
-                            contents={this.pivotField ? this.pivotField : 'N/A'}
-                        />
-                    </div>
+                    <div className="collectionViewBaseChrome">{!this._buttonizableCommands ? null : this.templateChrome}</div>
                 </div>
             </div>
         );
@@ -1230,123 +564,6 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
     }
 }
 
-@observer
-export class CollectionSchemaViewChrome extends React.Component<CollectionViewMenuProps> {
-    // private _textwrapAllRows: boolean = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
-    get document() {
-        return this.props.docView.props.Document;
-    }
-
-    @undoBatch
-    togglePreview = () => {
-        const dividerWidth = 4;
-        const borderWidth = 0;
-        const panelWidth = this.props.docView.props.PanelWidth();
-        const previewWidth = NumCast(this.document.schema_previewWidth);
-        const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
-        this.document.schema_previewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
-    };
-
-    @undoBatch
-    @action
-    toggleTextwrap = async () => {
-        const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []);
-        if (textwrappedRows.length) {
-            this.document.textwrappedSchemaRows = new List<string>([]);
-        } else {
-            const docs = DocListCast(this.document[this.props.fieldKey]);
-            const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
-            this.document.textwrappedSchemaRows = new List<string>(allRows);
-        }
-    };
-
-    render() {
-        const previewWidth = NumCast(this.document.schema_previewWidth);
-        const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []).length > 0;
-
-        return (
-            <div className="collectionSchemaViewChrome-cont">
-                <div className="collectionSchemaViewChrome-toggle">
-                    <div className="collectionSchemaViewChrome-label">Show Preview: </div>
-                    <div className="collectionSchemaViewChrome-toggler" onClick={this.togglePreview}>
-                        <div className={'collectionSchemaViewChrome-togglerButton' + (previewWidth !== 0 ? ' on' : ' off')}>{previewWidth !== 0 ? 'on' : 'off'}</div>
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
-
-@observer
-export class CollectionTreeViewChrome extends React.Component<CollectionViewMenuProps> {
-    get document() {
-        return this.props.docView.props.Document;
-    }
-    get sortAscending() {
-        return this.document[this.props.fieldKey + '-sortAscending'];
-    }
-    set sortAscending(value) {
-        this.document[this.props.fieldKey + '-sortAscending'] = value;
-    }
-    @computed private get ascending() {
-        return Cast(this.sortAscending, 'boolean', null);
-    }
-
-    @action toggleSort = () => {
-        if (this.sortAscending) this.sortAscending = undefined;
-        else if (this.sortAscending === undefined) this.sortAscending = false;
-        else this.sortAscending = true;
-    };
-
-    render() {
-        return (
-            <div className="collectionTreeViewChrome-cont">
-                <button className="collectionTreeViewChrome-sort" onClick={this.toggleSort}>
-                    <div className="collectionTreeViewChrome-sortLabel">Sort</div>
-                    <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? '90' : this.ascending ? '180' : '0'}deg)` }}>
-                        <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
-                    </div>
-                </button>
-            </div>
-        );
-    }
-}
-
-// Enter scroll speed for 3D Carousel
-@observer
-export class Collection3DCarouselViewChrome extends React.Component<CollectionViewMenuProps> {
-    get document() {
-        return this.props.docView.props.Document;
-    }
-    @computed get scrollSpeed() {
-        return this.document._autoScrollSpeed;
-    }
-
-    @action
-    setValue = (value: string) => {
-        const numValue = Number(StrCast(value));
-        if (numValue > 0) {
-            this.document._autoScrollSpeed = numValue;
-            return true;
-        }
-        return false;
-    };
-
-    render() {
-        return (
-            <div className="collection3DCarouselViewChrome-cont">
-                <div className="collection3DCarouselViewChrome-scrollSpeed-cont">
-                    {/* {FormattedTextBox.Focused ? <RichTextMenu /> : null} */}
-                    <div className="collectionStackingViewChrome-scrollSpeed-label">AUTOSCROLL SPEED:</div>
-                    <div className="collection3DCarouselViewChrome-scrollSpeed">
-                        <EditableView GetValue={() => StrCast(this.scrollSpeed)} oneLine SetValue={this.setValue} contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
-
 /**
  * Chrome for grid view.
  */
@@ -1492,12 +709,6 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
                     <input className="collectionGridViewChrome-columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
                     <input className="collectionGridViewChrome-columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
                 </span>
-                {/* <span className="grid-control">
-                    <span className="grid-icon">
-                        <FontAwesomeIcon icon="text-height" size="1x" />
-                    </span>
-                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
-                </span> */}
                 <span className="grid-control" style={{ width: this.resize ? '12%' : '20%' }}>
                     <input type="checkbox" style={{ marginRight: 5 }} onChange={this.toggleCollisions} checked={!this.document.gridPreventCollision} />
                     <label className="flexLabel">{this.resize ? 'Coll' : 'Collisions'}</label>
@@ -1526,6 +737,3 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         );
     }
 }
-ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
-    CollectionFreeFormViewChrome.gotoKeyFrame(doc, newFrame);
-});
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index ac916fef3..e99bc2f08 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -229,8 +229,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             <DocumentView
                 ref={r => (dref = r || undefined)}
                 Document={doc}
-                pointerEvents={this.blockPointerEventsWhenDragging}
                 DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
+                pointerEvents={this.blockPointerEventsWhenDragging}
                 renderDepth={this.props.renderDepth + 1}
                 PanelWidth={width}
                 PanelHeight={height}
@@ -635,7 +635,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 key="notes"
                 style={{
                     overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
-                    background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
+                    background: this.props.styleProvider?.(this.Document, this.props, StyleProp.BackgroundColor),
                     pointerEvents: this.backgroundEvents,
                 }}
                 onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index abb12a8ab..ad8144466 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -43,7 +43,7 @@ export class CollectionPileView extends CollectionSubView() {
     removePileDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
         (doc instanceof Doc ? [doc] : doc).forEach(d => Doc.deiconifyView(d));
         const ret = this.props.moveDocument?.(doc, targetCollection, addDoc) || false;
-        if (ret && !DocListCast(this.rootDoc[this.fieldKey ?? 'data']).length) this.props.DocumentView?.().props.removeDocument?.(this.rootDoc);
+        if (ret && !DocListCast(this.dataDoc[this.fieldKey ?? 'data']).length) this.props.DocumentView?.().props.removeDocument?.(this.Document);
         return ret;
     };
 
@@ -78,12 +78,12 @@ export class CollectionPileView extends CollectionSubView() {
     toggleStarburst = action(() => {
         this.layoutDoc._freeform_scale = undefined;
         if (this.layoutEngine() === computeStarburstLayout.name) {
-            if (NumCast(this.rootDoc._width) !== NumCast(this.rootDoc._starburstDiameter, 500)) {
-                this.rootDoc._starburstDiameter = NumCast(this.rootDoc._width);
+            if (NumCast(this.layoutDoc._width) !== NumCast(this.Document._starburstDiameter, 500)) {
+                this.Document._starburstDiameter = NumCast(this.layoutDoc._width);
             }
             const defaultSize = 110;
-            this.rootDoc.x = NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2;
-            this.rootDoc.y = NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2;
+            this.Document.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width) / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2;
+            this.Document.y = NumCast(this.Document.y) + NumCast(this.layoutDoc._height) / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2;
             this.layoutDoc._width = NumCast(this.layoutDoc._freeform_pileWidth, defaultSize);
             this.layoutDoc._height = NumCast(this.layoutDoc._freeform_pileHeight, defaultSize);
             DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
@@ -91,9 +91,9 @@ export class CollectionPileView extends CollectionSubView() {
             this.layoutDoc._freeform_panY = -10;
             this.props.Document._freeform_pileEngine = computePassLayout.name;
         } else {
-            const defaultSize = NumCast(this.rootDoc._starburstDiameter, 400);
-            this.rootDoc.x = NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) / 2 - defaultSize / 2;
-            this.rootDoc.y = NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) / 2 - defaultSize / 2;
+            const defaultSize = NumCast(this.Document._starburstDiameter, 400);
+            this.Document.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width) / 2 - defaultSize / 2;
+            this.Document.y = NumCast(this.Document.y) + NumCast(this.layoutDoc._height) / 2 - defaultSize / 2;
             this.layoutDoc._freeform_pileWidth = NumCast(this.layoutDoc._width);
             this.layoutDoc._freeform_pileHeight = NumCast(this.layoutDoc._height);
             this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 3351ca48e..7bde2cb5f 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -192,7 +192,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         CollectionStackedTimeline.SelectingRegion = this;
                     } else {
                         this._markerEnd = this.currentTime;
-                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
+                        CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
                         this._markerEnd = undefined;
                         CollectionStackedTimeline.SelectingRegion = undefined;
                     }
@@ -258,7 +258,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         this._markerEnd = tmp;
                     }
                     if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
-                        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
+                        const anchor = CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
                         setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
                     }
                     (!isClick || !wasSelecting) && (this._markerEnd = undefined);
@@ -274,7 +274,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 undefined,
                 () => {
                     if (shiftKey) {
-                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.currentTime, undefined, undefined, true);
+                        CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.props.fieldKey, this.currentTime, undefined, undefined, true);
                     } else {
                         !wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
                     }
@@ -388,8 +388,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     // creates marker on timeline
     @undoBatch
     @action
-    static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, anchorStartTime: Opt<number>, anchorEndTime: Opt<number>, docAnchor: Opt<Doc>, addAsAnnotation: boolean) {
-        if (anchorStartTime === undefined) return rootDoc;
+    static createAnchor(doc: Doc, dataDoc: Doc, fieldKey: string, anchorStartTime: Opt<number>, anchorEndTime: Opt<number>, docAnchor: Opt<Doc>, addAsAnnotation: boolean) {
+        if (anchorStartTime === undefined) return doc;
         const startTag = '_timecodeToShow';
         const endTag = '_timecodeToHide';
         const anchor =
@@ -402,7 +402,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 backgroundColor: 'rgba(128, 128, 128, 0.5)',
                 layout_hideLinkButton: true,
                 onClick: FollowLinkScript(),
-                annotationOn: rootDoc,
+                annotationOn: doc,
                 _isTimelineLabel: true,
                 layout_borderRounding: anchorEndTime === undefined ? '100%' : undefined,
             });
@@ -539,7 +539,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 <div
                     className="collectionStackedTimeline-timelineContainer"
                     style={{ width: this.props.PanelWidth(), cursor: SnappingManager.GetIsDragging() ? 'grab' : '' }}
-                    onWheel={e => e.stopPropagation()}
+                    onWheel={e => this.isContentActive() && e.stopPropagation()}
                     onScroll={this.setScroll}
                     onMouseMove={e => this.isContentActive() && this.onHover(e)}
                     ref={wrapper => (this._timelineWrapper = wrapper)}>
@@ -775,7 +775,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
     // context menu
     contextMenuItems = () => {
         const resetTitle = {
-            script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!,
+            script: ScriptField.MakeFunction(`this.title = this["${this.props.endTag}"] ? "#" + formatToTime(this["${this.props.startTag}"]) + "-" + formatToTime(this["${this.props.endTag}"]) : "#" + formatToTime(this["${this.props.startTag}"])`)!,
             icon: 'folder-plus',
             label: 'Reset Title',
         };
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index dddb3ec71..165ccbdb1 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -142,7 +142,7 @@
         transform-origin: top left;
         grid-column-end: span 1;
         height: 100%;
-        margin: auto;
+        //margin: auto;
         display: inline-grid;
     }
 
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index da00093dd..5b86aaf86 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -309,12 +309,14 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             : undefined;
     isChildButtonContentActive = () => (this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined);
     @observable docRefs = new ObservableMap<Doc, DocumentView>();
+    childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this.props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null));
     // this is what renders the document that you see on the screen
     // called in Children: this actually adds a document to our children list
     getDisplayDoc(doc: Doc, width: () => number, count: number) {
         const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc;
         const height = () => this.getDocHeight(doc);
-
+        const panelHeight = () => (this.isStackingView ? height() : Math.min(height(), this.props.PanelHeight()));
+        const panelWidth = () => (this.isStackingView ? width() : this.columnWidth);
         const stackedDocTransform = () => this.getDocTransform(doc);
         this._docXfs.push({ stackedDocTransform, width, height });
         return count > this._renderCount ? null : (
@@ -323,21 +325,21 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 Document={doc}
                 DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
                 renderDepth={this.props.renderDepth + 1}
-                PanelWidth={width}
-                PanelHeight={height}
+                PanelWidth={panelWidth}
+                PanelHeight={panelHeight}
                 pointerEvents={this.props.DocumentView?.().props.onClick?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
                 styleProvider={this.styleProvider}
                 docViewPath={this.props.docViewPath}
-                layout_fitWidth={this.props.childLayoutFitWidth}
+                layout_fitWidth={this.childFitWidth}
                 isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
                 onKey={this.onKeyDown}
                 onBrowseClick={this.props.onBrowseClick}
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this.props.childLayoutTemplate}
                 LayoutTemplateString={this.props.childLayoutString}
-                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
-                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
-                dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined}
+                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (this.childFitWidth(doc) && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (this.childFitWidth(doc) && !Doc.NativeHeight(doc)) ? height : undefined}
+                dontCenter={this.props.childIgnoreNativeSize ? 'xy' : (StrCast(this.layoutDoc.layout_dontCenter) as any)}
                 dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to.
                 rootSelected={this.rootSelected}
                 layout_showTitle={this.props.childlayout_showTitle}
@@ -377,7 +379,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         if (!d) return 0;
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const maxWidth = this.columnWidth / this.numGroupColumns;
-        if (!this.layoutDoc._columnsFill && !(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d))) {
+        if (!this.layoutDoc._columnsFill && !this.childFitWidth(childLayoutDoc)) {
             return Math.min(NumCast(d._width), maxWidth);
         }
         return maxWidth;
@@ -387,15 +389,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
         const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
-        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? NumCast(d._width) : 0);
-        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? NumCast(d._height) : 0);
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._width) : 0);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._height) : 0);
         if (nw && nh) {
             const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
             return Math.min(maxHeight, (docWid * nh) / nw);
         }
         const childHeight = NumCast(childLayoutDoc._height);
-        const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+        const panelHeight = this.childFitWidth(childLayoutDoc) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
@@ -664,41 +666,35 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
 
     return35 = () => 35;
     @computed get buttonMenu() {
-        const menuDoc: Doc = Cast(this.rootDoc.layout_headerButton, Doc, null);
-        // TODO:glr Allow support for multiple buttons
-        if (menuDoc) {
-            const width: number = NumCast(menuDoc._width, 30);
-            const height: number = NumCast(menuDoc._height, 30);
-            return (
-                <div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
-                    <DocumentView
-                        Document={menuDoc}
-                        DataDoc={menuDoc}
-                        isContentActive={this.isContentActive}
-                        isDocumentActive={this.isContentActive}
-                        addDocument={this.props.addDocument}
-                        moveDocument={this.props.moveDocument}
-                        addDocTab={this.props.addDocTab}
-                        onBrowseClick={this.props.onBrowseClick}
-                        pinToPres={emptyFunction}
-                        rootSelected={this.props.isSelected}
-                        removeDocument={this.props.removeDocument}
-                        ScreenToLocalTransform={Transform.Identity}
-                        PanelWidth={this.return35}
-                        PanelHeight={this.return35}
-                        renderDepth={this.props.renderDepth}
-                        focus={emptyFunction}
-                        styleProvider={this.props.styleProvider}
-                        docViewPath={returnEmptyDoclist}
-                        whenChildContentsActiveChanged={emptyFunction}
-                        bringToFront={emptyFunction}
-                        childFilters={this.props.childFilters}
-                        childFiltersByRanges={this.props.childFiltersByRanges}
-                        searchFilterDocs={this.props.searchFilterDocs}
-                    />
-                </div>
-            );
-        }
+        const menuDoc = DocCast(this.layoutDoc.layout_headerButton);
+        return !menuDoc ? null : (
+            <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+                <DocumentView
+                    Document={menuDoc}
+                    isContentActive={this.isContentActive}
+                    isDocumentActive={this.isContentActive}
+                    addDocument={this.props.addDocument}
+                    moveDocument={this.props.moveDocument}
+                    addDocTab={this.props.addDocTab}
+                    onBrowseClick={this.props.onBrowseClick}
+                    pinToPres={emptyFunction}
+                    rootSelected={this.props.isSelected}
+                    removeDocument={this.props.removeDocument}
+                    ScreenToLocalTransform={Transform.Identity}
+                    PanelWidth={this.return35}
+                    PanelHeight={this.return35}
+                    renderDepth={this.props.renderDepth}
+                    focus={emptyFunction}
+                    styleProvider={this.props.styleProvider}
+                    docViewPath={returnEmptyDoclist}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    childFilters={this.props.childFilters}
+                    childFiltersByRanges={this.props.childFiltersByRanges}
+                    searchFilterDocs={this.props.searchFilterDocs}
+                />
+            </div>
+        );
     }
 
     @computed get nativeWidth() {
@@ -723,8 +719,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             SetValue: this.addGroup,
             contents: '+ ADD A GROUP',
         };
-        const buttonMenu = this.rootDoc.layout_headerButton;
-        const noviceExplainer = this.rootDoc.layout_explainer;
+        const buttonMenu = this.layoutDoc.layout_headerButton;
+        const noviceExplainer = this.layoutDoc.layout_explainer;
         return (
             <>
                 {buttonMenu || noviceExplainer ? (
@@ -739,7 +735,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         ref={this.createRef}
                         style={{
                             overflowY: this.isContentActive() ? 'auto' : 'hidden',
-                            background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
+                            background: this.props.styleProvider?.(this.Document, this.props, StyleProp.BackgroundColor),
                             pointerEvents: (this.props.pointerEvents?.() as any) ?? this.backgroundEvents,
                         }}
                         onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 9626f06ca..8896e2d61 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -17,15 +17,14 @@ import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'
 import { Networking } from '../../Network';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
-import { InteractionUtils } from '../../util/InteractionUtils';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { DocComponent } from '../DocComponent';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { LoadingBox } from '../nodes/LoadingBox';
 import { CollectionView, CollectionViewProps } from './CollectionView';
 import React = require('react');
-import { LoadingBox } from '../nodes/LoadingBox';
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     isAnyChildContentActive: () => boolean;
@@ -60,7 +59,8 @@ export function CollectionSubView<X>(moreProps?: X) {
             return this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
         }
 
-        rootSelected = () => this.props.isSelected() || (this.rootDoc && this.props.rootSelected());
+        // this returns whether either the collection is selected, or the template that it is part of is selected
+        rootSelected = () => this.props.isSelected() || this.props.rootSelected();
 
         // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
         // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through
@@ -100,7 +100,7 @@ export function CollectionSubView<X>(moreProps?: X) {
             } else if (Cast(this.dataField, listSpec(Doc), null)) {
                 // otherwise, if the collection data is a list, then use it.
                 rawdocs = Cast(this.dataField, listSpec(Doc), null);
-            } else {
+            } else if (this.dataField) {
                 // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
                 // For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection.
                 // Since the data field is actually an image, we set the list of documents to the singleton of root document's proto which will be an image.
@@ -217,18 +217,18 @@ export function CollectionSubView<X>(moreProps?: X) {
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
                         const canAdd =
-                            (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.rootDoc));
-                        const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
+                            (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.Document)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.Document));
+                        const moved = docDragData.moveDocument(movedDocs, this.Document, canAdd ? this.addDocument : returnFalse);
                         added = canAdd || moved ? moved : undefined;
                     } else if (addedDocs.length) {
                         added = this.addDocument(addedDocs);
                     }
-                    if (!added && ScriptCast(this.rootDoc.dropConverter)) {
-                        ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
+                    if (!added && ScriptCast(this.Document.dropConverter)) {
+                        ScriptCast(this.Document.dropConverter)?.script.run({ dragData: docDragData });
                         added = addedDocs.length ? this.addDocument(addedDocs) : true;
                     }
                 } else {
-                    ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
+                    ScriptCast(this.Document.dropConverter)?.script.run({ dragData: docDragData });
                     added = this.addDocument(docDragData.droppedDocuments);
                     !added && alert('You cannot perform this move');
                 }
@@ -238,7 +238,7 @@ export function CollectionSubView<X>(moreProps?: X) {
             } else if (de.complete.annoDragData) {
                 const dropCreator = de.complete.annoDragData.dropDocCreator;
                 de.complete.annoDragData.dropDocCreator = () => {
-                    const dropped = dropCreator(this.props.isAnnotationOverlay ? this.rootDoc : undefined);
+                    const dropped = dropCreator(this.props.isAnnotationOverlay ? this.Document : undefined);
                     this.addDocument(dropped);
                     return dropped;
                 };
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index a8f5345b7..1cdd200e5 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -47,9 +47,9 @@ export class CollectionTimeView extends CollectionSubView() {
     getAnchor = (addAsAnnotation: boolean) => {
         const anchor = Docs.Create.HTMLMarkerDocument([], {
             title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
-            annotationOn: this.rootDoc,
+            annotationOn: this.Document,
         });
-        PresBox.pinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.rootDoc);
+        PresBox.pinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.Document);
 
         if (addAsAnnotation) {
             // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 54332a7fd..b34598731 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -101,7 +101,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     componentDidMount() {
         //this.props.setContentView?.(this);
         this._disposers.autoheight = reaction(
-            () => this.rootDoc.layout_autoHeight,
+            () => this.layoutDoc.layout_autoHeight,
             auto => auto && this.computeHeight(),
             { fireImmediately: true }
         );
@@ -117,19 +117,19 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     };
     unobserveHeight = (ref: any) => {
         this.refList.delete(ref);
-        this.rootDoc.layout_autoHeight && this.computeHeight();
+        this.layoutDoc.layout_autoHeight && this.computeHeight();
     };
     observeHeight = (ref: any) => {
         if (ref) {
             this.refList.add(ref);
             this.observer = new _global.ResizeObserver(
                 action((entries: any) => {
-                    if (this.rootDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
+                    if (this.layoutDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
                         this.computeHeight();
                     }
                 })
             );
-            this.rootDoc.layout_autoHeight && this.computeHeight();
+            this.layoutDoc.layout_autoHeight && this.computeHeight();
             this.observer.observe(ref);
         }
     };
@@ -142,7 +142,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const dropAction = this.layoutDoc.dropAction as dropActionType;
         const dragData = de.complete.docDragData;
         if (dragData) {
-            const sameTree = Doc.AreProtosEqual(dragData.treeViewDoc, this.rootDoc) ? true : false;
+            const sameTree = Doc.AreProtosEqual(dragData.treeViewDoc, this.Document) ? true : false;
             const isAlreadyInTree = () => sameTree || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d));
             if (isAlreadyInTree() !== sameTree) {
                 console.log('WHAAAT');
@@ -160,7 +160,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const targetDataDoc = this.doc[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.rootDoc, 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];
@@ -316,12 +316,12 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     @computed get noviceExplainer() {
-        return !Doc.noviceMode || !this.rootDoc.layout_explainer ? null : <div className="documentExplanation"> {StrCast(this.rootDoc.layout_explainer)} </div>;
+        return !Doc.noviceMode || !this.layoutDoc.layout_explainer ? null : <div className="documentExplanation"> {StrCast(this.layoutDoc.layout_explainer)} </div>;
     }
 
     return35 = () => 35;
     @computed get buttonMenu() {
-        const menuDoc = Cast(this.rootDoc.layout_headerButton, Doc, null);
+        const menuDoc = Cast(this.layoutDoc.layout_headerButton, Doc, null);
         // To create a multibutton menu add a CollectionLinearView
         return !menuDoc ? null : (
             <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 426ad2c2f..4f3c5b9a2 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -34,6 +34,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
 import React = require('react');
+import { Docs } from '../../documents/Documents';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface TabDocViewProps {
@@ -259,7 +260,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 return;
             }
             const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps);
-            const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
+            const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({});
             pinDoc.presentation_targetDoc = anchorDoc ?? doc;
             pinDoc.title = doc.title + ' - Slide';
             pinDoc.data = new List<Doc>(); // the children of the embedding's layout are the presentation slide children.  the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
@@ -271,7 +272,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc.treeView = ''; // not really needed, but makes key value pane look better
             pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
             pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
-            pinDoc.treeView_ChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
             pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
             pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
             pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6e3f8e807..9de74b761 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -19,7 +19,6 @@ import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { LinkManager } from '../../util/LinkManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
@@ -144,7 +143,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return NumCast(this.props.treeViewParent.maxEmbedHeight, 200);
     }
     @computed get dataDoc() {
-        return this.props.document.treeView_ChildrenOnRoot ? this.doc : this.doc[DocData];
+        return this.doc[DocData];
     }
     @computed get layoutDoc() {
         return Doc.Layout(this.doc);
@@ -333,7 +332,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const before = pt[1] < rect.top + rect.height / 2;
         const inside = pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length);
         this._header.current!.className = 'treeView-header';
-        if (!this.props.treeView.outlineMode || DragManager.DocDragData?.treeViewDoc === this.props.treeView.rootDoc) {
+        if (!this.props.treeView.outlineMode || DragManager.DocDragData?.treeViewDoc === this.props.treeView.Document) {
             if (inside) this._header.current!.className += ' treeView-header-inside';
             else if (before) this._header.current!.className += ' treeView-header-above';
             else if (!before) this._header.current!.className += ' treeView-header-below';
@@ -361,7 +360,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             _width: 1000,
             _height: 10,
         });
-        Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
+        Doc.GetProto(bullet).title = ComputedField.MakeFunction('this.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
         DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
 
@@ -838,7 +837,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 ? [openEmbedding, makeFolder]
                 : this.doc._type_collection === CollectionViewType.Docking
                 ? []
-                : this.props.treeView.rootDoc === Doc.MyRecentlyClosed
+                : this.props.treeView.Document === Doc.MyRecentlyClosed
                 ? [reopenDoc]
                 : [openEmbedding, focusDoc]),
         ];
@@ -882,8 +881,8 @@ export class TreeView extends React.Component<TreeViewProps> {
                             maxWidth: props?.PanelWidth() || undefined,
                             background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
                             outline: SnappingManager.GetIsDragging() ? undefined: `solid ${highlightColor} ${highlightIndex}px`,
-                            paddingLeft: NumCast(treeView.rootDoc.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
-                            paddingRight: NumCast(treeView.rootDoc.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
+                            paddingLeft: NumCast(treeView.Document.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
+                            paddingRight: NumCast(treeView.Document.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
                             paddingTop: treeView.props.childYPadding,
                             paddingBottom: treeView.props.childYPadding,
                         }}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 403fba67b..d6a738084 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -37,6 +37,7 @@ export interface PoolData {
     zIndex?: number;
     width?: number;
     height?: number;
+    autoDim?: number; // 1 for set to Panel dims, 0 for use width/height as entered
     backgroundColor?: string;
     color?: string;
     opacity?: number;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index 856e195a3..38aeea152 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -8,7 +8,7 @@ import React = require('react');
 import { CollectionFreeFormView } from './CollectionFreeFormView';
 
 export interface CollectionFreeFormPannableContentsProps {
-    rootDoc: Doc;
+    Document: Doc;
     viewDefDivClick?: ScriptField;
     children?: React.ReactNode | undefined;
     transition?: string;
@@ -20,7 +20,7 @@ export interface CollectionFreeFormPannableContentsProps {
 @observer
 export class CollectionFreeFormPannableContents extends React.Component<CollectionFreeFormPannableContentsProps> {
     @computed get presPaths() {
-        return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.rootDoc) : null;
+        return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.Document) : null;
     }
     // rectangle highlight used when following trail/link to a region of a collection that isn't a document
     showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7f8bde9b4..6babe3c8f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -134,7 +134,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return (this.props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
     }
     @computed get contentBounds() {
-        const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
+        const cb = Cast(this.dataDoc.contentBounds, listSpec('number'));
         return cb
             ? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
             : aggregateBounds(
@@ -155,9 +155,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
     @computed get cachedCenteringShiftY(): number {
         const dv = this.props.DocumentView?.();
+        const fitWidth = this.props.layout_fitWidth?.(this.Document) ?? dv?.layoutDoc.layout_fitWidth;
         const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
         // if freeform has a native aspect, then the panel height needs to be adjusted to match it
-        const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.layout_fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
+        const aspect = dv?.nativeWidth && dv?.nativeHeight && !fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
         return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
     }
     @computed get panZoomXf() {
@@ -181,15 +182,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const timecode = Math.round(time);
         docs.forEach(doc => {
             CollectionFreeFormDocumentView.animFields.forEach(val => {
-                const findexed = Cast(doc[`${val.key}-indexed`], listSpec('number'), null);
+                const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null);
                 findexed?.length <= timecode + 1 && findexed.push(undefined as any as number);
             });
             CollectionFreeFormDocumentView.animStringFields.forEach(val => {
-                const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null);
+                const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null);
                 findexed?.length <= timecode + 1 && findexed.push(undefined as any as string);
             });
             CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => {
-                const findexed = Cast(doc[`${val}-indexed`], listSpec(InkField), null);
+                const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null);
                 findexed?.length <= timecode + 1 && findexed.push(undefined as any);
             });
         });
@@ -262,7 +263,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             for (const newBox of newBoxes) {
                 if (newBox.activeFrame !== undefined) {
                     const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field.key]);
-                    CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field.key}-indexed`]);
+                    CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field.key}_indexed`]);
                     CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field.key]);
                     delete newBox.activeFrame;
                     CollectionFreeFormDocumentView.animFields.forEach((field, i) => field.key !== 'opacity' && (newBox[field.key] = vals[i]));
@@ -283,15 +284,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     groupFocus = (anchor: Doc, options: DocFocusOptions) => {
-        options.docTransform = new Transform(-NumCast(this.rootDoc[this.panXFieldKey]) + NumCast(anchor.x), -NumCast(this.rootDoc[this.panYFieldKey]) + NumCast(anchor.y), 1);
-        const res = this.props.focus(this.rootDoc, options);
+        options.docTransform = new Transform(-NumCast(this.layoutDoc[this.panXFieldKey]) + NumCast(anchor.x), -NumCast(this.layoutDoc[this.panYFieldKey]) + NumCast(anchor.y), 1);
+        const res = this.props.focus(this.Document, options);
         options.docTransform = undefined;
         return res;
     };
 
     focus = (anchor: Doc, options: DocFocusOptions) => {
         if (this._lightboxDoc) return;
-        if (anchor === this.rootDoc) {
+        if (anchor === this.Document) {
             if (options.willZoomCentered && options.zoomScale) {
                 this.fitContentOnce();
                 options.didMove = true;
@@ -300,7 +301,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor)) return;
         const xfToCollection = options?.docTransform ?? Transform.Identity();
         const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined };
-        const cantTransform = this.fitContentsToBox || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
+        const cantTransform = this.fitContentsToBox || ((this.Document.isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
         const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale ?? 0.75 : undefined);
 
         // focus on the document in the collection
@@ -407,7 +408,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             let added = false;
             //  do nothing if link is dropped into any freeform view parent of dragged document
             const source =
-                !linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.rootDoc
+                !linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.Document
                     ? Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' })
                     : Docs.Create.FontIconDocument({
                           title: 'anchor',
@@ -582,27 +583,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     clusterStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
         let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props'  used to be renderDepth + 1
-        switch (property) {
-            case StyleProp.BackgroundColor:
-                const cluster = NumCast(doc?.layout_cluster);
-                if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
-                    if (this._clusterSets.length <= cluster) {
-                        setTimeout(() => doc && this.updateCluster(doc));
-                    } else {
-                        // choose a cluster color from a palette
-                        const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
-                        styleProp = colors[cluster % colors.length];
-                        const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
-                        // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
-                        set?.map(s => (styleProp = StrCast(s.backgroundColor)));
+        if (doc && this.childDocList?.includes(doc))
+            switch (property) {
+                case StyleProp.BackgroundColor:
+                    const cluster = NumCast(doc?.layout_cluster);
+                    if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
+                        if (this._clusterSets.length <= cluster) {
+                            setTimeout(() => doc && this.updateCluster(doc));
+                        } else {
+                            // choose a cluster color from a palette
+                            const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
+                            styleProp = colors[cluster % colors.length];
+                            const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
+                            // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
+                            set?.map(s => (styleProp = StrCast(s.backgroundColor)));
+                        }
                     }
-                }
-                break;
-            case StyleProp.FillColor:
-                if (doc && this.Document._currentFrame !== undefined) {
-                    return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
-                }
-        }
+                    break;
+                case StyleProp.FillColor:
+                    if (doc && this.Document._currentFrame !== undefined) {
+                        return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
+                    }
+            }
         return styleProp;
     };
 
@@ -623,7 +625,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this._downTime = Date.now();
         const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
         if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this.props.isContentActive(true)) {
-            if (!this.props.Document._isGroup) {
+            if (!this.Document.isGroup) {
                 // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
                 // prettier-ignore
                 switch (Doc.ActiveTool) {
@@ -697,7 +699,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     @action
     onEraserUp = (e: PointerEvent): void => {
-        this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
+        this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.Document));
         this._deleteList = [];
         this._batch?.end();
     };
@@ -752,8 +754,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
             if (!this._deleteList.includes(intersect.inkView)) {
                 this._deleteList.push(intersect.inkView);
-                SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.stroke_width?.toString()) || '1');
-                SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
+                SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
+                SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
                 // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
                 if (!e.shiftKey) {
                     this._eraserLock++;
@@ -785,7 +787,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             return true;
         }
         // pan the view if this is a regular collection, or it's an overlay and the overlay is zoomed (otherwise, there's nothing to pan)
-        if (!this.props.isAnnotationOverlay || 1 - NumCast(this.rootDoc._freeform_scale_min, 1) / this.zoomScaling()) {
+        if (!this.props.isAnnotationOverlay || 1 - NumCast(this.layoutDoc._freeform_scale_min, 1) / this.zoomScaling()) {
             this.pan(e);
             e.stopPropagation(); // if we are actually panning, stop propagation -- this will preven things like the overlayView from dragging the document while we're panning
         }
@@ -927,7 +929,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     zoom = (pointX: number, pointY: number, deltaY: number): void => {
-        if (this.Document._isGroup || this.Document[(this.props.viewField ?? '_') + 'freeform_noZoom']) return;
+        if (this.Document.isGroup || this.Document[(this.props.viewField ?? '_') + 'freeform_noZoom']) return;
         let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
         if (deltaScale < 0) deltaScale = -deltaScale;
         const [x, y] = this.screenToLocalXf.transformPoint(pointX, pointY);
@@ -935,15 +937,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (deltaScale * invTransform.Scale > 20) {
             deltaScale = 20 / invTransform.Scale;
         }
-        if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc[this.scaleFieldKey + '_min'])) {
+        if (deltaScale < 1 && invTransform.Scale <= NumCast(this.Document[this.scaleFieldKey + '_min'])) {
             this.setPan(0, 0);
             return;
         }
-        if (deltaScale * invTransform.Scale > NumCast(this.rootDoc[this.scaleFieldKey + '_max'], Number.MAX_VALUE)) {
-            deltaScale = NumCast(this.rootDoc[this.scaleFieldKey + '_max'], 1) / invTransform.Scale;
+        if (deltaScale * invTransform.Scale > NumCast(this.Document[this.scaleFieldKey + '_max'], Number.MAX_VALUE)) {
+            deltaScale = NumCast(this.Document[this.scaleFieldKey + '_max'], 1) / invTransform.Scale;
         }
-        if (deltaScale * invTransform.Scale < NumCast(this.rootDoc[this.scaleFieldKey + '_min'], this.isAnnotationOverlay ? 1 : 0)) {
-            deltaScale = NumCast(this.rootDoc[this.scaleFieldKey + '_min'], 1) / invTransform.Scale;
+        if (deltaScale * invTransform.Scale < NumCast(this.Document[this.scaleFieldKey + '_min'], this.isAnnotationOverlay ? 1 : 0)) {
+            deltaScale = NumCast(this.Document[this.scaleFieldKey + '_min'], 1) / invTransform.Scale;
         }
 
         const localTransform = invTransform.scaleAbout(deltaScale, x, y);
@@ -956,11 +958,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
+        if (this.Document.isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
         PresBox.Instance?.pauseAutoPres();
         if (this.layoutDoc._Transform || this.props.Document.treeView_OutlineMode === TreeViewType.outline) return;
         e.stopPropagation();
-        const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
+        const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
         const scrollable = this.isAnnotationOverlay && NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling + 1e-4;
         switch (
             !e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
@@ -1024,28 +1026,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
         if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc) {
             this.setPanZoomTransition(panTime);
-            const minScale = NumCast(this.rootDoc._freeform_scale_min, 1);
+            const minScale = NumCast(this.dataDoc._freeform_scale_min, 1);
             const scale = 1 - minScale / this.zoomScaling();
-            const minPanX = NumCast(this.rootDoc._freeform_panX_min, 0);
-            const minPanY = NumCast(this.rootDoc._freeform_panY_min, 0);
-            const maxPanX = NumCast(this.rootDoc._freeform_panX_max, this.nativeWidth);
+            const minPanX = NumCast(this.dataDoc._freeform_panX_min, 0);
+            const minPanY = NumCast(this.dataDoc._freeform_panY_min, 0);
+            const maxPanX = NumCast(this.dataDoc._freeform_panX_max, this.nativeWidth);
             const newPanX = Math.min(minPanX + scale * maxPanX, Math.max(minPanX, panX));
             const fitYscroll = (((this.nativeHeight / this.nativeWidth) * this.props.PanelWidth() - this.props.PanelHeight()) * this.props.ScreenToLocalTransform().Scale) / minScale;
             const nativeHeight = (this.props.PanelHeight() / this.props.PanelWidth() / (this.nativeHeight / this.nativeWidth)) * this.nativeHeight;
             const maxScrollTop = this.nativeHeight / this.props.ScreenToLocalTransform().Scale - this.props.PanelHeight();
             const maxPanY =
                 minPanY + // minPanY + scrolling introduced by view scaling + scrolling introduced by layout_fitWidth
-                scale * NumCast(this.rootDoc._panY_max, nativeHeight) +
+                scale * NumCast(this.dataDoc._panY_max, nativeHeight) +
                 (!this.props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
             let newPanY = Math.max(minPanY, Math.min(maxPanY, panY));
-            if (false && NumCast(this.rootDoc.layout_scrollTop) && NumCast(this.rootDoc._freeform_scale, minScale) !== minScale) {
-                const relTop = NumCast(this.rootDoc.layout_scrollTop) / maxScrollTop;
-                this.rootDoc.layout_scrollTop = undefined;
+            if (false && NumCast(this.layoutDoc.layout_scrollTop) && NumCast(this.layoutDoc._freeform_scale, minScale) !== minScale) {
+                const relTop = NumCast(this.layoutDoc.layout_scrollTop) / maxScrollTop;
+                this.layoutDoc.layout_scrollTop = undefined;
                 newPanY = minPanY + relTop * (maxPanY - minPanY);
-            } else if (fitYscroll > 2 && this.rootDoc.layout_scrollTop === undefined && NumCast(this.rootDoc._freeform_scale, minScale) === minScale) {
+            } else if (fitYscroll > 2 && this.layoutDoc.layout_scrollTop === undefined && NumCast(this.layoutDoc._freeform_scale, minScale) === minScale) {
                 const maxPanY = minPanY + fitYscroll;
                 const relTop = (panY - minPanY) / (maxPanY - minPanY);
-                setTimeout(() => (this.rootDoc.layout_scrollTop = relTop * maxScrollTop), 10);
+                setTimeout(() => (this.layoutDoc.layout_scrollTop = relTop * maxScrollTop), 10);
                 newPanY = minPanY;
             }
             !this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX);
@@ -1055,8 +1057,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     nudge = (x: number, y: number, nudgeTime: number = 500) => {
-        const collectionDoc = this.props.docViewPath().lastElement().rootDoc;
-        if (collectionDoc?._type_collection !== CollectionViewType.Freeform || collectionDoc._freeform_ !== undefined) {
+        const collectionDoc = this.props.docViewPath().lastElement().Document;
+        if (collectionDoc?._type_collection !== CollectionViewType.Freeform) {
             this.setPan(
                 NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
                 NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(),
@@ -1104,7 +1106,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
-        if (this.Document._isGroup) return;
+        if (this.Document.isGroup) return;
         this.setPanZoomTransition(transitionTime);
         const screenXY = this.screenToLocalXf.inverse().transformPoint(docpt[0], docpt[1]);
         this.layoutDoc[this.scaleFieldKey] = scale;
@@ -1162,17 +1164,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
+        if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== 'Tab';
             const layout_fieldKey = StrCast(docView.LayoutFieldKey);
-            const newDoc = Doc.MakeCopy(docView.rootDoc, true);
-            const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
+            const newDoc = Doc.MakeCopy(docView.Document, true);
+            const dataField = docView.Document[Doc.LayoutFieldKey(newDoc)];
             newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10;
-            else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10;
-            if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
-                newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
+            if (below) newDoc.y = NumCast(docView.Document.y) + NumCast(docView.Document._height) + 10;
+            else newDoc.x = NumCast(docView.Document.x) + NumCast(docView.Document._width) + 10;
+            if (layout_fieldKey !== 'layout' && docView.Document[layout_fieldKey] instanceof Doc) {
+                newDoc[layout_fieldKey] = docView.Document[layout_fieldKey];
             }
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -1228,7 +1230,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 searchFilterDocs={this.searchFilterDocs}
                 isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
                 isContentActive={this.childContentsActive}
-                focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
+                focus={this.Document.isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
                 addDocTab={this.addDocTab}
                 addDocument={this.props.addDocument}
                 removeDocument={this.props.removeDocument}
@@ -1237,7 +1239,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                 docViewPath={this.props.docViewPath}
                 styleProvider={this.clusterStyleProvider}
-                dragAction={(this.rootDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
+                dragAction={(this.Document.childDragAction ?? this.props.childDragAction) as dropActionType}
                 bringToFront={this.bringToFront}
                 layout_showTitle={this.props.childlayout_showTitle}
                 dontRegisterView={this.props.dontRegisterView}
@@ -1288,9 +1290,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
         const { z, zIndex } = childDoc;
         const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber);
-        const { x, y, _width, _height, opacity, _rotation } =
+        const { x, y, autoDim, _width, _height, opacity, _rotation } =
             layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value)
-                ? { _width: -1, _height: -1, _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() }
+                ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() }
                 : CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
         // prettier-ignore
         const rotation = Cast(_rotation,'number',
@@ -1300,6 +1302,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
             y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
             z: Cast(z, 'number'),
+            autoDim,
             rotation,
             color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
             backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.clusterStyleProvider(childDoc, this.props, StyleProp.BackgroundColor),
@@ -1413,8 +1416,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         // create an anchor that saves information about the current state of the freeform view (pan, zoom, view type)
-        const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presentation_transition: 500, annotationOn: this.rootDoc });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document._isGroup, type_collection: true, filters: true } }, this.rootDoc);
+        const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presentation_transition: 500, annotationOn: this.Document });
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document.isGroup, type_collection: true, filters: true } }, this.Document);
 
         if (addAsAnnotation) {
             if (Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
@@ -1434,7 +1437,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 this._firstRender = false;
                 this._disposers.groupBounds = reaction(
                     () => {
-                        if (this.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
+                        if (this.Document.isGroup && this.childDocs.length === this.childDocList?.length) {
                             const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: NumCast(cd._width), height: NumCast(cd._height) }));
                             return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
                         }
@@ -1481,7 +1484,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
                 this._disposers.active = reaction(
                     () => this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions
-                    active => !SnappingManager.GetIsDragging() && this.rootDoc[this.autoResetFieldKey] && active && this.resetView()
+                    active => !SnappingManager.GetIsDragging() && this.dataDoc[this.autoResetFieldKey] && active && this.resetView()
                 );
             })
         );
@@ -1575,7 +1578,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     componentWillUnmount() {
-        this.rootDoc[this.autoResetFieldKey] && this.resetView();
+        this.dataDoc[this.autoResetFieldKey] && this.resetView();
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
@@ -1616,9 +1619,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     ///
     @undoBatch
     resetView = () => {
-        this.rootDoc[this.panXFieldKey] = NumCast(this.rootDoc[this.panXFieldKey + '_reset']);
-        this.props.Document[this.panYFieldKey] = NumCast(this.rootDoc[this.panYFieldKey + '_reset']);
-        this.rootDoc[this.scaleFieldKey] = NumCast(this.rootDoc[this.scaleFieldKey + '_reset'], 1);
+        this.layoutDoc[this.panXFieldKey] = NumCast(this.dataDoc[this.panXFieldKey + '_reset']);
+        this.layoutDoc[this.panYFieldKey] = NumCast(this.dataDoc[this.panYFieldKey + '_reset']);
+        this.layoutDoc[this.scaleFieldKey] = NumCast(this.dataDoc[this.scaleFieldKey + '_reset'], 1);
     };
     ///
     /// resetView restores a freeform collection to unit scale and centered at (0,0)  UNLESS
@@ -1626,11 +1629,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     ///
     @undoBatch
     toggleResetView = () => {
-        this.rootDoc[this.autoResetFieldKey] = !this.rootDoc[this.autoResetFieldKey];
-        if (this.rootDoc[this.autoResetFieldKey]) {
-            this.rootDoc[this.panXFieldKey + '_reset'] = this.rootDoc[this.panXFieldKey];
-            this.rootDoc[this.panYFieldKey + '_reset'] = this.rootDoc[this.panYFieldKey];
-            this.rootDoc[this.scaleFieldKey + '_reset'] = this.rootDoc[this.scaleFieldKey];
+        this.dataDoc[this.autoResetFieldKey] = !this.dataDoc[this.autoResetFieldKey];
+        if (this.dataDoc[this.autoResetFieldKey]) {
+            this.dataDoc[this.panXFieldKey + '_reset'] = this.dataDoc[this.panXFieldKey];
+            this.dataDoc[this.panYFieldKey + '_reset'] = this.dataDoc[this.panYFieldKey];
+            this.dataDoc[this.scaleFieldKey + '_reset'] = this.dataDoc[this.scaleFieldKey];
         }
     };
 
@@ -1639,8 +1642,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const appearance = ContextMenu.Instance.findByDescription('Appearance...');
         const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
-        !this.props.Document._isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
-        !this.props.Document._isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
+        !this.props.Document.isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
+        !this.props.Document.isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
         !Doc.noviceMode &&
             appearanceItems.push({
                 description: 'Toggle auto arrange',
@@ -1652,11 +1655,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             return;
         }
         !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' });
-        appearanceItems.push({ description: `Pin View`, event: () => this.props.pinToPres(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
+        appearanceItems.push({ description: `Pin View`, event: () => this.props.pinToPres(this.Document, { pinViewport: MarqueeView.CurViewBounds(this.dataDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
         !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' });
         this.props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
 
-        this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
+        this.props.Document.isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
 
         !Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null;
         !Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
@@ -1681,7 +1684,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @undoBatch
     @action
     transcribeStrokes = () => {
-        if (this.props.Document._isGroup && this.props.Document.transcription) {
+        if (this.props.Document.isGroup && this.props.Document.transcription) {
             const text = StrCast(this.props.Document.transcription);
             const lines = text.split('\n');
             const height = 30 + 15 * lines.length;
@@ -1697,9 +1700,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     @action
     dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean = true, visited = new Set<Doc>()) => {
-        if (visited.has(this.rootDoc)) return;
-        visited.add(this.rootDoc);
-        showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document._isGroup));
+        if (visited.has(this.Document)) return;
+        visited.add(this.Document);
+        showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document.isGroup));
         const activeDocs = this.getActiveDocuments();
         const size = this.screenToLocalXf.transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
         const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
@@ -1708,14 +1711,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
         activeDocs
-            .filter(doc => doc._isGroup && SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))
+            .filter(doc => doc.isGroup && SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))
             .forEach(doc => DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
 
         const horizLines: number[] = [];
         const vertLines: number[] = [];
         const invXf = this.screenToLocalXf.inverse();
         snappableDocs
-            .filter(doc => !doc._isGroup && (snapToDraggedDoc || (SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))))
+            .filter(doc => !doc.isGroup && (snapToDraggedDoc || (SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))))
             .forEach(doc => {
                 const { left, top, width, height } = docDims(doc);
                 const topLeftInScreen = invXf.transformPoint(left, top);
@@ -1783,7 +1786,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.incrementalRender();
         return (
             <CollectionFreeFormPannableContents
-                rootDoc={this.rootDoc}
+                Document={this.Document}
                 brushedView={this.brushedView}
                 isAnnotationOverlay={this.isAnnotationOverlay}
                 transform={this.PanZoomCenterXf}
@@ -1801,7 +1804,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             <MarqueeView
                 {...this.props}
                 ref={this._marqueeViewRef}
-                ungroup={this.rootDoc._isGroup ? this.promoteCollection : undefined}
+                ungroup={this.Document.isGroup ? this.promoteCollection : undefined}
                 nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
                 addDocTab={this.addDocTab}
                 slowLoadDocuments={this.slowLoadDocuments}
@@ -1828,7 +1831,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const nh = this.nativeHeight;
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
-        return wscale < hscale || this.layoutDoc.layout_fitWidth ? wscale : hscale;
+        return wscale < hscale || (this.props.layout_fitWidth?.(this.Document) ?? this.layoutDoc.layout_fitWidth) ? wscale : hscale;
     }
     nativeDim = () => this.nativeDimScaling;
 
@@ -1849,7 +1852,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     lightboxPanelHeight = () => Math.max(0, this.props.PanelHeight() - 30);
     lightboxScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-15, -15);
     onPassiveWheel = (e: WheelEvent) => {
-        const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
+        const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
         const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling;
         this.props.isSelected() && !scrollable && e.preventDefault();
     };
@@ -1930,14 +1933,14 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
     dv &&
         DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
             if (!focused) {
-                const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
+                const selfFfview = !dv.Document.isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
                 let containers = dv.props.docViewPath();
                 let parFfview = dv.CollectionFreeFormView;
                 for (var cont of containers) {
                     parFfview = parFfview ?? cont.CollectionFreeFormView;
                 }
-                while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.DocumentView?.().CollectionFreeFormView;
-                const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it.  Otherwise, zoom out on the parent ffview
+                while (parFfview?.Document.isGroup) parFfview = parFfview.props.DocumentView?.().CollectionFreeFormView;
+                const ffview = selfFfview && selfFfview.layoutDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it.  Otherwise, zoom out on the parent ffview
                 ffview?.zoomSmoothlyAboutPt(ffview.screenToLocalXf.transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
                 Doc.linkFollowHighlight(dv?.props.Document, false);
             }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 2092ecb7a..18b9b4423 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -348,6 +348,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             ? creator(selected, { title: 'nested stack' })
             : ((doc: Doc) => {
                   Doc.GetProto(doc).data = new List<Doc>(selected);
+                  Doc.GetProto(doc).isGroup = makeGroup;
                   Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
                   doc._freeform_panX = doc._freeform_panY = 0;
                   return doc;
@@ -355,7 +356,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.isSystem = undefined;
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
-        newCollection._isGroup = makeGroup;
         newCollection._dragWhenActive = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 274012000..be806d3cc 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -174,7 +174,7 @@ export class CollectionGridView extends CollectionSubView() {
     }
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined);
+    isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
     /**
      *
      * @param layout
@@ -187,6 +187,7 @@ export class CollectionGridView extends CollectionSubView() {
         return (
             <DocumentView
                 {...this.props}
+                shouldNotScale={returnFalse}
                 NativeWidth={returnZero}
                 NativeHeight={returnZero}
                 setContentView={emptyFunction}
@@ -199,7 +200,7 @@ export class CollectionGridView extends CollectionSubView() {
                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                 onClick={this.onChildClickHandler}
                 renderDepth={this.props.renderDepth + 1}
-                dontCenter={this.props.Document.centerY ? undefined : 'y'}
+                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
             />
         );
     }
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 4267a9059..a06ed3a2d 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -48,7 +48,7 @@ export class CollectionLinearView extends CollectionSubView() {
 
     componentDidMount() {
         this._widthDisposer = reaction(
-            () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (NumCast(doc._width) || this.dimension()) + tot + 4, 0) : 0),
+            () => 5 + NumCast(this.dataDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (NumCast(doc._width) || this.dimension()) + tot + 4, 0) : 0),
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
@@ -58,7 +58,7 @@ export class CollectionLinearView extends CollectionSubView() {
         if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
     };
 
-    dimension = () => NumCast(this.rootDoc._height);
+    dimension = () => NumCast(this.layoutDoc._height);
     getTransform = (ele: Opt<HTMLDivElement>) => {
         if (!ele) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
@@ -183,7 +183,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     PanelWidth={doc[Width]}
                     PanelHeight={nested || doc._height ? doc[Height] : this.dimension}
                     renderDepth={this.props.renderDepth + 1}
-                    dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
+                    dontRegisterView={BoolCast(this.Document.childDontRegisterViews)}
                     focus={emptyFunction}
                     styleProvider={this.props.styleProvider}
                     docViewPath={returnEmptyDoclist}
@@ -215,7 +215,7 @@ export class CollectionLinearView extends CollectionSubView() {
                 toggleStatus={BoolCast(this.layoutDoc.linearView_IsOpen)}
                 onClick={() => {
                     this.layoutDoc.linearView_IsOpen = !isExpanded;
-                    ScriptCast(this.rootDoc.onClick)?.script.run({ this: this.rootDoc }, console.log);
+                    ScriptCast(this.Document.onClick)?.script.run({ this: this.Document }, console.log);
                 }}
                 tooltip={isExpanded ? 'Close' : 'Open'}
                 fillWidth={true}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
index cb0d5e03f..4e2968933 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
@@ -17,7 +17,7 @@
         }
 
         .contentFittingDocumentView {
-            width: unset;
+            margin: auto;
         }
 
         .label-wrapper {
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index c2586fb4b..05ec0448b 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -219,8 +219,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                             if (this.childDocs.includes(d)) {
                                 if (dropInd > this.childDocs.indexOf(d)) dropInd--;
                             }
-                            Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
-                            Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
+                            Doc.RemoveDocFromList(this.dataDoc, this.props.fieldKey, d);
+                            Doc.AddDocToList(this.dataDoc, this.props.fieldKey, d, DocListCast(this.dataDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
                         }
                     })
                 );
@@ -235,18 +235,25 @@ export class CollectionMulticolumnView extends CollectionSubView() {
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
     isChildContentActive = () => {
-        const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive;
+        const childDocsActive = this.props.childDocumentsActive?.() ?? this.Document.childDocumentsActive;
         return this.props.isContentActive?.() === false || childDocsActive === false
             ? false //
             : this.props.isDocumentActive?.() && childDocsActive
             ? true
             : undefined;
     };
-    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number, shouldNotScale: () => boolean) => {
+    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 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);
         return (
             <DocumentView
-                Document={layout}
-                DataDoc={layout.resolvedDataDoc as Doc}
+                Document={childLayout}
+                DataDoc={childLayout.resolvedDataDoc as Doc}
                 styleProvider={this.props.styleProvider}
                 docViewPath={this.props.docViewPath}
                 LayoutTemplate={this.props.childLayoutTemplate}
@@ -263,7 +270,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 ScreenToLocalTransform={dxf}
                 isContentActive={this.isChildContentActive}
                 isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
-                hideResizeHandles={this.props.childHideResizeHandles?.()}
+                hideResizeHandles={childLayout.layout_fitWidth || this.props.childHideResizeHandles?.() ? true : false}
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 fitContentsToBox={this.props.fitContentsToBox}
                 focus={this.props.focus}
@@ -278,6 +285,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 addDocTab={this.props.addDocTab}
                 pinToPres={this.props.pinToPres}
                 bringToFront={returnFalse}
+                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
             />
         );
     };
@@ -288,26 +296,15 @@ export class CollectionMulticolumnView extends CollectionSubView() {
     @computed
     private get contents(): JSX.Element[] | null {
         const { childLayoutPairs } = this;
-        const { Document, PanelHeight } = this.props;
         const collector: JSX.Element[] = [];
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
-            const aspect = Doc.NativeAspect(layout, undefined, true);
-            const width = () => this.lookupPixels(layout);
-            const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
-            const docwidth = () => (layout._layout_reflowHorizontal ? width() : Math.min(height() * aspect, width()));
-            const docheight = () => Math.min(docwidth() / aspect, height());
-            const dxf = () =>
-                this.lookupIndividualTransform(layout)
-                    .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin))
-                    .scale(this.props.NativeDimScaling?.() || 1);
-            const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
             collector.push(
                 <Tooltip title={'Tab: ' + StrCast(layout.title)}>
-                    <div className="document-wrapper" key={'wrapper' + i} style={{ width: width() }}>
-                        {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)}
+                    <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} />
-                        <WidthLabel layout={layout} collectionDoc={Document} />
+                        <WidthLabel layout={layout} collectionDoc={this.Document} />
                     </div>
                 </Tooltip>,
                 <ResizeBar
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
index ec7200a03..5b0e6c981 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -10,11 +10,6 @@
         flex-direction: row;
         height: 100%;
         align-items: center;
-        margin: auto;
-
-        .contentFittingDocumentView {
-            height: unset;
-        }
 
         .label-wrapper {
             display: flex;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 249c3551b..b4b62e635 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -215,8 +215,8 @@ export class CollectionMultirowView extends CollectionSubView() {
                             if (this.childDocs.includes(d)) {
                                 if (dropInd > this.childDocs.indexOf(d)) dropInd--;
                             }
-                            Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
-                            Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
+                            Doc.RemoveDocFromList(this.dataDoc, this.props.fieldKey, d);
+                            Doc.AddDocToList(this.dataDoc, this.props.fieldKey, d, DocListCast(this.dataDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
                         }
                     })
                 );
@@ -231,14 +231,21 @@ export class CollectionMultirowView extends CollectionSubView() {
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
     isChildContentActive = () => {
-        const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive;
+        const childDocsActive = this.props.childDocumentsActive?.() ?? this.Document.childDocumentsActive;
         return this.props.isContentActive?.() === false || childDocsActive === false
             ? false //
             : this.props.isDocumentActive?.() && childDocsActive
             ? true
             : undefined;
     };
-    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number, shouldNotScale: () => boolean) => {
+    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 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);
         return (
             <DocumentView
                 Document={layout}
@@ -252,13 +259,13 @@ export class CollectionMultirowView extends CollectionSubView() {
                 PanelHeight={height}
                 shouldNotScale={shouldNotScale}
                 rootSelected={this.rootSelected}
-                dropAction={StrCast(this.rootDoc.childDragAction) as dropActionType}
+                dropAction={StrCast(this.Document.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={dxf}
                 isContentActive={this.isChildContentActive}
                 isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
-                hideResizeHandles={this.props.childHideResizeHandles?.()}
+                hideResizeHandles={layout.layout_fitWidth || this.props.childHideResizeHandles?.() ? true : false}
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 fitContentsToBox={this.props.fitContentsToBox}
                 dragAction={this.props.childDragAction}
@@ -274,6 +281,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                 addDocTab={this.props.addDocTab}
                 pinToPres={this.props.pinToPres}
                 bringToFront={returnFalse}
+                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
             />
         );
     };
@@ -284,24 +292,13 @@ export class CollectionMultirowView extends CollectionSubView() {
     @computed
     private get contents(): JSX.Element[] | null {
         const { childLayoutPairs } = this;
-        const { Document, PanelWidth } = this.props;
         const collector: JSX.Element[] = [];
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
-            const aspect = Doc.NativeAspect(layout, undefined, true);
-            const height = () => this.lookupPixels(layout);
-            const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
-            const docheight = () => Math.min(width() / aspect, height());
-            const docwidth = () => (layout._layout_reflowHorizontal ? width() : Math.min(width(), docheight() * aspect));
-            const dxf = () =>
-                this.lookupIndividualTransform(layout)
-                    .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin) - (height() - docheight()) / 2)
-                    .scale(this.props.NativeDimScaling?.() || 1);
-            const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
             collector.push(
-                <div className="document-wrapper" style={{ height: height() }} key={'wrapper' + i}>
-                    {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)}
-                    <HeightLabel layout={layout} collectionDoc={Document} />
+                <div className="document-wrapper" style={{ flexDirection: 'row', height: this.lookupPixels(layout) }} key={'wrapper' + i}>
+                    {this.getDisplayDoc(layout)}
+                    <HeightLabel layout={layout} collectionDoc={this.Document} />
                 </div>,
                 <ResizeBar
                     height={resizerHeight}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 190e4ff2a..22b80e21d 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -85,12 +85,12 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get _selectedDocs() {
-        const selected = SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
+        const selected = SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document));
         if (!selected.length) {
             for (const sel of SelectionManager.Docs()) {
                 const contextPath = DocumentManager.GetContextPath(sel, true);
-                if (contextPath.includes(this.rootDoc)) {
-                    const parentInd = contextPath.indexOf(this.rootDoc);
+                if (contextPath.includes(this.Document)) {
+                    const parentInd = contextPath.indexOf(this.Document);
                     return parentInd < contextPath.length - 1 ? [contextPath[parentInd + 1]] : [];
                 }
             }
@@ -146,7 +146,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
         Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
         this._keysDisposer = observe(
-            this.rootDoc[this.fieldKey ?? 'data'] as List<Doc>,
+            this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>,
             change => {
                 switch (change.type as any) {
                     case 'splice':
@@ -154,7 +154,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                         (change as any).added.forEach((doc: Doc) => // for each document added 
                             Doc.GetAllPrototypes(doc.value as Doc).forEach(proto => // for all of its prototypes (and itself)
                                 Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them
-                                    !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo(''))))));
+                                    !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo(key, key === 'author'))))));
                         break;
                     case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
                 }
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 9d5b533d1..2c251d3cf 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -102,7 +102,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     }
 
     @computed get defaultCellContent() {
-        const { color, textDecoration, fieldProps } = SchemaTableCell.renderProps(this.props);
+        const { color, textDecoration, fieldProps, pointerEvents } = SchemaTableCell.renderProps(this.props);
 
         return (
             <div
@@ -111,6 +111,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                     color,
                     textDecoration,
                     width: '100%',
+                    pointerEvents,
                 }}>
                 <EditableView
                     oneLine={this.props.oneLine}
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 3af523d73..fda3f731d 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -189,13 +189,13 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
     const editorView = textView?.EditorView;
     // prettier-ignore
     const alignments:attrfuncs[] = (['left','right','center','vcent'] as ("left"|"center"|"right"|"vcent")[]).map((where) =>
-        [ where, {      checkResult: () =>(editorView ? (where === 'vcent' ?  SelectionManager.Docs().some(doc => doc.layout_centered):
+        [ where, {      checkResult: () =>(editorView ? (where === 'vcent' ?  RichTextMenu.Instance.textVcenter:
                                                                               (RichTextMenu.Instance.textAlign === where)): 
-                                                         where === 'vcent' ? BoolCast(Doc.UserDoc().layout_centered):
+                                                         where === 'vcent' ? BoolCast(Doc.UserDoc()._layout_centered):
                                                                               (Doc.UserDoc().textAlign ===where) ? true:false),
-                        toggle: () => (editorView?.state ? (where === 'vcent' ? SelectionManager.Docs().forEach(doc => doc.layout_centered = !doc.layout_centered):
+                        toggle: () => (editorView?.state ? (where === 'vcent' ? RichTextMenu.Instance.vcenterToggle(editorView, editorView.dispatch):
                                                                               RichTextMenu.Instance.align(editorView, editorView.dispatch, where)):
-                                                            where === 'vcent' ?  Doc.UserDoc().layout_centered = !Doc.UserDoc().layout_centered:
+                                                            where === 'vcent' ?  Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered:
                                                                               (Doc.UserDoc().textAlign = where))}]); // prettier-ignore
     // prettier-ignore
     const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 8d80f1364..807a77233 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -366,8 +366,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
                 const textField = Doc.LayoutFieldKey(newDoc);
                 Doc.GetProto(newDoc)[`${textField}_recordingSource`] = this.dataDoc;
-                Doc.GetProto(newDoc)[`${textField}_recordingStart`] = ComputedField.MakeFunction(`self.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
-                Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction(`self.${textField}_recordingSource.mediaState`);
+                Doc.GetProto(newDoc)[`${textField}_recordingStart`] = ComputedField.MakeFunction(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
+                Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction(`this.${textField}_recordingSource.mediaState`);
                 if (Doc.IsInMyOverlay(this.rootDoc)) {
                     newDoc.overlayX = this.rootDoc.x;
                     newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index dd16ab71b..5b4fb3e29 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,6 +1,6 @@
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
@@ -15,14 +15,16 @@ import { StyleProp } from '../StyleProvider';
 import './CollectionFreeFormDocumentView.scss';
 import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
 import React = require('react');
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 
 export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentViewProps {
     x: number;
     y: number;
     z: number;
-    width: number; // -1 means use PanelWidth which should be the same as the Document's width, but avoids the delay of waiting for the width prop to change in PanelWidth function
-    height: number; // -1 means use PanelHeight
+    width: number;
+    height: number;
     zIndex?: number;
+    autoDim?: number; // 1 means use Panel Width/Height, 0 means use width/height
     rotation?: number;
     color?: string;
     backgroundColor?: string;
@@ -72,8 +74,8 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti
     w_Transition = ()      => this.Transition; // prettier-ignore
     w_DataTransition = ()  => this.DataTransition; // prettier-ignore
 
-    PanelWidth = ()        => this.Width  > 0 ? this.Width : this.props.PanelWidth?.(); // prettier-ignore
-    PanelHeight = ()       => this.Height > 0 ? this.Height : this.props.PanelHeight?.(); // prettier-ignore
+    PanelWidth = ()        => this.props.autoDim ? this.props.PanelWidth?.() : this.Width; // prettier-ignore
+    PanelHeight = ()       => this.props.autoDim ? this.props.PanelHeight?.() : this.Height; // prettier-ignore
     @action
     componentDidUpdate() {
         this.WrapperKeys.forEach(keys => ((this as any)[keys.upper] = (this.props as any)[keys.lower]));
@@ -113,7 +115,7 @@ export interface CollectionFreeFormDocumentViewProps {
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & DocumentViewProps>() {
     get displayName() { // this makes mobx trace() statements more descriptive
-        return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')';
+        return 'CollectionFreeFormDocumentView(' + this.Document.title + ')';
     } // prettier-ignore
     public static animFields: { key: string; val?: number }[] = [
         { key: 'x' },
@@ -148,14 +150,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
 
     public static getValues(doc: Doc, time: number, fillIn: boolean = true) {
         return CollectionFreeFormDocumentView.animFields.reduce((p, val) => {
-            p[val.key] = Cast(doc[`${val.key}-indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number);
+            p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number);
             return p;
         }, {} as { [val: string]: Opt<number> });
     }
 
     public static getStringValues(doc: Doc, time: number) {
         return CollectionFreeFormDocumentView.animStringFields.reduce((p, val) => {
-            p[val] = Cast(doc[`${val}-indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string);
+            p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string);
             return p;
         }, {} as { [val: string]: Opt<string> });
     }
@@ -163,40 +165,52 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     public static setStringValues(time: number, d: Doc, vals: { [val: string]: Opt<string> }) {
         const timecode = Math.round(time);
         Object.keys(vals).forEach(val => {
-            const findexed = Cast(d[`${val}-indexed`], listSpec('string'), []).slice();
+            const findexed = Cast(d[`${val}_indexed`], listSpec('string'), []).slice();
             findexed[timecode] = vals[val] as any as string;
-            d[`${val}-indexed`] = new List<string>(findexed);
+            d[`${val}_indexed`] = new List<string>(findexed);
         });
     }
 
     public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) {
         const timecode = Math.round(time);
         Object.keys(vals).forEach(val => {
-            const findexed = Cast(d[`${val}-indexed`], listSpec('number'), []).slice();
+            const findexed = Cast(d[`${val}_indexed`], listSpec('number'), []).slice();
             findexed[timecode] = vals[val] as any as number;
-            d[`${val}-indexed`] = new List<number>(findexed);
+            d[`${val}_indexed`] = new List<number>(findexed);
         });
     }
+    public static gotoKeyFrame(doc: Doc, newFrame: number) {
+        if (doc) {
+            const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]);
+            const currentFrame = Cast(doc._currentFrame, 'number', null);
+            if (currentFrame === undefined) {
+                doc._currentFrame = 0;
+                CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
+            }
+            CollectionFreeFormView.updateKeyframe(undefined, [...childDocs, doc], currentFrame || 0);
+            doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
+        }
+    }
 
     public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) {
         docs.forEach(doc => {
             if (doc.appearFrame === undefined) doc.appearFrame = currTimecode;
-            if (!doc['opacity-indexed']) {
+            if (!doc['opacity_indexed']) {
                 // opacity is unlike other fields because it's value should not be undefined before it appears to enable it to fade-in
-                doc['opacity-indexed'] = new List<number>(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1)));
+                doc['opacity_indexed'] = new List<number>(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1)));
             }
             CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val.key] = ComputedField.MakeInterpolatedNumber(val.key, 'activeFrame', doc, currTimecode, val.val)));
             CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode)));
             CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (doc[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', doc, currTimecode)));
             const targetDoc = doc; // data fields, like rtf 'text' exist on the data doc, so
             //doc !== targetDoc && (targetDoc.embedContainer = doc.embedContainer); // the computed fields don't see the layout doc -- need to copy the embedContainer to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
-            targetDoc.activeFrame = ComputedField.MakeFunction('self.embedContainer?._currentFrame||0');
+            targetDoc.activeFrame = ComputedField.MakeFunction('this.embedContainer?._currentFrame||0');
             targetDoc.dataTransition = 'inherit';
         });
     }
 
     @action public float = () => {
-        const topDoc = this.rootDoc;
+        const topDoc = this.Document;
         const containerDocView = this.props.docViewPath().lastElement();
         const screenXf = containerDocView?.screenToLocalTransform();
         if (screenXf) {
@@ -238,7 +252,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     //  undefined - this is not activated by a group
     isGroupActive = () => {
         if (this.CollectionFreeFormView.isAnyChildContentActive()) return undefined;
-        const isGroup = this.rootDoc._isGroup && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent');
+        const isGroup = this.Document.isGroup && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
         return isGroup ? (this.props.isDocumentActive?.() ? 'group' : this.props.isGroupActive?.() ? 'child' : 'inactive') : this.props.isGroupActive?.() ? 'child' : undefined;
     };
     public static CollectionFreeFormDocViewClassName = 'collectionFreeFormDocumentView-container';
@@ -265,3 +279,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         );
     }
 }
+ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
+    CollectionFreeFormDocumentView.gotoKeyFrame(doc, newFrame);
+});
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 8c8c987fe..eed787b6d 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,4 +1,4 @@
-import { computed } from 'mobx';
+import { computed, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../../fields/Doc';
 import { AclPrivate } from '../../../fields/DocSymbols';
@@ -66,7 +66,6 @@ export const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 
 interface HTMLtagProps {
     Document: Doc;
-    RootDoc: Doc;
     htmltag: string;
     onClick?: ScriptField;
     onInput?: ScriptField;
@@ -90,18 +89,18 @@ interface HTMLtagProps {
 export class HTMLtag extends React.Component<HTMLtagProps> {
     click = (e: React.MouseEvent) => {
         const clickScript = (this.props as any).onClick as Opt<ScriptField>;
-        clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, scale: this.props.scaling });
+        clickScript?.script.run({ this: this.props.Document, self: this.props.Document, scale: this.props.scaling });
     };
     onInput = (e: React.FormEvent<HTMLDivElement>) => {
         const onInputScript = (this.props as any).onInput as Opt<ScriptField>;
-        onInputScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, value: (e.target as any).textContent });
+        onInputScript?.script.run({ this: this.props.Document, self: this.props.Document, value: (e.target as any).textContent });
     };
     render() {
         const style: { [key: string]: any } = {};
-        const divKeys = OmitKeys(this.props, ['children', 'dragStarting', 'dragEnding', 'htmltag', 'RootDoc', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit;
+        const divKeys = OmitKeys(this.props, ['children', 'dragStarting', 'dragEnding', 'htmltag', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit;
         const replacer = (match: any, expr: string, offset: any, string: any) => {
-            // bcz: this executes a script to convert a propery expression string:  { script }  into a value
-            return (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string) || '';
+            // bcz: this executes a script to convert a property expression string:  { script }  into a value
+            return (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.props.Document, this: this.props.Document, scale: this.props.scaling }).result as string) || '';
         };
         Object.keys(divKeys).map((prop: string) => {
             const p = (this.props as any)[prop] as string;
@@ -119,9 +118,6 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
 @observer
 export class DocumentContentsView extends React.Component<
     DocumentViewProps & {
-        isSelected: () => boolean;
-        select: (ctrl: boolean) => void;
-        NativeDimScaling?: () => number;
         setHeight?: (height: number) => void;
         layout_fieldKey: string;
     }
@@ -137,6 +133,10 @@ export class DocumentContentsView extends React.Component<
         return '<p>Loading layout</p>';
     }
 
+    componentDidUpdate(prevProps: Readonly<DocumentViewProps & { setHeight?: ((height: number) => void) | undefined; layout_fieldKey: string }>, prevState: Readonly<{}>, snapshot?: any): void {
+        Object.keys(prevProps).forEach(pkey => (prevProps as any)[pkey] !== (this.props as any)[pkey] && console.log(pkey + ' ' + (prevProps as any)[pkey] + ' ' + (this.props as any)[pkey]));
+    }
+
     get dataDoc() {
         const proto = this.props.DataDoc || Doc.GetProto(this.props.Document);
         return proto instanceof Promise ? undefined : proto;
@@ -170,7 +170,6 @@ export class DocumentContentsView extends React.Component<
         ];
         const list = {
             ...OmitKeys(this.props, [...docOnlyProps], '').omit,
-            RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc,
             Document: this.layoutDoc,
             DataDoc: this.dataDoc,
             onClick: onClick,
@@ -195,7 +194,7 @@ export class DocumentContentsView extends React.Component<
 
         // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'>
         const replacer2 = (match: any, p1: string, offset: any, string: any) => {
-            return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.NativeDimScaling?.() || 1}' htmltag='${p1}'`;
+            return `<HTMLtag Document={props.Document} scaling='${this.props.NativeDimScaling?.() || 1}' htmltag='${p1}'`;
         };
         layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ef2736b6d..f89768d3d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -283,7 +283,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return Doc.LayoutFieldKey(this.layoutDoc);
     }
     @computed get layout_showTitle() {
-        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
     }
     @computed get NativeDimScaling() {
         return this.props.NativeDimScaling?.() || 1;
@@ -302,13 +302,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     @computed get widgetDecorations() {
         TraceMobx();
-        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations);
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Decorations);
     }
     @computed get backgroundBoxColor() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box');
     }
     @computed get docContents() {
-        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.DocContents);
+        return this.props.styleProvider?.(this.Document, this.props, StyleProp.DocContents);
     }
     @computed get headerMargin() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
@@ -369,7 +369,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 // undefined - it is not active, but it should be responsive to actions that might activate it or its contents (eg clicking)
                 return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none'
                     ? false
-                    : Doc.ActiveTool !== InkTool.None || SnappingManager.GetCanEmbed() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()
+                    : Doc.ActiveTool !== InkTool.None || SnappingManager.GetCanEmbed() || this.rootSelected() || this.Document.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()
                     ? true
                     : undefined;
             },
@@ -405,8 +405,8 @@ 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 selected = views.length > 1 && views.some(dv => dv.rootDoc === this.Document) ? views : [this.props.DocumentView()];
-            const dragData = new DragManager.DocumentDragData(selected.map(dv => dv.rootDoc));
+            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);
             dragData.offset = this.props
                 .ScreenToLocalTransform()
@@ -417,7 +417,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             dragData.removeDocument = this.props.removeDocument;
             dragData.moveDocument = this.props.moveDocument;
             dragData.draggedViews = [this.props.DocumentView()];
-            dragData.canEmbed = this.rootDoc.dragAction ?? this.props.dragAction ? true : false;
+            dragData.canEmbed = this.Document.dragAction ?? this.props.dragAction ? true : false;
             DragManager.StartDocumentDrag(
                 selected.map(dv => dv.docView!._mainCont.current!),
                 dragData,
@@ -430,8 +430,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     defaultRestoreTargetView = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => {
         const targetMatch =
-            Doc.AreProtosEqual(anchor, this.rootDoc) || // anchor is this document, so anchor's properties apply to this document
-            (DocCast(anchor)?.layout_unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) // the anchor is an layout_unrendered annotation on this document, so anchor properties apply to this document
+            Doc.AreProtosEqual(anchor, this.Document) || // anchor is this document, so anchor's properties apply to this document
+            (DocCast(anchor)?.layout_unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.Document)) // the anchor is an layout_unrendered annotation on this document, so anchor properties apply to this document
                 ? true
                 : false;
         return targetMatch && PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined;
@@ -450,23 +450,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
             let stopPropagate = true;
             let preventDefault = true;
-            !this.rootDoc._keepZWhenDragged && this.props.bringToFront(this.rootDoc);
+            !this.layoutDoc._keepZWhenDragged && this.props.bringToFront(this.Document);
             if (this._doubleTap) {
                 const defaultDblclick = this.props.defaultDoubleClick?.() || this.Document.defaultDoubleClick;
                 if (this.onDoubleClickHandler?.script) {
                     const { clientX, clientY, shiftKey, altKey, ctrlKey } = e; // or we could call e.persist() to capture variables
                     // prettier-ignore
                     const func = () => this.onDoubleClickHandler.script.run( {
-                            this: this.layoutDoc,
-                            self: this.rootDoc,
+                            this: this.Document,
                             scriptContext: this.props.scriptContext,
                             documentView: this.props.DocumentView(),
                             clientX, clientY,  altKey,  shiftKey,  ctrlKey,
                             value: undefined,
                         }, console.log  );
                     UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
-                } else if (!Doc.IsSystem(this.rootDoc) && (defaultDblclick === undefined || defaultDblclick === 'default')) {
-                    UndoManager.RunInBatch(() => LightboxView.Instance.AddDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
+                } else if (!Doc.IsSystem(this.Document) && (defaultDblclick === undefined || defaultDblclick === 'default')) {
+                    UndoManager.RunInBatch(() => LightboxView.Instance.AddDocTab(this.Document, OpenWhere.lightbox), 'double tap');
                     SelectionManager.DeselectAll();
                     Doc.UnBrushDoc(this.props.Document);
                 } else {
@@ -488,8 +487,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         DocumentViewInternal.addDocTabFunc = this.props.addDocTab;
                         this.onClickHandler?.script.run(
                             {
-                                this: this.layoutDoc,
-                                self: this.rootDoc,
+                                this: this.Document,
                                 _readOnly_: false,
                                 scriptContext: this.props.scriptContext,
                                 documentView: this.props.DocumentView(),
@@ -505,7 +503,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             : '';
                         DocumentViewInternal.addDocTabFunc = oldFunc;
                     };
-                    clickFunc = () => UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title);
+                    clickFunc = () => UndoManager.RunInBatch(func, 'click ' + this.Document.title);
                 } else {
                     // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
@@ -516,7 +514,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 const sendToBack = e.altKey;
                 this._singleClickFunc =
                     // prettier-ignore
-                    clickFunc ?? (() => (sendToBack ? this.props.DocumentView().props.bringToFront(this.rootDoc, true) : 
+                    clickFunc ?? (() => (sendToBack ? this.props.DocumentView().props.bringToFront(this.Document, true) : 
                                                       this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? 
                                         this.props.select(e.ctrlKey||e.shiftKey, e.metaKey)));
                 const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
@@ -538,7 +536,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (this.props.isGroupActive?.() === 'child' && !this.props.isDocumentActive?.()) return;
         this._longPressSelector = setTimeout(() => {
             if (DocumentView.LongPress) {
-                if (this.rootDoc.undoIgnoreFields) {
+                if (this.Document.undoIgnoreFields) {
                     runInAction(() => (UndoStack.HideInline = !UndoStack.HideInline));
                 } else {
                     this.props.select(false);
@@ -563,10 +561,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             ) {
                 e.stopPropagation();
                 // don't preventDefault anymore.  Goldenlayout, PDF text selection and RTF text selection all need it to go though
-                //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._type_collection !== CollectionViewType.Docking) e.preventDefault();
+                //if (this.props.isSelected(true) && this.Document.type !== DocumentType.PDF && this.layoutDoc._type_collection !== CollectionViewType.Docking) e.preventDefault();
 
                 // listen to move events if document content isn't active or document is draggable
-                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || BoolCast(this.rootDoc._dragWhenActive, this.props.dragWhenActive))) {
+                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || BoolCast(this.layoutDoc._dragWhenActive, this.props.dragWhenActive))) {
                     document.addEventListener('pointermove', this.onPointerMove);
                 }
             }
@@ -596,9 +594,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         this._longPressSelector && clearTimeout(this._longPressSelector);
 
         if (this.onPointerUpHandler?.script) {
-            this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
+            this.onPointerUpHandler.script.run({ this: this.Document }, console.log);
         } else if (e.button === 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
-            this._doubleTap = (this.onDoubleClickHandler?.script || this.rootDoc.defaultDoubleClick !== 'ignore') && Date.now() - this._lastTap < Utils.CLICK_TIME;
+            this._doubleTap = (this.onDoubleClickHandler?.script || this.Document.defaultDoubleClick !== 'ignore') && Date.now() - this._lastTap < Utils.CLICK_TIME;
             if (!this.isContentActive()) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
         }
         if (DocumentView.LongPress) e.preventDefault();
@@ -607,7 +605,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch
     @action
     toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => {
-        const hadOnClick = this.rootDoc.onClick;
+        const hadOnClick = this.Document.onClick;
         this.noOnClick();
         this.Document.onClick = hadOnClick ? undefined : FollowLinkScript();
         this.Document.waitForDoubleClickToClick = hadOnClick ? undefined : 'never';
@@ -653,11 +651,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
         if (linkdrag) {
             linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor();
-            if (linkdrag.linkSourceDoc && linkdrag.linkSourceDoc !== this.rootDoc) {
+            if (linkdrag.linkSourceDoc && linkdrag.linkSourceDoc !== this.Document) {
                 if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
                     de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
                 }
-                if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.embedContainer) {
+                if (de.complete.annoDragData || this.Document !== linkdrag.linkSourceDoc.embedContainer) {
                     const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
                     de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                     if (de.complete.linkDocument) {
@@ -713,7 +711,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
-        if (e && this.rootDoc._layout_hideContextMenu && Doc.noviceMode) {
+        if (e && this.layoutDoc._layout_hideContextMenu && Doc.noviceMode) {
             e.preventDefault();
             e.stopPropagation();
             //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
@@ -738,7 +736,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         if (e && !(e.nativeEvent as any).dash) {
             const onDisplay = () => {
-                if (this.rootDoc.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected() && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
+                if (this.Document.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected() && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
                 setTimeout(() => simulateMouseClick(document.elementFromPoint(e.clientX, e.clientY), e.clientX, e.clientY, e.screenX, e.screenY));
             };
             if (navigator.userAgent.includes('Macintosh')) {
@@ -751,11 +749,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
         StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
-            cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: 'sticky-note' })
+            cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.Document, scriptContext: this.props.scriptContext }), icon: 'sticky-note' })
         );
-        this.props
-            .contextMenuItems?.()
-            .forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp }));
+        this.props.contextMenuItems?.().forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.Document, scriptContext: this.props.scriptContext }), icon: item.icon as IconProp }));
 
         if (!this.props.Document.isFolder) {
             const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layout_fieldKey)], Doc, null);
@@ -765,22 +761,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             if (this.props.renderDepth === 0) {
                 appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => LightboxView.Instance.SetLightboxDoc(this.rootDoc), icon: 'external-link-alt' });
             }
-            this.rootDoc.type === DocumentType.PRES && appearanceItems.push({ description: 'Pin', event: () => this.props.pinToPres(this.rootDoc, {}), icon: 'eye' });
+            this.Document.type === DocumentType.PRES && appearanceItems.push({ description: 'Pin', event: () => this.props.pinToPres(this.rootDoc, {}), icon: 'eye' });
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
             !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'compass' });
 
-            if (!Doc.IsSystem(this.rootDoc) && this.rootDoc.type !== DocumentType.PRES && ![CollectionViewType.Docking, CollectionViewType.Tree].includes(this.rootDoc._type_collection as any)) {
+            if (!Doc.IsSystem(this.Document) && this.Document.type !== DocumentType.PRES && ![CollectionViewType.Docking, CollectionViewType.Tree].includes(this.Document._type_collection as any)) {
                 const existingOnClick = cm.findByDescription('OnClick...');
                 const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
 
                 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.rootDoc, false)), icon: 'arrow-up' });
-                    zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, 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.rootDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
-                        event: undoBatch(action(() => (this.rootDoc._keepZWhenDragged = !this.rootDoc._keepZWhenDragged))),
+                        description: !this.layoutDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
+                        event: undoBatch(action(() => (this.layoutDoc._keepZWhenDragged = !this.layoutDoc._keepZWhenDragged))),
                         icon: 'hand-point-up',
                     });
                     !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'layer-group' });
@@ -816,7 +812,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
             const more = cm.findByDescription('More...');
             const moreItems = more && 'subitems' in more ? more.subitems : [];
-            if (!Doc.IsSystem(this.rootDoc)) {
+            if (!Doc.IsSystem(this.Document)) {
                 if (!Doc.noviceMode) {
                     moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: 'concierge-bell' });
                     moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => (this.Document._chromeHidden = !this.Document._chromeHidden), icon: 'project-diagram' });
@@ -833,9 +829,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
         }
         const constantItems: ContextMenuProps[] = [];
-        if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._type_collection !== CollectionViewType.Docking) {
+        if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking) {
             constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
-            (this.rootDoc._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
+            (this.Document._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
             if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) {
                 // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
                 constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
@@ -847,8 +843,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const help = cm.findByDescription('Help...');
         const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
         !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' });
-        !Doc.noviceMode && helpItems.push({ description: 'Print Document in Console', event: () => console.log(this.props.Document), icon: 'hand-point-right' });
-        !Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.props.Document[DocData]), icon: 'hand-point-right' });
+        !Doc.noviceMode && helpItems.push({ description: 'Print Document in Console', event: () => console.log(this.Document), icon: 'hand-point-right' });
+        !Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.dataDoc), icon: 'hand-point-right' });
 
         let documentationDescription: string | undefined = undefined;
         let documentationLink: string | undefined = undefined;
@@ -902,7 +898,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     };
 
     @computed get _rootSelected() {
-        return this.props.isSelected() || (this.props.Document.rootDocument && this.props.rootSelected?.()) || false;
+        return this.props.isSelected() || BoolCast(this.Document.rootDocument && this.props.rootSelected?.());
     }
     rootSelected = () => this._rootSelected;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
@@ -931,7 +927,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get contents() {
         TraceMobx();
         const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString;
-        const noBackground = this.rootDoc._isGroup && !this.props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent');
+        const noBackground = this.Document.isGroup && !this.props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
         return (
             <div
                 className="documentView-contentsView"
@@ -946,7 +942,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     docViewPath={this.props.viewPath}
                     setContentView={this.setContentView}
                     childFilters={this.childFilters}
-                    NativeDimScaling={this.props.NativeDimScaling}
                     PanelHeight={this.panelHeight}
                     setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
                     isContentActive={this.isContentActive}
@@ -985,17 +980,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     // For each of these, we create LinkAnchorBox's on the border of the DocumentView.
     @computed get directLinks() {
         TraceMobx();
-        return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(
+        return LinkManager.Instance.getAllRelatedLinks(this.Document).filter(
             link =>
-                (link.link_matchEmbeddings ? link.link_anchor_1 === this.rootDoc : Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.rootDoc)) ||
-                (link.link_matchEmbeddings ? link.link_anchor_2 === this.rootDoc : Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.rootDoc)) ||
-                ((link.link_anchor_1 as Doc)?.layout_unrendered && Doc.AreProtosEqual((link.link_anchor_1 as Doc)?.annotationOn as Doc, this.rootDoc)) ||
-                ((link.link_anchor_2 as Doc)?.layout_unrendered && Doc.AreProtosEqual((link.link_anchor_2 as Doc)?.annotationOn as Doc, this.rootDoc))
+                (link.link_matchEmbeddings ? link.link_anchor_1 === this.Document : Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.Document)) ||
+                (link.link_matchEmbeddings ? link.link_anchor_2 === this.Document : Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.Document)) ||
+                ((link.link_anchor_1 as Doc)?.layout_unrendered && Doc.AreProtosEqual((link.link_anchor_1 as Doc)?.annotationOn as Doc, this.Document)) ||
+                ((link.link_anchor_2 as Doc)?.layout_unrendered && Doc.AreProtosEqual((link.link_anchor_2 as Doc)?.annotationOn as Doc, this.Document))
         );
     }
     @computed get allLinks() {
         TraceMobx();
-        return LinkManager.Instance.getAllRelatedLinks(this.rootDoc);
+        return LinkManager.Instance.getAllRelatedLinks(this.Document);
     }
     hideLink = computedFn((link: Doc) => () => (link.link_displayLine = false));
     @computed get allLinkEndpoints() {
@@ -1020,7 +1015,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     removeDocument={this.hideLink(link)}
                     styleProvider={this.anchorStyleProvider}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LinkAnchorBox.LayoutString(`link_anchor_${LinkManager.anchorIndex(link, this.rootDoc)}`)}
+                    LayoutTemplateString={LinkAnchorBox.LayoutString(`link_anchor_${LinkManager.anchorIndex(link, this.Document)}`)}
                 />
             </div>
         ));
@@ -1143,7 +1138,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             <div
                 className="documentView-captionWrapper"
                 style={{
-                    pointerEvents: this.rootDoc.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+                    pointerEvents: this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                     background: StrCast(this.layoutDoc._backgroundColor, 'rgba(0,0,0,0.2)'),
                     color: lightOrDark(StrCast(this.layoutDoc._backgroundColor, 'black')),
                 }}>
@@ -1191,8 +1186,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                           dropdownWidth,
                           StrCast(this.layoutDoc.layout_showTitle).split(':')[0],
                           action((field: string | number) => {
-                              if (this.rootDoc.layout_showTitle) {
-                                  this.rootDoc._layout_showTitle = field;
+                              if (this.layoutDoc.layout_showTitle) {
+                                  this.layoutDoc._layout_showTitle = field;
                               } else if (!this.props.layout_showTitle) {
                                   Doc.UserDoc().layout_showTitle = field;
                               }
@@ -1216,11 +1211,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         display="block"
                         oneLine={true}
                         fontSize={(this.titleHeight / 15) * 10}
-                        GetValue={() => (showTitle.split(';').length !== 1 ? '#' + showTitle : Field.toKeyValueString(this.rootDoc, showTitle.split(';')[0]))}
+                        GetValue={() => (showTitle.split(';').length !== 1 ? '#' + showTitle : Field.toKeyValueString(this.Document, showTitle.split(';')[0]))}
                         SetValue={undoBatch((input: string) => {
                             if (input?.startsWith('#')) {
-                                if (this.rootDoc.layout_showTitle) {
-                                    this.rootDoc._layout_showTitle = input?.substring(1);
+                                if (this.layoutDoc.layout_showTitle) {
+                                    this.layoutDoc._layout_showTitle = input?.substring(1);
                                 } else if (!this.props.layout_showTitle) {
                                     Doc.UserDoc().layout_showTitle = input?.substring(1) ?? 'author_date';
                                 }
@@ -1246,7 +1241,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     renderDoc = (style: object) => {
         TraceMobx();
-        return !DocCast(this.Document) || GetEffectiveAcl(this.Document[DocData]) === AclPrivate
+        return !DocCast(this.Document) || GetEffectiveAcl(this.dataDoc) === AclPrivate
             ? null
             : this.docContents ?? (
                   <div
@@ -1299,10 +1294,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
     }
     @computed get highlighting() {
-        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting);
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Highlighting);
     }
     @computed get borderPath() {
-        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BorderPath);
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderPath);
     }
     render() {
         TraceMobx();
@@ -1313,7 +1308,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 ? this.boxShadow
                 : highlighting && this.borderRounding && highlighting.highlightStyle !== 'dashed'
                 ? `0 0 0 ${highlighting.highlightIndex}px ${highlighting.highlightColor}`
-                : this.boxShadow || (this.rootDoc.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
+                : this.boxShadow || (this.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
         const renderDoc = this.renderDoc({
             borderRadius: this.borderRounding,
             outline: highlighting && !this.borderRounding && !highlighting.highlightStroke ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px',
@@ -1337,7 +1332,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents, // visible painted means that the underlying doc contents are irregular and will process their own pointer events (otherwise, the contents are expected to fill the entire doc view box so we can handle pointer events here)
                 }}>
                 <>
-                    {this._componentView instanceof KeyValueBox ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)}
+                    {this._componentView instanceof KeyValueBox ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation], this.Document)}
                     {borderPath?.jsx}
                 </>
             </div>
@@ -1377,7 +1372,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     private _disposers: { [name: string]: IReactionDisposer } = {};
     public clearViewTransition = () => {
         this.ViewTimer && clearTimeout(this.ViewTimer);
-        this.rootDoc._viewTransition = undefined;
+        this.layoutDoc._viewTransition = undefined;
     };
     public startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource);
 
@@ -1385,15 +1380,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     public setAnimEffect = (presEffect: Doc, timeInMs: number, afterTrans?: () => void) => {
         this.AnimEffectTimer && clearTimeout(this.AnimEffectTimer);
-        this.rootDoc[Animation] = presEffect;
-        this.AnimEffectTimer = setTimeout(() => (this.rootDoc[Animation] = undefined), timeInMs);
+        this.Document[Animation] = presEffect;
+        this.AnimEffectTimer = setTimeout(() => (this.Document[Animation] = undefined), timeInMs);
     };
     public setViewTransition = (transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) => {
-        this.rootDoc._viewTransition = `${transProp} ${timeInMs}ms`;
+        this.layoutDoc._viewTransition = `${transProp} ${timeInMs}ms`;
         if (dataTrans) this.rootDoc._dataTransition = `${transProp} ${timeInMs}ms`;
         this.ViewTimer && clearTimeout(this.ViewTimer);
         return (this.ViewTimer = setTimeout(() => {
-            this.rootDoc._viewTransition = undefined;
+            this.layoutDoc._viewTransition = undefined;
             this.rootDoc._dataTransition = 'inherit';
             afterTrans?.();
         }, timeInMs + 10));
@@ -1421,7 +1416,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         DocServer.GetRefField(docId).then(docx =>
             LightboxView.Instance.SetLightboxDoc(
                 (docx as Doc) ?? // reuse existing pivot view of documents, or else create a new collection
-                Docs.Create.StackingDocument([], { title: linkAnchor.title + '-pivot', _width: 500, _height: 500, target: linkAnchor, updateContentsScript: ScriptField.MakeScript('updateLinkCollection(self, self.target)') }, docId)
+                Docs.Create.StackingDocument([], { title: linkAnchor.title + '-pivot', _width: 500, _height: 500, target: linkAnchor, updateContentsScript: ScriptField.MakeScript('updateLinkCollection(this, this.target)') }, docId)
             )
         );
     }
@@ -1445,16 +1440,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.docView?._componentView;
     }
     get allLinks() {
-        return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.rootDoc || link.link_anchor_2 === this.rootDoc);
+        return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.rootDoc);
     }
     get LayoutFieldKey() {
         return this.docView?.LayoutFieldKey || 'layout';
     }
     @computed get layout_fitWidth() {
-        return this.docView?._componentView?.layout_fitWidth?.() ?? this.props.layout_fitWidth?.(this.rootDoc) ?? this.layoutDoc?.layout_fitWidth;
+        return this.docView?._componentView?.layout_fitWidth?.() ?? this.props.layout_fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth;
     }
     @computed get anchorViewDoc() {
-        return this.props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.rootDoc['link_anchor_2']) : this.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.rootDoc['link_anchor_1']) : undefined;
+        return this.props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.Document['link_anchor_2']) : this.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.Document['link_anchor_1']) : undefined;
     }
     @computed get hideLinkButton() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkBtn + (this.isSelected() ? ':selected' : ''));
@@ -1526,7 +1521,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.props.CollectionFreeFormDocumentView?.();
     }
 
-    public toggleNativeDimensions = () => this.docView && this.rootDoc.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
+    public toggleNativeDimensions = () => this.docView && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
     public getBounds = () => {
         if (!this.docView?.ContentDiv || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
             return undefined;
@@ -1560,7 +1555,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
             this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finalFinished);
             this.Document.deiconifyLayout = undefined;
-            this.props.bringToFront(this.rootDoc);
+            this.props.bringToFront(this.Document);
         }
     }
     @undoBatch
@@ -1574,8 +1569,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc
         setTimeout(
             action(() => {
-                if (useExistingLayout && custom && this.rootDoc['layout_' + view]) {
-                    this.rootDoc.layout_fieldKey = 'layout_' + view;
+                if (useExistingLayout && custom && this.Document['layout_' + view]) {
+                    this.Document.layout_fieldKey = 'layout_' + view;
                 } else {
                     this.setCustomView(custom, view);
                 }
@@ -1592,6 +1587,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         );
     };
 
+    layout_fitWidthFunc = (doc: Doc) => BoolCast(this.layout_fitWidth);
     scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale;
     docViewPathFunc = () => this.docViewPath;
     isSelected = () => this.SELECTED;
@@ -1600,7 +1596,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         else {
             SelectionManager.SelectView(this, extendSelection);
             if (focusSelection) {
-                DocumentManager.Instance.showDocument(this.rootDoc, {
+                DocumentManager.Instance.showDocument(this.Document, {
                     willZoomCentered: true,
                     zoomScale: 0.9,
                     zoomTime: 500,
@@ -1608,6 +1604,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             }
         }
     };
+    ShouldNotScale = () => this.shouldNotScale;
     NativeWidth = () => this.effectiveNativeWidth;
     NativeHeight = () => this.effectiveNativeHeight;
     PanelWidth = () => this.panelWidth;
@@ -1622,8 +1619,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     @action
     componentDidMount() {
-        this.rootDoc[DocViews].add(this);
-        this._disposers.updateContentsScript = reaction(() => ScriptCast(this.rootDoc.updateContentsScript)?.script?.run({ this: this.rootDoc, self: this.rootDoc }).result, emptyFunction);
+        this.Document[DocViews].add(this);
+        this._disposers.updateContentsScript = reaction(() => ScriptCast(this.Document.updateContentsScript)?.script?.run({ this: this.Document }).result, emptyFunction);
         this._disposers.height = reaction(
             // increase max auto height if document has been resized to be greater than current max
             () => NumCast(this.layoutDoc._height),
@@ -1636,7 +1633,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
     @action
     componentWillUnmount() {
-        this.rootDoc[DocViews].delete(this);
+        this.Document[DocViews].delete(this);
         Object.values(this._disposers).forEach(disposer => disposer?.());
         !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
     }
@@ -1662,7 +1659,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} />
                         </div>,
                         { ...(this.htmlOverlayEffect ?? {}), presentation_effect: effect ?? PresEffect.Zoom } as any as Doc,
-                        this.rootDoc
+                        this.Document
                     )}
                 </div>
             </div>
@@ -1683,15 +1680,14 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         style={{
                             transition: 'inherit', // this.props.dataTransition,
                             transform: `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                            width: xshift ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
-                            height: this.props.forceAutoHeight
-                                ? undefined
-                                : yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
+                            width: xshift ?? `${this.props.PanelWidth() - this.Xshift * 2}px`,
+                            height: this.props.forceAutoHeight ? undefined : yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(this.effectiveNativeHeight / this.effectiveNativeWidth) * this.props.PanelWidth()}px`),
                         }}>
                         <DocumentViewInternal
                             {...this.props}
                             DocumentView={this.selfView}
                             viewPath={this.docViewPathFunc}
+                            shouldNotScale={this.ShouldNotScale}
                             PanelWidth={this.PanelWidth}
                             PanelHeight={this.PanelHeight}
                             NativeWidth={this.NativeWidth}
@@ -1699,6 +1695,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             NativeDimScaling={this.NativeDimScaling}
                             isSelected={this.isSelected}
                             select={this.select}
+                            layout_fitWidth={this.layout_fitWidthFunc}
                             ScreenToLocalTransform={this.screenToLocalTransform}
                             focus={this.props.focus || emptyFunction}
                             ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
@@ -1719,7 +1716,7 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
 });
 
 ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView) {
-    LightboxView.Instance.AddDocTab(documentView.rootDoc, OpenWhere.lightbox, 'layout'); //, 0);
+    LightboxView.Instance.AddDocTab(documentView.Document, OpenWhere.lightbox, 'layout'); //, 0);
 });
 
 ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index de5ad1631..ba5370360 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -26,7 +26,6 @@ export enum ButtonType {
     TextButton = 'textBtn',
     MenuButton = 'menuBtn',
     DropdownList = 'dropdownList',
-    DropdownButton = 'dropdownBtn',
     ClickButton = 'clickBtn',
     ToggleButton = 'toggleBtn',
     ColorButton = 'colorBtn',
@@ -78,7 +77,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
     // Determining UI Specs
     @computed get label() {
-        return StrCast(this.rootDoc.icon_label, StrCast(this.rootDoc.title));
+        return StrCast(this.dataDoc.icon_label, StrCast(this.Document.title));
     }
     Icon = (color: string, iconFalse?: boolean) => {
         let icon;
@@ -91,13 +90,13 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         return !icon ? null : icon === 'pres-trail' ? TrailsIcon(color) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
     };
     @computed get dropdown() {
-        return BoolCast(this.rootDoc.dropDownOpen);
+        return BoolCast(this.Document.dropDownOpen);
     }
     @computed get buttonList() {
-        return StrListCast(this.rootDoc.btnList);
+        return StrListCast(this.Document.btnList);
     }
     @computed get type() {
-        return StrCast(this.rootDoc.btnType);
+        return StrCast(this.Document.btnType);
     }
 
     /**
@@ -131,8 +130,8 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 type = 'slider';
                 break;
         }
-        const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ this: this.layoutDoc, self: this.rootDoc, value, _readOnly_: value === undefined });
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, value, _readOnly_: value === undefined });
+        const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
         // Script for checking the outcome of the toggle
         const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));
 
@@ -144,52 +143,21 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 showPlusMinus={false}
                 tooltip={this.label}
                 type={Type.PRIM}
-                min={NumCast(this.rootDoc.numBtnMin, 0)}
-                max={NumCast(this.rootDoc.numBtnMax, 100)}
+                min={NumCast(this.dataDoc.numBtnMin, 0)}
+                max={NumCast(this.dataDoc.numBtnMax, 100)}
                 number={checkResult}
-                setNumber={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}
+                setNumber={undoable(value => numScript(value), `${this.Document.title} button set from list`)}
                 fillWidth
             />
         );
     }
 
-    /**
-     * Dropdown button
-     */
-    @computed get dropdownButton() {
-        const active: string = StrCast(this.rootDoc.dropDownOpen);
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        return (
-            <div
-                className={`menuButton ${this.type} ${active}`}
-                style={{ color, backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
-                onClick={action(() => {
-                    this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
-                    this.noTooltip = this.rootDoc.dropDownOpen;
-                    Doc.UnBrushAllDocs();
-                })}>
-                {this.Icon(color)}
-                {!this.label || !FontIconBox.ShowIconLabels ? null : (
-                    <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
-                        {' '}
-                        {this.label}{' '}
-                    </div>
-                )}
-                <div className="menuButton-dropdown" style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
-                    <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
-                </div>
-                {this.rootDoc.dropDownOpen ? <div className="menuButton-dropdownBox">{/* DROPDOWN BOX CONTENTS */}</div> : null}
-            </div>
-        );
-    }
-
     dropdownItemDown = (e: React.PointerEvent, value: string | number) => {
         setupMoveUpEvents(
             this,
             e,
             (e: PointerEvent) => {
-                return ScriptCast(this.rootDoc.onDragScript)?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: { doc: value, e } }).result;
+                return ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, value: { doc: value, e } }).result;
             },
             emptyFunction,
             emptyFunction
@@ -201,7 +169,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
      * Dropdown list
      */
     @computed get dropdownListButton() {
-        const script = ScriptCast(this.rootDoc.script);
+        const script = ScriptCast(this.Document.script);
 
         let noviceList: string[] = [];
         let text: string | undefined;
@@ -237,7 +205,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             }
             noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
         } else {
-            text = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result;
+            text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result;
             // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
             getStyle = (val: string) => ({ fontFamily: val });
         }
@@ -255,7 +223,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         return (
             <Dropdown
                 selectedVal={text}
-                setSelectedVal={undoable(value => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), `dropdown select ${this.label}`)}
+                setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)}
                 color={SettingsManager.userColor}
                 background={SettingsManager.userVariantColor}
                 type={Type.TERT}
@@ -270,7 +238,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     }
 
     @computed get colorScript() {
-        return ScriptCast(this.rootDoc.script);
+        return ScriptCast(this.Document.script);
     }
 
     colorBatch: UndoManager.Batch | undefined;
@@ -278,18 +246,18 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
      * Color button
      */
     @computed get colorButton() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const curColor = this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent';
-        const tooltip: string = StrCast(this.rootDoc.toolTip);
+        const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+        const curColor = this.colorScript?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent';
+        const tooltip: string = StrCast(this.Document.toolTip);
 
         return (
             <ColorPicker
                 setSelectedColor={value => {
                     if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
-                    this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false });
+                    this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
                 }}
                 setFinalColor={value => {
-                    this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false });
+                    this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
                     this.colorBatch?.end();
                     this.colorBatch = undefined;
                 }}
@@ -306,13 +274,13 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     }
     @computed get multiToggleButton() {
         // Determine the type of toggle button
-        const tooltip: string = StrCast(this.rootDoc.toolTip);
+        const tooltip: string = StrCast(this.Document.toolTip);
 
-        const script = ScriptCast(this.rootDoc.onClick);
-        const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
+        const script = ScriptCast(this.Document.onClick);
+        const toggleStatus = script ? script.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result : false;
         // Colors
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const items = DocListCast(this.rootDoc.data);
+        const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+        const items = DocListCast(this.dataDoc.data);
         return (
             <MultiToggle
                 tooltip={`Toggle ${tooltip}`}
@@ -320,7 +288,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 color={color}
                 background={SettingsManager.userBackgroundColor}
                 label={this.label}
-                items={DocListCast(this.rootDoc.data).map(item => ({
+                items={DocListCast(this.dataDoc.data).map(item => ({
                     icon: <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={StrCast(item.icon) as any} color={color} />,
                     tooltip: StrCast(item.toolTip),
                     val: StrCast(item.toolType),
@@ -336,14 +304,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
     @computed get toggleButton() {
         // Determine the type of toggle button
-        const buttonText: string = StrCast(this.rootDoc.buttonText);
-        const tooltip: string = StrCast(this.rootDoc.toolTip);
+        const buttonText = StrCast(this.dataDoc.buttonText);
+        const tooltip = StrCast(this.Document.toolTip);
 
-        const script = ScriptCast(this.rootDoc.onClick);
-        const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
+        const script = ScriptCast(this.Document.onClick);
+        const toggleStatus = script ? script.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result : false;
         // Colors
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
 
         return (
             <Toggle
@@ -356,7 +324,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 //background={SettingsManager.userBackgroundColor}
                 icon={this.Icon(color)!}
                 label={this.label}
-                onPointerDown={() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })}
+                onPointerDown={() => script.script.run({ this: this.Document, value: !toggleStatus, _readOnly_: false })}
             />
         );
     }
@@ -365,20 +333,20 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
      * Default
      */
     @computed get defaultButton() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const tooltip: string = StrCast(this.rootDoc.toolTip);
+        const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
+        const tooltip: string = StrCast(this.Document.toolTip);
 
         return <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label} />;
     }
 
     @computed get editableText() {
         // Script for running the toggle
-        const script = ScriptCast(this.rootDoc.script);
+        const script = ScriptCast(this.Document.script);
         // Function to run the script
-        const checkResult = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result;
+        const checkResult = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result;
 
-        const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value, _readOnly_: false }).result;
+        const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.Document, value, _readOnly_: false }).result;
 
         return <EditableText editing={false} setEditing={(editing: boolean) => {}} />;
 
@@ -386,16 +354,16 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             <div className="menuButton editableText">
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} />
                 <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}>
-                    <EditableView GetValue={() => script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
+                    <EditableView GetValue={() => script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
                 </div>
             </div>
         );
     }
 
     render() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const tooltip = StrCast(this.rootDoc.toolTip);
-        const scriptFunc = () => ScriptCast(this.rootDoc.onClick)?.script.run({ this: this.layoutDoc, self: this.rootDoc, _readOnly_: false });
+        const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+        const tooltip = StrCast(this.Document.toolTip);
+        const scriptFunc = () => ScriptCast(this.Document.onClick)?.script.run({ this: this.Document, self: this.Document, _readOnly_: false });
         const btnProps = { tooltip, icon: this.Icon(color)!, label: this.label };
         // prettier-ignore
         switch (this.type) {
@@ -405,15 +373,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             case ButtonType.EditableText:       return this.editableText; 
             case ButtonType.DropdownList:       return this.dropdownListButton;
             case ButtonType.ColorButton:        return this.colorButton; 
-            case ButtonType.DropdownButton:     return this.dropdownButton;
             case ButtonType.MultiToggleButton:  return this.multiToggleButton;
             case ButtonType.ToggleButton:       return this.toggleButton;
             case ButtonType.ClickButton:
             case ButtonType.ToolButton: return <IconButton {...btnProps} size={Size.LARGE} color={color} />;
             case ButtonType.TextButton: return <Button     {...btnProps} color={color} 
-            background={SettingsManager.userBackgroundColor} text={StrCast(this.rootDoc.buttonText)}/>;
+                        background={SettingsManager.userBackgroundColor} text={StrCast(this.dataDoc.buttonText)}/>;
             case ButtonType.MenuButton: return <IconButton {...btnProps} color={color} 
-            background={SettingsManager.userBackgroundColor}  size={Size.LARGE} tooltipPlacement='right' onPointerDown={scriptFunc} />; 
+                        background={SettingsManager.userBackgroundColor} size={Size.LARGE} tooltipPlacement='right' onPointerDown={scriptFunc} />; 
         }
         return this.defaultButton;
     }
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 40f48dafe..daf6cd9a6 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -38,7 +38,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
     componentDidMount() {
         this.props.setContentView?.(this);
         reaction(
-            () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.rootDoc.xRange, this.rootDoc.yRange],
+            () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.layoutDoc.xRange, this.layoutDoc.yRange],
             () => this.createGraph()
         );
     }
@@ -64,8 +64,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
                 target: '#' + this._plotEle.id,
                 width,
                 height,
-                xAxis: { domain: Cast(this.rootDoc.xRange, listSpec('number'), [-10, 10]) },
-                yAxis: { domain: Cast(this.rootDoc.yRange, listSpec('number'), [-1, 9]) },
+                xAxis: { domain: Cast(this.layoutDoc.xRange, listSpec('number'), [-10, 10]) },
+                yAxis: { domain: Cast(this.layoutDoc.yRange, listSpec('number'), [-1, 9]) },
                 grid: true,
                 data: fns.map(fn => ({
                     fn,
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 103843046..19e393968 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -559,7 +559,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 {this.annotationLayer}
                 {!this._mainCont.current || !this._annotationLayer.current ? null : (
                     <MarqueeAnnotator
-                        rootDoc={this.rootDoc}
+                        Document={this.Document}
+                        ref={this._marqueeref}
                         scrollTop={0}
                         annotationLayerScrollTop={0}
                         scaling={returnOne}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 95aeb5331..95d2a2667 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -110,7 +110,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && this.props.isSelected(true)) {
+        if (e.buttons === 1 && this.props.isSelected()) {
             e.stopPropagation();
         }
     };
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 416cb11cc..30cd65cb4 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -22,12 +22,12 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     onClickScriptDisable = returnAlways;
     @computed get anchor1() {
-        const anchor1 = DocCast(this.rootDoc.link_anchor_1);
+        const anchor1 = DocCast(this.dataDoc.link_anchor_1);
         const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1;
         return DocumentManager.Instance.getDocumentView(anchor_1, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement());
     }
     @computed get anchor2() {
-        const anchor2 = DocCast(this.rootDoc.link_anchor_2);
+        const anchor2 = DocCast(this.dataDoc.link_anchor_2);
         const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2;
         return DocumentManager.Instance.getDocumentView(anchor_2, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement());
     }
@@ -106,22 +106,22 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable renderProps: { lx: number; rx: number; ty: number; by: number; pts: number[][] } | undefined;
     render() {
         if (this.renderProps) {
-            const highlight = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting);
+            const highlight = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Highlighting);
             const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined;
 
             const bez = new Bezier(this.renderProps.pts.map(p => ({ x: p[0], y: p[1] })));
             const text = bez.get(0.5);
-            const linkDesc = StrCast(this.rootDoc.link_description) || 'description';
-            const strokeWidth = NumCast(this.rootDoc.stroke_width, 4);
-            const dash = StrCast(this.rootDoc.stroke_dash);
+            const linkDesc = StrCast(this.dataDoc.link_description) || 'description';
+            const strokeWidth = NumCast(this.dataDoc.stroke_width, 4);
+            const dash = StrCast(this.Document.stroke_dash);
             const strokeDasharray = dash && Number(dash) ? String(strokeWidth * Number(dash)) : undefined;
             const { pts, lx, ty, rx, by } = this.renderProps;
             return (
                 <div style={{ transition: 'inherit', pointerEvents: 'none', position: 'absolute', width: '100%', height: '100%' }}>
                     <svg width={Math.max(100, rx - lx)} height={Math.max(100, by - ty)} style={{ transition: 'inherit', overflow: 'visible' }}>
                         <defs>
-                            <filter x="0" y="0" width="1" height="1" id={`${this.rootDoc[Id] + 'background'}`}>
-                                <feFlood floodColor={`${StrCast(this.rootDoc._backgroundColor, 'lightblue')}`} result="bg" />
+                            <filter x="0" y="0" width="1" height="1" id={`${this.Document[Id] + 'background'}`}>
+                                <feFlood floodColor={`${StrCast(this.layoutDoc._backgroundColor, 'lightblue')}`} result="bg" />
                                 <feMerge>
                                     <feMergeNode in="bg" />
                                     <feMergeNode in="SourceGraphic" />
@@ -141,7 +141,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             ${pts[2][0] + pts[2][0] - pts[3][0] - lx} ${pts[2][1] + pts[2][1] - pts[3][1] - ty}, ${pts[2][0] - lx} ${pts[2][1] - ty}`}
                         />
                         <text
-                            filter={`url(#${this.rootDoc[Id] + 'background'})`}
+                            filter={`url(#${this.Document[Id] + 'background'})`}
                             style={{ pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'all', textAnchor: 'middle', fontSize: '12', stroke: 'black' }}
                             x={text.x - lx}
                             y={text.y - ty}>
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 42bada0ef..56f0a49b8 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -15,17 +15,17 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return FieldView.LayoutString(MapPushpinBox, fieldKey);
     }
     componentDidMount() {
-        this.mapBoxView.addPushpin(this.rootDoc);
+        this.mapBoxView.addPushpin(this.Document);
     }
     componentWillUnmount() {
-        this.mapBoxView.deletePushpin(this.rootDoc);
+        this.mapBoxView.deletePushpin(this.Document);
     }
 
     @computed get mapBoxView() {
         return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as MapBox;
     }
     @computed get mapBox() {
-        return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
+        return this.props.DocumentView?.().props.docViewPath().lastElement()?.Document;
     }
 
     render() {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 108fa5ce5..1035116d5 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -578,7 +578,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <PDFViewer
                         {...this.props}
                         sidebarAddDoc={this.sidebarAddDocument}
-                        rootDoc={this.rootDoc}
                         addDocTab={this.sidebarAddDocTab}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 116069cbd..3838ce4ee 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -62,7 +62,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     public static WorkspaceStopRecording() {
-        const remDoc = RecordingBox.screengrabber?.rootDoc;
+        const remDoc = RecordingBox.screengrabber?.Document;
         if (remDoc) {
             //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening
             RecordingBox.screengrabber?.Pause?.();
@@ -122,7 +122,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
         value.overlayY = window.innerHeight - 180;
         Doc.AddToMyOverlay(value);
         DocumentManager.Instance.AddViewRenderedCb(value, docView => {
-            Doc.UserDoc().currentRecording = docView.rootDoc;
+            Doc.UserDoc().currentRecording = docView.Document;
             docView.select(false);
             RecordingBox.resumeWorkspaceReplaying(value);
         });
@@ -136,9 +136,9 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     public static addRecToWorkspace(value: RecordingBox) {
         let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
-        (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value.rootDoc);
-        Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value.rootDoc);
-        Doc.RemFromMyOverlay(value.rootDoc);
+        (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value.Document);
+        Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value.Document);
+        Doc.RemFromMyOverlay(value.Document);
         Doc.UserDoc().currentRecording = undefined;
         Doc.UserDoc().workspaceReplayingState = undefined;
         Doc.UserDoc().workspaceRecordingState = undefined;
@@ -200,7 +200,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         getControls={this.getControls}
                         setResult={this.setResult}
                         setDuration={this.setVideoDuration}
-                        id={DocCast(this.rootDoc.proto)?.[Id] || ''}
+                        id={DocCast(this.Document.proto)?.[Id] || ''}
                     />
                 )}
             </div>
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index b2512a0e5..224bc6f67 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -290,8 +290,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         dictationText._layout_autoHeight = false;
         const dictationTextProto = Doc.GetProto(dictationText);
         dictationTextProto[`${textField}_recordingSource`] = this.dataDoc;
-        dictationTextProto[`${textField}_recordingStart`] = ComputedField.MakeFunction(`self.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
-        dictationTextProto.mediaState = ComputedField.MakeFunction(`self.${textField}_recordingSource.mediaState`);
+        dictationTextProto[`${textField}_recordingStart`] = ComputedField.MakeFunction(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
+        dictationTextProto.mediaState = ComputedField.MakeFunction(`this.${textField}_recordingSource.mediaState`);
         this.dataDoc[this.fieldKey + '_dictation'] = dictationText;
     };
     videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc._height)) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc._width))) * this.props.PanelWidth();
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 197c520c7..a51a83b26 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -200,7 +200,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         if (this.onCompile()) {
             const bindings: { [name: string]: any } = {};
             this.paramsNames.forEach(key => (bindings[key] = this.rootDoc[key]));
-            // binds vars so user doesnt have to refer to everything as self.<var>
+            // binds vars so user doesnt have to refer to everything as this.<var>
             ScriptCast(this.rootDoc[this.fieldKey], null)?.script.run({ ...bindings, self: this.rootDoc, this: this.layoutDoc }, this.onError);
         }
     };
@@ -748,7 +748,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
     // inputs UI for params which allows you to set values for each displayed in a list
     renderParamsInputs() {
         return (
-            <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected(true) && e.stopPropagation()}>
+            <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected() && e.stopPropagation()}>
                 {!this.compileParams.length || !this.paramsNames ? null : (
                     <div className="scriptingBox-plist">
                         {this.paramsNames.map((parameter: string, i: number) => (
@@ -815,7 +815,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         TraceMobx();
         return (
             <div className={`scriptingBox`} onContextMenu={this.specificContextMenu} onPointerUp={!this._function ? this.suggestionPos : undefined}>
-                <div className="scriptingBox-outerDiv" onWheel={e => this.props.isSelected(true) && e.stopPropagation()}>
+                <div className="scriptingBox-outerDiv" onWheel={e => this.props.isSelected() && e.stopPropagation()}>
                     {this._paramSuggestion ? (
                         <div className="boxed" ref={this._suggestionRef} style={{ left: this._suggestionBoxX + 20, top: this._suggestionBoxY - 15, display: 'inline' }}>
                             {' '}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 8bf2f4ce5..f87d94784 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1054,10 +1054,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
                     borderRadius,
                     overflow: this.props.docViewPath?.().slice(-1)[0].layout_fitWidth ? 'auto' : undefined,
-                }}
-                onWheel={e => {
-                    e.stopPropagation();
-                    e.preventDefault();
                 }}>
                 <div className="videoBox-viewer" onPointerDown={this.marqueeDown}>
                     <div
@@ -1097,7 +1093,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     {!this._mainCont.current || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
                             ref={this._marqueeref}
-                            rootDoc={this.rootDoc}
+                            Document={this.rootDoc}
                             scrollTop={0}
                             annotationLayerScrollTop={0}
                             scaling={returnOne}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e42fb4a03..5c46035be 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1111,7 +1111,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <div style={{ position: 'absolute', height: '100%', width: '100%', pointerEvents: this.marqueeing ? 'all' : 'none' }}>
                         <MarqueeAnnotator
                             ref={this._marqueeref}
-                            rootDoc={this.rootDoc}
+                            Document={this.rootDoc}
                             anchorMenuClick={this.anchorMenuClick}
                             scrollTop={NumCast(this.layoutDoc.layout_scrollTop)}
                             annotationLayerScrollTop={0}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index b65c440d1..dd4be7fcd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -316,7 +316,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
         });
         const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
-        this.props.isSelected() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
+        this.props.rootSelected() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
         let ele: Opt<HTMLDivElement> = undefined;
         try {
             const contents = window.getSelection()?.getRangeAt(0).cloneContents();
@@ -388,7 +388,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     this._editorView.updateState(EditorState.fromJSON(this.config, json));
                 }
             }
-            if (window.getSelection()?.isCollapsed && this.props.isSelected()) {
+            if (window.getSelection()?.isCollapsed && this.props.rootSelected()) {
                 AnchorMenu.Instance.fadeOut(true);
             }
         }
@@ -576,9 +576,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (dragData) {
             const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
             const effectiveAcl = GetEffectiveAcl(dataDoc);
-            let added = [AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl);
             const draggedDoc = dragData.draggedDocuments.lastElement();
-            if (added) {
+            let added: Opt<boolean>;
+            if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) {
                 // replace text contents when dragging with Alt
                 if (de.altKey) {
                     const fieldKey = Doc.LayoutFieldKey(draggedDoc);
@@ -613,8 +613,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     }
                 }
             } // otherwise, fall through to outer collection to handle drop
-            !added && e.preventDefault();
-            e.stopPropagation();
+            added === false && e.preventDefault();
+            added === true && e.stopPropagation();
             return added;
         }
         return false;
@@ -1256,17 +1256,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         );
 
         this._disposers.selected = reaction(
-            () => this.props.isSelected(),
+            () => this.props.rootSelected(),
             action(selected => {
                 //selected && setTimeout(() => this.prepareForTyping());
                 if (FormattedTextBox._globalHighlights.has('Bold Text')) {
                     this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
                 }
                 if (RichTextMenu.Instance?.view === this._editorView && !selected) {
-                    RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
+                    RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
                 }
                 if (this._editorView && selected) {
-                    RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
+                    RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props, this.layoutDoc);
                     setTimeout(this.autoLink, 20);
                 }
             }),
@@ -1451,7 +1451,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         const self = this;
         return new Plugin({
             view(newView) {
-                runInAction(() => self.props.isSelected() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView));
+                runInAction(() => self.props.rootSelected() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView));
                 return new RichTextMenuPlugin({ editorProps: this.props });
             },
         });
@@ -1588,7 +1588,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         FormattedTextBox.LiveTextUndo = undefined;
         this.unhighlightSearchTerms();
         this._editorView?.destroy();
-        RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined);
+        RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined, undefined);
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = 'none');
     }
 
@@ -1624,7 +1624,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this._downX = e.clientX;
         this._downY = e.clientY;
         FormattedTextBoxComment.textBox = this;
-        if (e.button === 0 && (this.props.rootSelected() || this.props.isSelected()) && !e.altKey && !e.ctrlKey && !e.metaKey) {
+        if (e.button === 0 && (this.props.rootSelected() || this.props.rootSelected()) && !e.altKey && !e.ctrlKey && !e.metaKey) {
             if (e.clientX < this.ProseRef!.getBoundingClientRect().right) {
                 // stop propagation if not in sidebar, otherwise nested boxes will lose focus to outer boxes.
                 e.stopPropagation(); // if the text box's content is active, then it consumes all down events
@@ -1658,7 +1658,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     @action
     onDoubleClick = (e: React.MouseEvent): void => {
         FormattedTextBoxComment.textBox = this;
-        if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
+        if (e.button === 0 && this.props.rootSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             if (e.clientX < this.ProseRef!.getBoundingClientRect().right) {
                 // stop propagation if not in sidebar
                 e.stopPropagation(); // if the text box is selected, then it consumes all click events
@@ -1669,7 +1669,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
         FormattedTextBoxComment.Hide();
 
-        if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
+        if (e.buttons === 1 && this.props.rootSelected() && !e.altKey) {
             e.stopPropagation();
         }
     };
@@ -1680,8 +1680,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
     @action
     onFocused = (e: React.FocusEvent): void => {
+        console.log('FOCUSED = ' + this.layoutDoc.title + ' ' + this.props.rootSelected());
         //applyDevTools.applyDevTools(this._editorView);
-        this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
+        this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props, this.layoutDoc);
         e.stopPropagation();
     };
 
@@ -1715,7 +1716,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 this._editorView!.dispatch(this._editorView!.state.tr.setSelection(NodeSelection.create(this._editorView!.state.doc, pcords.pos)));
             }
         }
-        if (this.props.isSelected()) {
+        if (this.props.rootSelected()) {
             // if text box is selected, then it consumes all click events
             (e.nativeEvent as any).handledByInnerReactInstance = true;
             this.hitBulletTargets(e.clientX, e.clientY, !this._editorView?.state.selection.empty || this._forceUncollapse, false, this._forceDownNode, e.shiftKey);
@@ -1729,7 +1730,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
         const clickPos = this._editorView!.posAtCoords({ left: x, top: y });
         let olistPos = clickPos?.pos;
-        if (clickPos && olistPos && this.props.isSelected()) {
+        if (clickPos && olistPos && this.props.rootSelected()) {
             const clickNode = this._editorView?.state.doc.nodeAt(olistPos);
             const nodeBef = this._editorView?.state.doc.nodeAt(Math.max(0, olistPos - 1));
             olistPos = nodeBef?.type === this._editorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos;
@@ -1766,6 +1767,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     @action
     onBlur = (e: any) => {
+        console.log('BLUREd = ' + this.layoutDoc.title + ' ' + this.props.rootSelected());
         if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
         if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) {
             const stordMarks = this._editorView?.state.storedMarks?.slice();
@@ -1778,8 +1780,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 tr && this._editorView.dispatch(tr);
             }
         }
-        if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected()) {
-            RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
+        if (RichTextMenu.Instance?.view === this._editorView && !this.props.rootSelected()) {
+            RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
         }
         FormattedTextBox._hadSelection = window.getSelection()?.toString() !== '';
         this.endUndoTypingBatch();
@@ -1846,7 +1848,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
                 (document.activeElement as any).blur?.();
                 SelectionManager.DeselectAll();
-                RichTextMenu.Instance.updateMenu(undefined, undefined, undefined);
+                RichTextMenu.Instance.updateMenu(undefined, undefined, undefined, undefined);
                 return;
             case 'Enter':
                 this.insertTime();
@@ -2073,10 +2075,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
         // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
         if (this.props.isContentActive()) {
+            const scale = this.props.NativeDimScaling?.() || 1;
+            const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this.props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
+            const height = Number(styleFromLayoutString.height?.replace('px', ''));
             // prevent default if selected || child is active  but this doc isn't scrollable
             if (
-                (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil(this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1)) && //
-                (this.props.isSelected() || this.isAnyChildContentActive())
+                (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil((height ? height : this.props.PanelHeight()) / scale) && //
+                (this.props.rootSelected() || this.isAnyChildContentActive())
             ) {
                 e.preventDefault();
             }
@@ -2103,7 +2108,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         setTimeout(() => !this.props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
         const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
-        const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
+        const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this.props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
         return styleFromLayoutString?.height === '0px' ? null : (
             <div
                 className="formattedTextBox"
@@ -2135,7 +2140,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     ref={this._ref}
                     style={{
                         cursor: this.props.isContentActive() ? 'text' : undefined,
-                        height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
+                        height: this.props.height ? 'max-content' : undefined,
                         pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none',
                     }}
                     onContextMenu={this.specificContextMenu}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 712be91c8..7f9bfe753 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -9,19 +9,19 @@ import { wrapInList } from 'prosemirror-schema-list';
 import { EditorState, NodeSelection, TextSelection } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { Doc } from '../../../../fields/Doc';
-import { Cast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
+import { numberRange } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { LinkManager } from '../../../util/LinkManager';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { EquationBox } from '../EquationBox';
 import { FieldViewProps } from '../FieldView';
 import { FormattedTextBox } from './FormattedTextBox';
 import { updateBullets } from './ProsemirrorExampleTransfer';
 import './RichTextMenu.scss';
 import { schema } from './schema_rts';
-import { EquationBox } from '../EquationBox';
-import { numberRange } from '../../../../Utils';
 const { toggleMark } = require('prosemirror-commands');
 
 @observer
@@ -30,6 +30,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
     private _linkToRef = React.createRef<HTMLInputElement>();
 
+    layoutDoc: Doc | undefined;
     @observable public view?: EditorView;
     public editorProps: FieldViewProps | undefined;
 
@@ -66,7 +67,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         super(props);
         runInAction(() => {
             RichTextMenu.Instance = this;
-            this.updateMenu(undefined, undefined, props);
+            this.updateMenu(undefined, undefined, props, this.layoutDoc);
             this._canFade = false;
             this.Pinned = true;
         });
@@ -102,11 +103,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @computed get textAlign() {
         return this._activeAlignment;
     }
+    @computed get textVcenter() {
+        return BoolCast(this.layoutDoc?.layout_centered);
+    }
     _disposer: IReactionDisposer | undefined;
     componentDidMount() {
         this._disposer = reaction(
             () => SelectionManager.Views().slice(),
-            views => this.updateMenu(undefined, undefined, undefined)
+            views => this.updateMenu(undefined, undefined, undefined, undefined)
         );
     }
     componentWillUnmount() {
@@ -114,11 +118,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     }
 
     @action
-    public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any) {
+    public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any, layoutDoc: Doc | undefined) {
         if (this._linkToRef.current?.getBoundingClientRect().width) {
             return;
         }
         this.view = view;
+        this.layoutDoc = layoutDoc;
         props && (this.editorProps = props);
 
         // Don't do anything if the document/selection didn't change
@@ -181,7 +186,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     // finds font sizes and families in selection
     getActiveAlignment() {
-        if (this.view && this.TextView?.props.isSelected()) {
+        if (this.view && this.TextView?.props.rootSelected()) {
             const path = (this.view.state.selection.$from as any).path;
             for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) {
                 if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) {
@@ -194,7 +199,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     // finds font sizes and families in selection
     getActiveListStyle() {
-        if (this.view && this.TextView?.props.isSelected()) {
+        if (this.view && this.TextView?.props.rootSelected()) {
             const path = (this.view.state.selection.$from as any).path;
             for (let i = 0; i < path.length; i += 3) {
                 if (path[i].type === this.view.state.schema.nodes.ordered_list) {
@@ -214,7 +219,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         const activeSizes = new Set<string>();
         const activeColors = new Set<string>();
         const activeHighlights = new Set<string>();
-        if (this.view && this.TextView?.props.isSelected()) {
+        if (this.view && this.TextView?.props.rootSelected()) {
             const state = this.view.state;
             const pos = this.view.state.selection.$from;
             const marks: Mark[] = [...(state.storedMarks ?? [])];
@@ -249,7 +254,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     //finds all active marks on selection in given group
     getActiveMarksOnSelection() {
         let activeMarks: MarkType[] = [];
-        if (!this.view || !this.TextView?.props.isSelected()) return activeMarks;
+        if (!this.view || !this.TextView?.props.rootSelected()) return activeMarks;
 
         const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
         if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
@@ -285,10 +290,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         return activeMarks;
     }
 
-    destroy() {
-        !this.TextView?.props.isSelected() && this.fadeOut(true);
-    }
-
     @action
     setActiveMarkButtons(activeMarks: MarkType[] | undefined) {
         if (!activeMarks) return;
@@ -360,7 +361,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) {
             SelectionManager.Views().forEach(dv => (dv.rootDoc._text_fontSize = fontSize));
         } else Doc.UserDoc().fontSize = fontSize;
-        this.updateMenu(this.view, undefined, this.props);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
 
     setFontFamily = (family: string) => {
@@ -369,7 +370,7 @@ 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 Doc.UserDoc().fontFamily = family;
-        this.updateMenu(this.view, undefined, this.props);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
 
     setHighlight(color: string) {
@@ -378,7 +379,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.setMark(highlightMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(highlightMark)), true);
             this.view.focus();
         } else Doc.UserDoc()._fontHighlight = color;
-        this.updateMenu(this.view, undefined, this.props);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     }
 
     setColor(color: string) {
@@ -387,7 +388,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.setMark(colorMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(colorMark)), true);
             this.view.focus();
         } else Doc.UserDoc().fontColor = color;
-        this.updateMenu(this.view, undefined, this.props);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     }
 
     // TODO: remove doesn't work
@@ -428,7 +429,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             }
         }
         this.view.focus();
-        this.updateMenu(this.view, undefined, this.props);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
 
     insertSummarizer(state: EditorState, dispatch: any) {
@@ -442,8 +443,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         return true;
     }
 
+    vcenterToggle = (view: EditorView, dispatch: any) => {
+        this.layoutDoc && (this.layoutDoc.layout_centered = !this.layoutDoc.layout_centered);
+    };
     align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => {
-        if (this.TextView?.props.isSelected()) {
+        if (this.TextView?.props.rootSelected()) {
             var tr = view.state.tr;
             view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => {
                 if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) {
@@ -875,6 +879,6 @@ export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps>
         return null;
     }
     update(view: EditorView, lastState: EditorState | undefined) {
-        RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps);
+        RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, (view as any).TextView?.layoutDoc);
     }
 }
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 3e2afd2ce..701ee4053 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -95,7 +95,7 @@ export class RichTextRules {
                 textDocInline.title_custom = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
                 textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
                 textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
-                textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
+                textDocInline._textContext = ComputedField.MakeFunction(`copyField(this.${inlineFieldKey})`);
                 textDoc[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
                 textDoc[inlineFieldKey] = ''; // set a default value for the annotation
                 const node = (state.doc.resolve(start) as any).nodeAfter;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 789509784..bb0ed0943 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -75,7 +75,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     constructor(props: any) {
         super(props);
         if (!PresBox.navigateToDocScript) {
-            PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(self.presentation_targetDoc, self)')!;
+            PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(this.presentation_targetDoc, self)')!;
         }
     }
 
@@ -114,16 +114,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return StrCast(this.layoutDoc.presFieldKey, 'data');
     }
     @computed get childDocs() {
-        return DocListCast(this.rootDoc[this.presFieldKey]);
+        return DocListCast(this.Document[this.presFieldKey]);
     }
     @computed get tagDocs() {
         return this.childDocs.map(doc => Cast(doc.presentation_targetDoc, Doc, null));
     }
     @computed get itemIndex() {
-        return NumCast(this.rootDoc._itemIndex);
+        return NumCast(this.Document._itemIndex);
     }
     @computed get activeItem() {
-        return DocCast(this.childDocs[NumCast(this.rootDoc._itemIndex)]);
+        return DocCast(this.childDocs[NumCast(this.Document._itemIndex)]);
     }
     @computed get targetDoc() {
         return Cast(this.activeItem?.presentation_targetDoc, Doc, null);
@@ -142,13 +142,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
-        if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
+        if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.Document);
     }
     @computed get isPres() {
-        return this.selectedDoc === this.rootDoc;
+        return this.selectedDoc === this.Document;
     }
     @computed get selectedDoc() {
-        return this.selectedDocumentView?.rootDoc;
+        return this.selectedDocumentView?.Document;
     }
     isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentation_targetDoc === layoutDoc;
     clearSelectedArray = () => this.selectedArray.clear();
@@ -170,7 +170,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             () => this.selectedDoc,
             selected => {
                 document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
-                (this._presKeyEvents = selected === this.rootDoc) && document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
+                (this._presKeyEvents = selected === this.Document) && document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
             },
             { fireImmediately: true }
         );
@@ -188,17 +188,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
         this.props.setContentView?.(this);
         this._unmounting = false;
-        if (this.props.renderDepth > 0) {
-            runInAction(() => {
-                this.rootDoc._forceRenderEngine = computeTimelineLayout.name;
-                this.layoutDoc._gridGap = 0;
-                this.layoutDoc._yMargin = 0;
-            });
-        }
         this.turnOffEdit(true);
         this._disposers.selection = reaction(
             () => SelectionManager.Views().slice(),
-            views => (!PresBox.Instance || views.some(view => view.props.Document === this.rootDoc)) && this.updateCurrentPresentation(),
+            views => (!PresBox.Instance || views.some(view => view.Document === this.Document)) && this.updateCurrentPresentation(),
             { fireImmediately: true }
         );
         this._disposers.editing = reaction(
@@ -213,7 +206,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
-        Doc.ActivePresentation = pres ?? this.rootDoc;
+        Doc.ActivePresentation = pres ?? this.Document;
         PresBox.Instance = this;
     };
 
@@ -321,7 +314,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (progressiveReveal(false)) return true;
         if (this.childDocs[this.itemIndex + 1] !== undefined) {
             // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide
-            const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]);
+            const slides = DocListCast(this.Document[StrCast(this.presFieldKey, 'data')]);
             const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex;
 
             // before moving onto next slide, run the subroutines :)
@@ -356,7 +349,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // Case 2: There are no other frames so it should go to the previous slide
             prevSelected = Math.max(0, prevSelected - 1);
             this.nextSlide(prevSelected);
-            this.rootDoc._itemIndex = prevSelected;
+            this.Document._itemIndex = prevSelected;
         } else if (this.childDocs[this.itemIndex - 1] === undefined && this.layoutDoc.presLoop) {
             // Case 3: Pres loop is on so it should go to the last slide
             this.nextSlide(this.childDocs.length - 1);
@@ -370,7 +363,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public gotoDocument = action((index: number, from?: Doc, group?: boolean, finished?: () => void) => {
         Doc.UnBrushAllDocs();
         if (index >= 0 && index < this.childDocs.length) {
-            this.rootDoc._itemIndex = index;
+            this.Document._itemIndex = index;
             if (from?.mediaStopTriggerList && this.layoutDoc.presentation_status !== PresStatus.Edit) {
                 DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
             }
@@ -410,7 +403,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     playAnnotation = (anno: AudioField) => {};
     @action
     static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.config_pinLayout), pinDataTypes?: pinDataTypes, targetDoc?: Doc) {
-        const bestTarget = bestTargetView?.rootDoc ?? (targetDoc?.layout_unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
+        const bestTarget = bestTargetView?.Document ?? (targetDoc?.layout_unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
         if (!bestTarget) return;
         let changed = false;
         if (pinDocLayout) {
@@ -441,7 +434,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.CollectionFreeFormView;
                 if (ffview?.childDocs) {
                     PresBox.Instance._keyTimer = CollectionFreeFormView.gotoKeyframe(PresBox.Instance._keyTimer, ffview.childDocs, transTime);
-                    ffview.rootDoc._currentFrame = NumCast(activeFrame);
+                    ffview.layoutDoc._currentFrame = NumCast(activeFrame);
                 }
             }
         }
@@ -600,7 +593,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 });
             setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10);
         }
-        if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.config_viewBounds !== undefined || activeItem.config_panX !== undefined || activeItem.config_viewScale !== undefined))) && !bestTarget._isGroup) {
+        if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.config_viewBounds !== undefined || activeItem.config_panX !== undefined || activeItem.config_viewScale !== undefined))) && !bestTarget.isGroup) {
             const contentBounds = Cast(activeItem.config_viewBounds, listSpec('number'));
             if (contentBounds) {
                 const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
@@ -755,7 +748,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const eleViewCache = Array.from(this._eleArray);
         const resetSelection = action(() => {
             if (!this.props.isSelected()) {
-                const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
+                const presDocView = DocumentManager.Instance.getDocumentView(this.Document);
                 if (presDocView) SelectionManager.SelectView(presDocView, false);
                 this.clearSelectedArray();
                 selViewCache.forEach(doc => this.addToSelectedArray(doc));
@@ -897,7 +890,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     }
                 });
             LightboxView.Instance.SetLightboxDoc(undefined);
-            Doc.RemFromMyOverlay(this.rootDoc);
+            Doc.RemFromMyOverlay(this.Document);
             return PresStatus.Edit;
         };
     };
@@ -936,7 +929,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // The function allows for expanding the view of pres on toggle
     @action toggleExpandMode = () => {
         runInAction(() => (this._expandBoolean = !this._expandBoolean));
-        this.rootDoc.expandBoolean = this._expandBoolean;
+        this.Document.expandBoolean = this._expandBoolean;
         this.childDocs.forEach(doc => {
             doc.presentation_expandInlineButton = this._expandBoolean;
         });
@@ -986,16 +979,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     enterMinimize = () => {
-        this.updateCurrentPresentation(this.rootDoc);
+        this.updateCurrentPresentation(this.Document);
         clearTimeout(this._presTimer);
         const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
         this.props.removeDocument?.(this.layoutDoc);
-        return PresBox.OpenPresMinimized(this.rootDoc, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]);
+        return PresBox.OpenPresMinimized(this.Document, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]);
     };
     exitMinimize = () => {
         if (Doc.IsInMyOverlay(this.layoutDoc)) {
-            Doc.RemFromMyOverlay(this.rootDoc);
-            CollectionDockingView.AddSplit(this.rootDoc, OpenWhereMod.right);
+            Doc.RemFromMyOverlay(this.Document);
+            CollectionDockingView.AddSplit(this.Document, OpenWhereMod.right);
         }
         return PresStatus.Edit;
     };
@@ -1021,8 +1014,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const type_collection = e.target.selectedOptions[0].value as CollectionViewType;
         this.layoutDoc.presFieldKey = this.fieldKey + (type_collection === CollectionViewType.Tree ? '-linearized' : '');
         // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
-        [CollectionViewType.Tree || CollectionViewType.Stacking].includes(type_collection) && (this.rootDoc._pivotField = undefined);
-        this.rootDoc._type_collection = type_collection;
+        [CollectionViewType.Tree || CollectionViewType.Stacking].includes(type_collection) && (this.Document._pivotField = undefined);
+        this.Document._type_collection = type_collection;
         if (this.isTreeOrStack) {
             this.layoutDoc._gridGap = 0;
         }
@@ -1083,7 +1076,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return true;
     };
     childLayoutTemplate = () => Docs.Create.PresElementBoxDocument();
-    removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
+    removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.Document, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
     /**
@@ -1123,7 +1116,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     selectPres = () => {
-        const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
+        const presDocView = DocumentManager.Instance.getDocumentView(this.Document);
         presDocView && SelectionManager.SelectView(presDocView, false);
     };
 
@@ -1139,7 +1132,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (noNav) {
             const index = this.childDocs.indexOf(doc);
             if (index >= 0 && index < this.childDocs.length) {
-                this.rootDoc._itemIndex = index;
+                this.Document._itemIndex = index;
             }
         } else {
             this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
@@ -1237,8 +1230,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             case 'ArrowRight':
                 if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) {
                     // TODO: update to work properly
-                    this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1;
-                    this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]);
+                    this.Document._itemIndex = NumCast(this.Document._itemIndex) + 1;
+                    this.addToSelectedArray(this.childDocs[this.Document._itemIndex]);
                 } else {
                     this.next();
                     if (this._presTimer) {
@@ -1254,8 +1247,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             case 'ArrowLeft':
                 if (e.shiftKey && this.itemIndex !== 0) {
                     // TODO: update to work properly
-                    this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1;
-                    this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]);
+                    this.Document._itemIndex = NumCast(this.Document._itemIndex) - 1;
+                    this.addToSelectedArray(this.childDocs[this.Document._itemIndex]);
                 } else {
                     this.back();
                     if (this._presTimer) {
@@ -1640,8 +1633,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 let dataField = Doc.LayoutFieldKey(tagDoc);
                                 if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
 
-                                if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc.annotationOn?.["${dataField}"]`);
-                                else activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc?.["${dataField}"]`);
+                                if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
+                                else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`);
                             }}
                             checked={Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? true : false}
                         />
@@ -2221,7 +2214,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc;
             const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentation_targetDoc ?? tab;
             const data = Cast(presCollection?.data, listSpec(Doc));
-            const config_data = Cast(this.rootDoc.data, listSpec(Doc));
+            const config_data = Cast(this.Document.data, listSpec(Doc));
             if (data && config_data) {
                 data.push(doc);
                 this.props.pinToPres(doc, {});
@@ -2297,11 +2290,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get toolbar() {
         const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
         const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
-        const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType;
+        const mode = StrCast(this.Document._type_collection) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
         const activeColor = SettingsManager.userVariantColor;
         const inactiveColor = lightOrDark(SettingsManager.userBackgroundColor) === Colors.WHITE ? Colors.WHITE : SettingsManager.userBackgroundColor;
-        return mode === CollectionViewType.Carousel3D || Doc.IsInMyOverlay(this.rootDoc) ? null : (
+        return mode === CollectionViewType.Carousel3D || Doc.IsInMyOverlay(this.Document) ? null : (
             <div id="toolbarContainer" className={'presBox-toolbar'}>
                 {/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
                     <FontAwesomeIcon icon={"plus"} />
@@ -2340,12 +2333,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * presentPanel: The button to start the presentation / open minimized view of the presentation
      */
     @computed get topPanel() {
-        const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType;
+        const mode = StrCast(this.Document._type_collection) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
         return (
             <div
-                className={`presBox-buttons${Doc.IsInMyOverlay(this.rootDoc) ? ' inOverlay' : ''}`}
-                style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
+                className={`presBox-buttons${Doc.IsInMyOverlay(this.Document) ? ' inOverlay' : ''}`}
+                style={{ background: Doc.ActivePresentation === this.Document ? Colors.LIGHT_BLUE : undefined, display: !this.Document._chromeHidden ? 'none' : undefined }}>
                 {isMini ? null : (
                     <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presentation_status === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
                         <option onPointerDown={StopEvent} value={CollectionViewType.Stacking}>
@@ -2398,7 +2391,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.itemIndex === this.childDocs.length - 1 &&
             (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
         const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
-        const inOverlay = Doc.IsInMyOverlay(this.rootDoc);
+        const inOverlay = Doc.IsInMyOverlay(this.Document);
         // Case 1: There are still other frames and should go through all frames before going to next slide
         return (
             <div className="presPanelOverlay" style={{ display: this.layoutDoc.presentation_status !== 'edit' ? 'inline-flex' : 'none' }}>
@@ -2569,7 +2562,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     render() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
-        const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType;
+        const mode = StrCast(this.Document._type_collection) as CollectionViewType;
         const presEnd =
             !this.layoutDoc.presLoop &&
             this.itemIndex === this.childDocs.length - 1 &&
@@ -2579,7 +2572,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             <div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}>
                 <div
                     className="presPanelOverlay"
-                    style={{ display: 'inline-flex', height: 30, background: Doc.ActivePresentation === this.rootDoc ? 'green' : '#323232', top: 0, zIndex: 3000000, boxShadow: this._presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
+                    style={{ display: 'inline-flex', height: 30, background: Doc.ActivePresentation === this.Document ? 'green' : '#323232', top: 0, zIndex: 3000000, boxShadow: this._presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                     <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
                         <div
                             className="presPanel-button"
@@ -2617,7 +2610,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div>
         ) : (
-            <div className="presBox-cont" style={{ minWidth: Doc.IsInMyOverlay(this.rootDoc) ? PresBox.minimizedWidth : undefined }}>
+            <div className="presBox-cont" style={{ minWidth: Doc.IsInMyOverlay(this.Document) ? PresBox.minimizedWidth : undefined }}>
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
@@ -2637,7 +2630,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 childOpacity={returnOne}
                                 childClickScript={PresBox.navigateToDocScript}
                                 childLayoutTemplate={this.childLayoutTemplate}
-                                childXPadding={Doc.IsComicStyle(this.rootDoc) ? 20 : undefined}
+                                childXPadding={Doc.IsComicStyle(this.Document) ? 20 : undefined}
                                 filterAddDocument={this.addDocumentFilter}
                                 removeDocument={returnFalse}
                                 dontRegisterView={true}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 10a9395a0..53fb991e7 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -40,7 +40,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // Idea: this boolean will determine whether to automatically show the video when this preselement is selected.
     // @observable static showVideo: boolean = false;
     @computed get indexInPres() {
-        return DocListCast(this.presBox?.[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc);
+        return DocListCast(this.presBox?.[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.Document);
     } // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
     @computed get expandViewHeight() {
         return 100;
@@ -55,16 +55,16 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as PresBox;
     }
     @computed get presBox() {
-        return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
+        return this.props.DocumentView?.().props.docViewPath().lastElement()?.Document;
     }
     @computed get targetDoc() {
-        return Cast(this.rootDoc.presentation_targetDoc, Doc, null) || this.rootDoc;
+        return Cast(this.Document.presentation_targetDoc, Doc, null) || this.Document;
     }
 
     componentDidMount() {
         this.layoutDoc.layout_hideLinkButton = true;
         this._heightDisposer = reaction(
-            () => ({ expand: this.rootDoc.presentation_expandInlineButton, height: this.collapsedHeight }),
+            () => ({ expand: this.Document.presentation_expandInlineButton, height: this.collapsedHeight }),
             ({ expand, height }) => (this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0)),
             { fireImmediately: true }
         );
@@ -79,7 +79,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
 
     @action
-    presExpandDocumentClick = () => (this.rootDoc.presentation_expandInlineButton = !this.rootDoc.presentation_expandInlineButton);
+    presExpandDocumentClick = () => (this.Document.presentation_expandInlineButton = !this.Document.presentation_expandInlineButton);
 
     embedHeight = (): number => this.collapsedHeight + this.expandViewHeight;
     // embedWidth = () => this.props.PanelWidth();
@@ -94,10 +94,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * presentation element.
      */
     @computed get renderEmbeddedInline() {
-        return !this.rootDoc.presentation_expandInlineButton || !this.targetDoc ? null : (
+        return !this.Document.presentation_expandInlineButton || !this.targetDoc ? null : (
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}>
                 <DocumentView
-                    Document={PresBox.targetRenderedDoc(this.rootDoc)}
+                    Document={PresBox.targetRenderedDoc(this.Document)}
                     DataDoc={undefined} //this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
@@ -158,9 +158,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get transition() {
         let transitionInS: number;
-        if (this.rootDoc.presentation_transition) transitionInS = NumCast(this.rootDoc.presentation_transition) / 1000;
+        if (this.Document.presentation_transition) transitionInS = NumCast(this.Document.presentation_transition) / 1000;
         else transitionInS = 0.5;
-        return this.rootDoc.presentation_movement === PresMovement.Jump || this.rootDoc.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
+        return this.Document.presentation_movement === PresMovement.Jump || this.Document.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
     }
 
     private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -173,11 +173,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         e.stopPropagation();
         e.preventDefault();
         if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) {
-            this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true, false);
+            this.presBoxView?.regularSelect(this.Document, this._itemRef.current!, this._dragRef.current!, true, false);
             setupMoveUpEvents(this, e, this.startDrag, emptyFunction, e => {
                 e.stopPropagation();
                 e.preventDefault();
-                this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+                this.presBoxView?.modifierSelect(this.Document, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
                 this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem);
             });
         }
@@ -188,10 +188,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     startDrag = (e: PointerEvent) => {
         const miniView: boolean = this.toolbarWidth <= 100;
-        const activeItem = this.rootDoc;
+        const activeItem = this.Document;
         const dragArray = this.presBoxView?._dragArray ?? [];
         const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
-        if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
+        if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.Document);
         dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.props.DocumentView?.()?.props.treeViewDoc;
         dragData.moveDocument = this.props.moveDocument;
         const dragItem: HTMLElement[] = [];
@@ -273,8 +273,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
             runInAction(() => (this.presBox!.itemIndex = (this.presBoxView?.itemIndex || 0) - 1));
         }
-        this.props.removeDocument?.(this.rootDoc);
-        this.presBoxView?.removeFromSelectedArray(this.rootDoc);
+        this.props.removeDocument?.(this.Document);
+        this.presBoxView?.removeFromSelectedArray(this.Document);
         this.removeAllRecordingInOverlay();
     }, 'Remove doc from pres trail');
 
@@ -282,7 +282,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     onSetValue = (value: string) => {
-        this.rootDoc.title = !value.trim().length ? '-untitled-' : value;
+        this.Document.title = !value.trim().length ? '-untitled-' : value;
         return true;
     };
 
@@ -328,7 +328,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     @computed get recordingIsInOverlay() {
-        return Doc.MyOverlayDocs.some(doc => doc.slides === this.rootDoc);
+        return Doc.MyOverlayDocs.some(doc => doc.slides === this.Document);
     }
 
     // a previously recorded video will have timecode defined
@@ -338,7 +338,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     removeAllRecordingInOverlay = () => {
-        Doc.MyOverlayDocs.filter(doc => doc.slides === this.rootDoc).forEach(Doc.RemFromMyOverlay);
+        Doc.MyOverlayDocs.filter(doc => doc.slides === this.Document).forEach(Doc.RemFromMyOverlay);
     };
 
     static removeEveryExistingRecordingInOverlay = () => {
@@ -413,10 +413,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     lfg = (e: React.MouseEvent) => {
         e.stopPropagation();
         // TODO: fix this bug
-        const { toggleChildrenRun } = this.rootDoc;
-        TreeView.ToggleChildrenRun.get(this.rootDoc)?.();
+        const { toggleChildrenRun } = this.Document;
+        TreeView.ToggleChildrenRun.get(this.Document)?.();
 
-        // call this.rootDoc.recurChildren() to get all the children
+        // call this.Document.recurChildren() to get all the children
         // if (iconClick) PresElementBox.showVideo = false;
     };
 
@@ -434,8 +434,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presBoxColor = StrCast(presBox?._backgroundColor);
         const presColorBool = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
         const targetDoc = this.targetDoc;
-        const activeItem = this.rootDoc;
-        const hasChildren = BoolCast(this.rootDoc?.hasChildren);
+        const activeItem = this.Document;
+        const hasChildren = BoolCast(this.Document?.hasChildren);
 
         const items: JSX.Element[] = [];
 
@@ -498,14 +498,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             );
         }
         items.push(
-            <Tooltip key="eye" title={<div className="dash-tooltip">{this.rootDoc.presentation_expandInlineButton ? 'Minimize' : 'Expand'}</div>}>
+            <Tooltip key="eye" title={<div className="dash-tooltip">{this.Document.presentation_expandInlineButton ? 'Minimize' : 'Expand'}</div>}>
                 <div
                     className="slideButton"
                     onClick={e => {
                         e.stopPropagation();
                         this.presExpandDocumentClick();
                     }}>
-                    <FontAwesomeIcon icon={this.rootDoc.presentation_expandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
+                    <FontAwesomeIcon icon={this.Document.presentation_expandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
                 </div>
             </Tooltip>
         );
@@ -536,18 +536,18 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get mainItem() {
-        const isSelected: boolean = this.selectedArray?.has(this.rootDoc) ? true : false;
+        const isSelected: boolean = this.selectedArray?.has(this.Document) ? true : false;
         const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres;
         const miniView: boolean = this.toolbarWidth <= 110;
         const presBox = this.presBox; //presBox
         const presBoxColor: string = StrCast(presBox?._backgroundColor);
         const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
-        const activeItem: Doc = this.rootDoc;
+        const activeItem: Doc = this.Document;
 
         return (
             <div
                 className="presItem-container"
-                key={this.props.Document[Id] + this.indexInPres}
+                key={this.Document[Id] + this.indexInPres}
                 ref={this._itemRef}
                 style={{
                     backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
@@ -559,7 +559,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }}
                 onDoubleClick={action(e => {
                     this.toggleProperties();
-                    this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false);
+                    this.presBoxView?.regularSelect(this.Document, this._itemRef.current!, this._dragRef.current!, false);
                 })}
                 onPointerOver={this.onPointerOver}
                 onPointerLeave={this.onPointerLeave}
@@ -571,7 +571,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 ) : (
                     <div
                         ref={this._dragRef}
-                        className={`presItem-slide ${isCurrent ? 'active' : ''}${this.rootDoc.runProcess ? ' testingv2' : ''}`}
+                        className={`presItem-slide ${isCurrent ? 'active' : ''}${this.Document.runProcess ? ' testingv2' : ''}`}
                         style={{
                             display: 'infline-block',
                             backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
@@ -583,7 +583,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             style={{
                                 display: 'inline-flex',
                                 pointerEvents: isSelected ? undefined : 'none',
-                                width: `calc(100% ${this.rootDoc.presentation_expandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
+                                width: `calc(100% ${this.Document.presentation_expandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
                                 cursor: isSelected ? 'text' : 'grab',
                             }}>
                             <div
@@ -611,6 +611,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     render() {
-        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem;
+        return !(this.Document instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem;
     }
 }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index ca9bf7bd2..33a336de3 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -34,7 +34,6 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.16.105/
 
 interface IViewerProps extends FieldViewProps {
     Document: Doc;
-    rootDoc: Doc;
     dataDoc: Doc;
     layoutDoc: Doc;
     fieldKey: string;
@@ -168,7 +167,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     scrollFocus = (doc: Doc, scrollTop: number, options: DocFocusOptions) => {
         const mainCont = this._mainCont.current;
         let focusSpeed: Opt<number>;
-        if (doc !== this.props.rootDoc && mainCont) {
+        if (doc !== this.props.Document && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
             const scrollTo = Utils.scrollIntoView(scrollTop, doc[Height](), NumCast(this.props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._layout_scrollTop) {
@@ -577,7 +576,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     {!this._mainCont.current || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
                             ref={this._marqueeref}
-                            rootDoc={this.props.rootDoc}
+                            Document={this.props.Document}
                             getPageFromScroll={this.getPageFromScroll}
                             anchorMenuClick={this.props.anchorMenuClick}
                             scrollTop={0}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index aa46e57dc..74464181f 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -186,7 +186,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     @action
     searchCollection(query: string) {
         this._selectedResult = undefined;
-        this._results = SearchUtil.SearchCollection(CollectionDockingView.Instance?.rootDoc, query);
+        this._results = SearchUtil.SearchCollection(CollectionDockingView.Instance?.Document, query);
 
         this.computePageRanks();
     }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e2746091e..993715e6f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -537,7 +537,7 @@ export namespace Doc {
         embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
         embedding.author = Doc.CurrentUserEmail;
 
-        Doc.AddDocToList(Doc.GetProto(doc)[DocData], 'proto_embeddings', embedding);
+        Doc.AddDocToList(doc[DocData], 'proto_embeddings', embedding);
 
         return embedding;
     }
@@ -788,8 +788,9 @@ export namespace Doc {
                             const dataDoc = Doc.GetProto(targetDoc);
                             newLayoutDoc.resolvedDataDoc = dataDoc;
                             newLayoutDoc['acl-Guest'] = SharingPermissions.Edit;
-                            if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List && (templateLayoutDoc[templateField] as any).length) {
-                                dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
+                            if (dataDoc[templateField] === undefined && (templateLayoutDoc[templateField] as any)?.length) {
+                                dataDoc[templateField] = ObjectField.MakeCopy(templateLayoutDoc[templateField] as List<Doc>);
+                                // ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"])`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
                             }
                             targetDoc[expandedLayoutFieldKey] = newLayoutDoc;
 
@@ -867,7 +868,7 @@ export namespace Doc {
     }
 
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
-        const copy = new Doc(copyProtoId, true);
+        const copy = runInAction(() => new Doc(copyProtoId, true));
         updateCachedAcls(copy);
         const exclude = [...StrListCast(doc.cloneFieldFilter), 'dragFactory_count', 'cloneFieldFilter'];
         Object.keys(doc)
@@ -900,7 +901,7 @@ export namespace Doc {
             Doc.GetProto(copy).embedContainer = undefined;
             Doc.GetProto(copy).proto_embeddings = new List<Doc>([copy]);
         } else {
-            Doc.AddDocToList(Doc.GetProto(copy)[DocData], 'proto_embeddings', copy);
+            Doc.AddDocToList(copy[DocData], 'proto_embeddings', copy);
         }
         copy.embedContainer = undefined;
         if (retitle) {
@@ -1305,12 +1306,12 @@ export namespace Doc {
         });
     }
 
-    export function styleFromLayoutString(rootDoc: Doc, layoutDoc: Doc, props: any, scale: number) {
+    export function styleFromLayoutString(doc: Doc, props: any, scale: number) {
         const style: { [key: string]: any } = {};
         const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'backgroundColor', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position'];
         const replacer = (match: any, expr: string, offset: any, string: any) => {
             // bcz: this executes a script to convert a property expression string:  { script }  into a value
-            return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: rootDoc, this: layoutDoc, scale }).result?.toString() ?? '';
+            return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ this: doc, self: doc, scale }).result?.toString() ?? '';
         };
         divKeys.map((prop: string) => {
             const p = props[prop];
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 2b8750714..cd07a8885 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -151,7 +151,7 @@ export class ComputedField extends ScriptField {
     _lastComputedResult: any;
     //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc
     value = computedFn((doc: Doc) => this._valueOutsideReaction(doc));
-    _valueOutsideReaction = (doc: Doc) => (this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) ?? doc, value: '', _last_: this._lastComputedResult, _readOnly_: true }, console.log).result);
+    _valueOutsideReaction = (doc: Doc) => (this._lastComputedResult = this.script.run({ this: doc, self: doc, value: '', _last_: this._lastComputedResult, _readOnly_: true }, console.log).result);
 
     [ToValue](doc: Doc) {
         return ComputedField.toValue(doc, this);
@@ -167,35 +167,35 @@ export class ComputedField extends ScriptField {
         return compiled.compiled ? new ComputedField(compiled, compiledsetscript) : undefined;
     }
     public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt<number>) {
-        if (!doc[`${fieldKey}-indexed`]) {
+        if (!doc[`${fieldKey}_indexed`]) {
             const flist = new List<number>(numberRange(curTimecode + 1).map(i => undefined) as any as number[]);
             flist[curTimecode] = Cast(doc[fieldKey], 'number', null);
-            doc[`${fieldKey}-indexed`] = flist;
+            doc[`${fieldKey}_indexed`] = flist;
         }
-        const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, ${defaultVal})`, {}, true, {});
-        const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {});
+        const getField = ScriptField.CompileScript(`getIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey}, ${defaultVal})`, {}, true, {});
+        const setField = ScriptField.CompileScript(`setIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey}, value)`, { value: 'any' }, true, {});
         return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
     }
     public static MakeInterpolatedString(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
-        if (!doc[`${fieldKey}-indexed`]) {
+        if (!doc[`${fieldKey}_`]) {
             const flist = new List<string>(numberRange(curTimecode + 1).map(i => undefined) as any as string[]);
             flist[curTimecode] = StrCast(doc[fieldKey]);
-            doc[`${fieldKey}-indexed`] = flist;
+            doc[`${fieldKey}_indexed`] = flist;
         }
-        const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {});
-        const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {});
+        const getField = ScriptField.CompileScript(`getIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey})`, {}, true, {});
+        const setField = ScriptField.CompileScript(`setIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey}, value)`, { value: 'any' }, true, {});
         return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
     }
     public static MakeInterpolatedDataField(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
         if (doc[`${fieldKey}`] instanceof List) return;
-        if (!doc[`${fieldKey}-indexed`]) {
+        if (!doc[`${fieldKey}_indexed`]) {
             const flist = new List<Field>(numberRange(curTimecode + 1).map(i => undefined) as any as Field[]);
             flist[curTimecode] = Field.Copy(doc[fieldKey]);
-            doc[`${fieldKey}-indexed`] = flist;
+            doc[`${fieldKey}_indexed`] = flist;
         }
-        const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {});
+        const getField = ScriptField.CompileScript(`getIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey})`, {}, true, {});
         const setField = ScriptField.CompileScript(
-            `{setIndexVal (self['${fieldKey}-indexed'], self.${interpolatorKey}, value); console.log(self["${fieldKey}-indexed"][self.${interpolatorKey}],self.data,self["${fieldKey}-indexed"]))}`,
+            `{setIndexVal (this['${fieldKey}_indexed'], this.${interpolatorKey}, value); console.log(this["${fieldKey}_indexed"][this.${interpolatorKey}],this.data,this["${fieldKey}_indexed"]))}`,
             { value: 'any' },
             false,
             {}
-- 
cgit v1.2.3-70-g09d2


From 50e770e42bd036d092f3c774921be87045dcd1ef Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 30 Nov 2023 21:01:57 -0500
Subject: final rootDoc refactoring

---
 src/client/util/CurrentUserUtils.ts                |   4 +-
 src/client/util/DragManager.ts                     |   6 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |   4 +-
 src/client/util/SearchUtil.ts                      |   6 +-
 src/client/util/SelectionManager.ts                |   2 +-
 src/client/util/SharingManager.tsx                 |   6 +-
 src/client/views/DashboardView.tsx                 |   2 +-
 src/client/views/DocumentButtonBar.tsx             |  16 ++--
 src/client/views/DocumentDecorations.tsx           | 100 ++++++++++-----------
 src/client/views/FilterPanel.tsx                   |   8 +-
 src/client/views/GestureOverlay.tsx                |   2 +-
 src/client/views/InkStrokeProperties.ts            |  16 ++--
 src/client/views/MarqueeAnnotator.tsx              |   6 +-
 src/client/views/PropertiesButtons.tsx             |  20 ++---
 src/client/views/PropertiesDocContextSelector.tsx  |   2 +-
 src/client/views/PropertiesView.tsx                |   8 +-
 src/client/views/StyleProvider.tsx                 |   8 +-
 .../views/collections/CollectionNoteTakingView.tsx |   4 +-
 .../views/collections/CollectionStackingView.tsx   |   8 +-
 src/client/views/collections/CollectionSubView.tsx |   4 +-
 src/client/views/collections/CollectionView.tsx    |   2 +-
 src/client/views/collections/TabDocView.tsx        |   4 +-
 src/client/views/collections/TreeView.tsx          |   2 +-
 .../CollectionFreeFormLinkView.tsx                 |  12 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  14 +--
 .../collectionLinear/CollectionLinearView.tsx      |   4 +-
 .../collectionSchema/CollectionSchemaView.tsx      |   4 +-
 src/client/views/global/globalScripts.ts           |  24 ++---
 src/client/views/linking/LinkMenu.tsx              |   2 +-
 src/client/views/linking/LinkMenuGroup.tsx         |   4 +-
 src/client/views/linking/LinkMenuItem.tsx          |   2 +-
 .../components/Recommendation/Recommendation.tsx   |   2 +-
 src/client/views/nodes/ColorBox.tsx                |   6 +-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |   2 +-
 .../nodes/DataVizBox/components/Histogram.tsx      |  11 ++-
 .../nodes/DataVizBox/components/LineChart.tsx      |   4 +-
 .../views/nodes/DataVizBox/components/PieChart.tsx |  10 +--
 .../views/nodes/DataVizBox/components/TableBox.tsx |  16 ++--
 src/client/views/nodes/DocumentIcon.tsx            |   4 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   8 +-
 .../views/nodes/FontIconBox/ButtonInterface.ts     |  10 +--
 src/client/views/nodes/LinkBox.tsx                 |   8 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   4 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |   4 +-
 47 files changed, 200 insertions(+), 201 deletions(-)

(limited to 'src/client/views/DashboardView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 7070a47e8..a4eaa71ef 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -110,7 +110,7 @@ export class CurrentUserUtils {
         const tempClicks = DocCast(doc[field]);
         const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, isSystem: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
-            { opts: { title: "Open In Target",        targetScriptKey: "onChildClick"},       script: "docCastAsync(documentView?.props.docViewPath().lastElement()?.rootDoc.target).then((target) => target && (target.proto.data = new List([self])))"},
+            { opts: { title: "Open In Target",        targetScriptKey: "onChildClick"},       script: "docCastAsync(documentView?.props.docViewPath().lastElement()?.Document.target).then((target) => target && (target.proto.data = new List([self])))"},
             { opts: { title: "Open Detail On Right",  targetScriptKey: "onChildDoubleClick"}, script: `openDoc(this.doubleClickView.${OpenWhere.addRight})`}];
         const reqdClickList = reqdTempOpts.map(opts => {
             const allOpts = {...reqdClickOpts, ...opts.opts};
@@ -816,7 +816,7 @@ export class CurrentUserUtils {
     // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field
     // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents
     static setupSharedDocs(doc: Doc, sharingDocumentId: string) {
-        const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(documentView.props.treeViewDoc, 'viewed', documentView.rootDoc);}";
+        const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(documentView.props.treeViewDoc, 'viewed', documentView.Document);}";
         
         const sharedScripts = {  treeView_ChildDoubleClick: dblClkScript,  }
         const sharedDocOpts:DocumentOptions = {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 6e4de252d..162a0a11f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -261,7 +261,7 @@ export namespace DragManager {
 
     // drags a linker button and creates a link on drop
     export function StartLinkDrag(ele: HTMLElement, sourceView: DocumentView, sourceDocGetAnchor: undefined | ((addAsAnnotation: boolean) => Doc), downX: number, downY: number, options?: DragOptions) {
-        StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.(true) ?? sourceView.rootDoc), downX, downY, options);
+        StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.(true) ?? sourceView.Document), downX, downY, options);
     }
 
     // drags a column from a schema view
@@ -611,7 +611,7 @@ export namespace DragManager {
 
 ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) {
     if (readOnly) {
-        return SelectionManager.Views().some(dv => dv.rootDoc.keepZWhenDragged);
+        return SelectionManager.Views().some(dv => dv.Document.keepZWhenDragged);
     }
-    SelectionManager.Views().map(dv => (dv.rootDoc.keepZWhenDragged = !dv.rootDoc.keepZWhenDragged));
+    SelectionManager.Views().map(dv => (dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged));
 });
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 1a4c2450e..c45143f43 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -112,7 +112,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
                 sizes.push(file.size);
                 modifiedDates.push(file.lastModified);
             });
-            collector.push(...(await Networking.UploadFilesToServer<Upload.ImageInformation>(batch.map(file =>({file})))));
+            collector.push(...(await Networking.UploadFilesToServer<Upload.ImageInformation>(batch.map(file => ({ file })))));
             runInAction(() => (this.completed += batch.length));
         });
 
@@ -158,7 +158,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
             y: NumCast(doc.y) + offset,
         };
         const parent = this.props.DocumentView?.().props.docViewPath().lastElement();
-        if (parent?.rootDoc.type === DocumentType.COL) {
+        if (parent?.Document.type === DocumentType.COL) {
             let importContainer: Doc;
             if (docs.length < 50) {
                 importContainer = Docs.Create.MasonryDocument(docs, options);
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 560d6b30f..e51770c25 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -9,7 +9,7 @@ import { StrCast } from '../../fields/Types';
 export namespace SearchUtil {
     export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
 
-    export function SearchCollection(rootDoc: Opt<Doc>, query: string) {
+    export function SearchCollection(collectionDoc: Opt<Doc>, query: string) {
         const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
         const blockedKeys = [
             'x',
@@ -48,8 +48,8 @@ export namespace SearchUtil {
         query = query.toLowerCase();
 
         const results = new Map<Doc, string[]>();
-        if (rootDoc) {
-            const docs = DocListCast(rootDoc[Doc.LayoutFieldKey(rootDoc)]);
+        if (collectionDoc) {
+            const docs = DocListCast(collectionDoc[Doc.LayoutFieldKey(collectionDoc)]);
             const docIDs: String[] = [];
             SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
                 const dtype = StrCast(doc.type) as DocumentType;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index f7e6fa2dc..25f158f40 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -81,7 +81,7 @@ export namespace SelectionManager {
         return manager.SelectedSchemaDocument;
     }
     export function Docs(): Doc[] {
-        return manager.SelectedViews.map(dv => dv.rootDoc).filter(doc => doc?._type_collection !== CollectionViewType.Docking);
+        return manager.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking);
     }
 }
 ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 8d59426ec..34e294a4a 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.rootDoc);
+        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.rootDoc);
+        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);
 
@@ -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.rootDoc);
+        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/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 2765e95e6..04fb1fe80 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -429,7 +429,7 @@ export class DashboardView extends React.Component {
             isSystem: true,
             layout_explainer: 'All of the trails that you have created will appear here.',
         };
-        const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeView_ChildDoubleClick: 'openPresentation(documentView.rootDoc)' });
+        const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeView_ChildDoubleClick: 'openPresentation(documentView.Document)' });
         dashboardDoc.myTrails = new PrefetchProxy(myTrails);
 
         const contextMenuScripts = [reqdBtnScript.onClick];
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index bd82f7782..59d7e75da 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -229,7 +229,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             onPointerEnter={action(e => (this.subPin = allDocs ? 'All ' : ''))}
                             onPointerLeave={action(e => (this.subPin = ''))}
                             onClick={e => {
-                                this.props.views().forEach(dv => click(dv!.rootDoc));
+                                this.props.views().forEach(dv => click(dv!.Document));
                                 e.stopPropagation();
                             }}
                         />
@@ -356,7 +356,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                                 const docs = this.props
                                     .views()
                                     .filter(v => v)
-                                    .map(dv => dv!.rootDoc);
+                                    .map(dv => dv!.Document);
                                 TabDocView.PinDoc(docs, {
                                     pinAudioPlay: true,
                                     pinDocLayout: pinLayoutView,
@@ -379,7 +379,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         const docs = this.props
                             .views()
                             .filter(v => v)
-                            .map(dv => dv!.rootDoc);
+                            .map(dv => dv!.Document);
                         TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
                         e.stopPropagation();
                     }}>
@@ -560,9 +560,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     @action
     toggleTrail = (e: React.PointerEvent) => {
         const rootView = this.props.views()[0];
-        const rootDoc = rootView?.rootDoc;
-        if (rootDoc) {
-            const anchor = rootView.ComponentView?.getAnchor?.(true) ?? rootDoc;
+        const doc = rootView?.Document;
+        if (doc) {
+            const anchor = rootView.ComponentView?.getAnchor?.(true) ?? doc;
             const trail = DocCast(anchor.presentationTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
             if (trail !== anchor.presentationTrail) {
                 DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
@@ -589,9 +589,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                     <div style={{ position: 'absolute', zIndex: 1000 }}>
                         <LinkPopup
                             key="popup"
-                            linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
+                            linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.Document.onClick))}
                             linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
-                            linkFrom={() => this.props.views().lastElement()?.rootDoc}
+                            linkFrom={() => this.props.views().lastElement()?.Document}
                         />
                     </div>
                 ) : (
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4ede2e2bb..719055149 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -116,11 +116,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     SelectionManager.Views().forEach(d => {
                         if (titleFieldKey === 'title') {
                             d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-');
-                            if (StrCast(d.rootDoc.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
-                                Doc.RemFromMyPublished(d.rootDoc);
+                            if (StrCast(d.Document.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
+                                Doc.RemFromMyPublished(d.Document);
                             }
-                            if (!StrCast(d.rootDoc.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) {
-                                Doc.AddToMyPublished(d.rootDoc);
+                            if (!StrCast(d.Document.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) {
+                                Doc.AddToMyPublished(d.Document);
                             }
                         }
                         //@ts-ignore
@@ -128,20 +128,20 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
                         if (titleField.toString().startsWith('<this>')) {
                             const title = titleField.toString().replace(/<this>\.?/, '');
-                            const curKey = Doc.LayoutFieldKey(d.rootDoc);
+                            const curKey = Doc.LayoutFieldKey(d.Document);
                             if (curKey !== title) {
                                 if (title) {
                                     if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') {
-                                        d.rootDoc.layout_fieldKey = `layout_${title}`;
-                                        d.rootDoc[`layout_${title}`] = FormattedTextBox.LayoutString(title);
-                                        d.rootDoc[`${title}_nativeWidth`] = d.rootDoc[`${title}_nativeHeight`] = 0;
+                                        d.Document.layout_fieldKey = `layout_${title}`;
+                                        d.Document[`layout_${title}`] = FormattedTextBox.LayoutString(title);
+                                        d.Document[`${title}_nativeWidth`] = d.Document[`${title}_nativeHeight`] = 0;
                                     }
                                 } else {
-                                    d.rootDoc.layout_fieldKey = undefined;
+                                    d.Document.layout_fieldKey = undefined;
                                 }
                             }
                         } else {
-                            Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
+                            Doc.SetInPlace(d.Document, titleFieldKey, titleField, true);
                         }
                     }),
                 'edit title'
@@ -157,7 +157,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     };
 
     onContainerDown = (e: React.PointerEvent) => {
-        const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc);
+        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();
@@ -165,7 +165,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     };
 
     onTitleDown = (e: React.PointerEvent) => {
-        const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc);
+        const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].Document);
         if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
@@ -189,12 +189,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     @action
     onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
         const dragDocView = SelectionManager.Views()[0];
-        const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.rootDoc);
+        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.rootDoc.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(
@@ -256,7 +256,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     };
 
     onMaximizeDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views().lastElement().rootDoc]) ?? 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 => {
@@ -264,19 +264,19 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         if (selectedDocs.length) {
             if (e.ctrlKey) {
                 // open an embedding in a new tab with Ctrl Key
-                CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].rootDoc), OpenWhereMod.right);
+                CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].Document), OpenWhereMod.right);
             } else if (e.shiftKey) {
                 // open centered in a new workspace with Shift Key
-                const embedding = Doc.MakeEmbedding(selectedDocs[0].rootDoc);
+                const embedding = Doc.MakeEmbedding(selectedDocs[0].Document);
                 embedding.embedContainer = undefined;
                 embedding.x = -NumCast(embedding._width) / 2;
                 embedding.y = -NumCast(embedding._height) / 2;
                 CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right);
             } else if (e.altKey) {
                 // open same document in new tab
-                CollectionDockingView.ToggleSplit(selectedDocs[0].rootDoc, OpenWhereMod.right);
+                CollectionDockingView.ToggleSplit(selectedDocs[0].Document, OpenWhereMod.right);
             } else {
-                var openDoc = selectedDocs[0].rootDoc;
+                var openDoc = selectedDocs[0].Document;
                 if (openDoc.layout_fieldKey === 'layout_icon') {
                     openDoc = DocListCast(openDoc.proto_embeddings).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc);
                     Doc.deiconifyView(openDoc);
@@ -284,7 +284,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 LightboxView.Instance.SetLightboxDoc(
                     openDoc,
                     undefined,
-                    selectedDocs.slice(1).map(view => view.rootDoc)
+                    selectedDocs.slice(1).map(view => view.Document)
                 );
             }
         }
@@ -346,9 +346,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => {
         const newloccentern = seldocview.props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]);
         const newlocenter = [newloccentern[0] - NumCast(seldocview.layoutDoc._width) / 2, newloccentern[1] - NumCast(seldocview.layoutDoc._height) / 2];
-        const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI);
-        seldocview.rootDoc.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width);
-        seldocview.rootDoc.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height);
+        const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.Document._rotation) / 180) * Math.PI);
+        seldocview.Document.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width);
+        seldocview.Document.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height);
     };
 
     @action
@@ -361,7 +361,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             (e: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events
                 this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean, 
             action(e => (this._isRotating = false)), // upEvent
-            action(e => (seldocview.rootDoc.rotation_centerX = seldocview.rootDoc.rotation_centerY = 0))
+            action(e => (seldocview.Document.rotation_centerX = seldocview.Document.rotation_centerY = 0))
         ); // prettier-ignore
     };
 
@@ -375,22 +375,22 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         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 accumRot = (NumCast(dv.rootDoc._rotation) / 180) * Math.PI;
+            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.rootDoc.width) / 2, localRotCtr[1] - NumCast(dv.rootDoc.height) / 2];
+            const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2];
             const startRotCtr = Utils.rotPt(localRotCtrOffset[0], localRotCtrOffset[1], -accumRot);
-            const unrotatedDocPos = { x: NumCast(dv.rootDoc.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.rootDoc.y) + localRotCtrOffset[1] - startRotCtr.y };
-            infos.set(dv.rootDoc, { unrotatedDocPos, startRotCtr, accumRot });
+            const unrotatedDocPos = { x: NumCast(dv.Document.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.Document.y) + localRotCtrOffset[1] - startRotCtr.y };
+            infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot });
         });
         const infoRot = (angle: number, isAbs = false) => {
             SelectionManager.Views().forEach(
                 action(dv => {
-                    const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.rootDoc)!;
+                    const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.Document)!;
                     const endRotCtr = Utils.rotPt(startRotCtr.x, startRotCtr.y, isAbs ? angle : accumRot + angle);
-                    infos.set(dv.rootDoc, { unrotatedDocPos, startRotCtr, accumRot: isAbs ? angle : accumRot + angle });
-                    dv.rootDoc.x = infos.get(dv.rootDoc)!.unrotatedDocPos.x - (endRotCtr.x - startRotCtr.x);
-                    dv.rootDoc.y = infos.get(dv.rootDoc)!.unrotatedDocPos.y - (endRotCtr.y - startRotCtr.y);
-                    dv.rootDoc._rotation = ((isAbs ? 0 : NumCast(dv.rootDoc._rotation)) + (angle * 180) / Math.PI) % 360; // Rotation between -360 and 360
+                    infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot: isAbs ? angle : accumRot + angle });
+                    dv.Document.x = infos.get(dv.Document)!.unrotatedDocPos.x - (endRotCtr.x - startRotCtr.x);
+                    dv.Document.y = infos.get(dv.Document)!.unrotatedDocPos.y - (endRotCtr.y - startRotCtr.y);
+                    dv.Document._rotation = ((isAbs ? 0 : NumCast(dv.Document._rotation)) + (angle * 180) / Math.PI) % 360; // Rotation between -360 and 360
                 })
             );
         };
@@ -410,7 +410,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 return false;
             }, // moveEvent
             action(() => {
-                const oldRotation = NumCast(seldocview.rootDoc._rotation);
+                const oldRotation = NumCast(seldocview.Document._rotation);
                 const diff = oldRotation - Math.round(oldRotation / 45) * 45;
                 if (Math.abs(diff) < 5) {
                     if (selectedInk.length) {
@@ -459,7 +459,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     };
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
         const first = SelectionManager.Views()[0];
-        const effectiveAcl = GetEffectiveAcl(first.rootDoc);
+        const effectiveAcl = GetEffectiveAcl(first.Document);
         if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false;
         if (!first) return false;
         var fixedAspect = Doc.NativeAspect(first.layoutDoc);
@@ -514,7 +514,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     // resize a single DocumentView about the specified reference point, possibly setting/updating the native dimensions of the Doc
     //
     resizeView = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; ctrlKey: boolean }) => {
-        const doc = docView.rootDoc;
+        const doc = docView.Document;
         if (doc.isGroup) {
             DocListCast(doc.data)
                 .map(member => DocumentManager.Instance.getDocumentView(member, docView)!)
@@ -615,7 +615,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 return selected.ComponentView.getTitle();
             }
             if (this._titleControlString.startsWith('=')) {
-                return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || '';
+                return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.Document, this: selected.layoutDoc }, console.log).result?.toString() || '';
             }
             if (this._titleControlString.startsWith('#')) {
                 return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || '-unset-';
@@ -645,24 +645,24 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
 
         // sharing
-        const acl = GetEffectiveAcl(!this._showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc);
+        const acl = GetEffectiveAcl(!this._showLayoutAcl ? Doc.GetProto(seldocview.Document) : seldocview.Document);
         const docShareMode = HierarchyMapping.get(acl)!.name;
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image;
 
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
-        const hideDecorations = SnappingManager.GetIsResizing() || seldocview.props.hideDecorations || seldocview.rootDoc.layout_hideDecorations;
+        const hideDecorations = SnappingManager.GetIsResizing() || seldocview.props.hideDecorations || seldocview.Document.layout_hideDecorations;
         const hideResizers =
-            ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
-        const hideTitle = this._showNothing || hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
-        const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating;
+            ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.Document)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.Document.layout_hideResizeHandles || this._isRounding || this._isRotating;
+        const hideTitle = this._showNothing || hideDecorations || seldocview.props.hideDecorationTitle || seldocview.Document.layout_hideDecorationTitle || this._isRounding || this._isRotating;
+        const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.Document.layout_hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
         const hideOpenButton =
             this._showNothing ||
             hideDecorations ||
             seldocview.props.hideOpenButton ||
-            seldocview.rootDoc.layout_hideOpenButton ||
-            SelectionManager.Views().some(docView => docView.rootDoc._dragOnlyWithinContainer || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) ||
+            seldocview.Document.layout_hideOpenButton ||
+            SelectionManager.Views().some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) ||
             this._isRounding ||
             this._isRotating;
         const hideDeleteButton =
@@ -671,10 +671,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             this._isRounding ||
             this._isRotating ||
             seldocview.props.hideDeleteButton ||
-            seldocview.rootDoc.hideDeleteButton ||
+            seldocview.Document.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
                 const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().dataDoc) : AclEdit;
-                return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin;
+                return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin;
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
@@ -686,13 +686,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         const bounds = this.ClippedBounds;
         const useLock = bounds.r - bounds.x > 135 && seldocview.CollectionFreeFormDocumentView;
-        const useRotation = !hideResizers && seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate?
+        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;
 
         // Radius constants
         const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
-        const borderRadius = numberValue(Cast(seldocview.rootDoc.layout_borderRounding, 'string', null));
-        const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2);
+        const borderRadius = numberValue(Cast(seldocview.Document.layout_borderRounding, 'string', null));
+        const docMax = Math.min(NumCast(seldocview.Document.width) / 2, NumCast(seldocview.Document.height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
         const radiusHandle = (borderRadius / docMax) * maxDist;
         const radiusHandleLocation = Math.min(radiusHandle, maxDist);
@@ -740,7 +740,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 {sharingMenu}
                 {!useLock ? null : (
                     <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top">
-                        <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}>
+                        <div className="documentDecorations-lock" style={{ color: seldocview.Document._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}>
                             <FontAwesomeIcon size="sm" icon="lock" />
                         </div>
                     </Tooltip>
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index cb5c9b085..1deca401d 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -21,7 +21,7 @@ import { List } from '../../fields/List';
 import { emptyFunction } from '../../Utils';
 
 interface filterProps {
-    rootDoc: Doc;
+    Document: Doc;
 }
 
 @observer
@@ -34,7 +34,7 @@ export class FilterPanel extends React.Component<filterProps> {
      * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
      */
     @computed get targetDoc() {
-        return this.props.rootDoc;
+        return this.props.Document;
     }
     @computed get targetDocChildKey() {
         const targetView = DocumentManager.Instance.getFirstDocumentView(this.targetDoc);
@@ -113,7 +113,7 @@ export class FilterPanel extends React.Component<filterProps> {
     // }
 
     gatherFieldValues(childDocs: Doc[], facetKey: string) {
-        const valueSet = new Set<string>(StrListCast(this.props.rootDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1]));
+        const valueSet = new Set<string>(StrListCast(this.props.Document.childFilters).map(filter => filter.split(Doc.FilterSep)[1]));
         let rtFields = 0;
         let subDocs = childDocs;
         if (subDocs.length > 0) {
@@ -224,7 +224,7 @@ export class FilterPanel extends React.Component<filterProps> {
     facetValues = (facetHeader: string) => {
         const allCollectionDocs = new Set<Doc>();
         SearchUtil.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
-        const set = new Set<string>([...StrListCast(this.props.rootDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]);
+        const set = new Set<string>([...StrListCast(this.props.Document.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]);
         if (facetHeader === 'tags')
             allCollectionDocs.forEach(child =>
                 StrListCast(child[facetHeader])
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 09be95542..508339efc 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -386,7 +386,7 @@ export class GestureOverlay extends React.Component<React.PropsWithChildren<Gest
 
     @computed get elements() {
         const selView = GestureOverlay.DownDocView;
-        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._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;
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 62165bc48..9efa7baac 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -49,7 +49,7 @@ export class InkStrokeProperties {
         (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach(
             action(inkView => {
                 if (!requireCurrPoint || this._currentPoint !== -1) {
-                    const doc = inkView.rootDoc;
+                    const doc = inkView.Document;
                     if (doc.type === DocumentType.INK && doc.width && doc.height) {
                         const ink = Cast(doc.stroke, InkField)?.inkData;
                         if (ink) {
@@ -86,7 +86,7 @@ export class InkStrokeProperties {
     @action
     addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number; Y: number }[]) => {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
-            const doc = view.rootDoc;
+            const doc = view.Document;
             const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]];
             const newsegs = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).split(t);
             const splicepts = [...newsegs.left.points, ...newsegs.right.points];
@@ -159,7 +159,7 @@ export class InkStrokeProperties {
         this.applyFunction(
             inkView,
             (view: DocumentView, ink: InkData) => {
-                const doc = view.rootDoc;
+                const doc = view.Document;
                 const newPoints = ink.slice();
                 const brokenIndices = NumListCast(doc.brokenInkIndices);
                 if (preserve || this._currentPoint === 0 || this._currentPoint === ink.length - 1 || brokenIndices.includes(this._currentPoint)) {
@@ -335,7 +335,7 @@ export class InkStrokeProperties {
      * Handles the movement/scaling of a control point.
      */
     snapControl = (inkView: DocumentView, controlIndex: number) => {
-        const inkDoc = inkView.rootDoc;
+        const inkDoc = inkView.Document;
         const ink = Cast(inkDoc[Doc.LayoutFieldKey(inkDoc)], InkField)?.inkData;
 
         if (ink) {
@@ -378,9 +378,9 @@ export class InkStrokeProperties {
             .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
                 const testInkView = DocumentManager.Instance.getDocumentView(doc, containingDocView);
-                const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.rootDoc ? this.excludeSelfSnapSegs(ink, controlIndex) : []);
+                const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.Document ? this.excludeSelfSnapSegs(ink, controlIndex) : []);
                 if (snapped && snapped.distance < snapData.distance) {
-                    const snappedInkPt = doc === inkView.rootDoc ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space
+                    const snappedInkPt = doc === inkView.Document ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space
 
                     if (snappedInkPt) {
                         snapData = { nearestPt: snappedInkPt, distance: snapped.distance };
@@ -397,7 +397,7 @@ export class InkStrokeProperties {
      */
     snapHandleTangent = (inkView: DocumentView, controlIndex: number, handleIndexA: number, handleIndexB: number) => {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
-            const doc = view.rootDoc;
+            const doc = view.Document;
             const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number'), []);
             const ind = brokenIndices.findIndex(value => value === controlIndex);
             if (ind !== -1) {
@@ -459,7 +459,7 @@ export class InkStrokeProperties {
     @action
     moveTangentHandle = (inkView: DocumentView, deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) =>
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
-            const doc = view.rootDoc;
+            const doc = view.Document;
             const closed = InkingStroke.IsClosed(ink);
             const oldHandlePoint = ink[handleIndex];
             const oppositeHandlePoint = ink[oppositeHandleIndex];
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 8b8838464..b9b7e3f0b 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -61,7 +61,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         const savedAnnoMap = savedAnnotations?.values() && Array.from(savedAnnotations?.values()).length ? savedAnnotations : this.props.savedAnnotations();
         if (savedAnnoMap.size === 0) return undefined;
         const savedAnnos = Array.from(savedAnnoMap.values())[0];
-        const doc = this.props.docView().rootDoc;
+        const doc = this.props.docView().Document;
         const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1);
         if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) {
             const anno = savedAnnos[0];
@@ -213,7 +213,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                       dragComplete: e => {
                           if (!e.aborted && e.linkDocument) {
                               Doc.GetProto(e.linkDocument).link_relationship = 'cropped image';
-                              Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView().rootDoc.title;
+                              Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView().Document.title;
                               Doc.GetProto(e.linkDocument).link_displayLine = false;
                           }
                       },
@@ -242,7 +242,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             // configure and show the annotation/link menu if a the drag region is big enough
             // copy the temporary marquee to allow for multiple selections (not currently available though).
             const copy = document.createElement('div');
-            const scale = (this.props.scaling?.() || 1) * NumCast(this.props.docView().rootDoc._freeform_scale, 1);
+            const scale = (this.props.scaling?.() || 1) * NumCast(this.props.docView().Document._freeform_scale, 1);
             ['border', 'opacity', 'top', 'left', 'width', 'height'].forEach(prop => (copy.style[prop as any] = marqueeStyle[prop as any]));
             copy.className = 'marqueeAnnotator-annotationBox';
             copy.style.top = parseInt(marqueeStyle.top.toString().replace('px', '')) / scale + this.props.scrollTop + 'px';
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 84b1bf038..7a8a0bfb9 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -40,7 +40,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @observable public static Instance: PropertiesButtons;
 
     @computed get selectedDoc() {
-        return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.rootDoc;
+        return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.Document;
     }
     @computed get selectedLayoutDoc() {
         return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.layoutDoc;
@@ -65,7 +65,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 toggleType={ToggleType.BUTTON}
                 onClick={undoable(() => {
                     if (SelectionManager.Views().length > 1) {
-                        SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property));
+                        SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.Document, property));
                     } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
                 }, property)}
             />
@@ -83,7 +83,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             on => 'window-restore',
             onClick => {
                 SelectionManager.Views().forEach(dv => {
-                    const containerDoc = dv.rootDoc;
+                    const containerDoc = dv.Document;
                     //containerDoc.followAllLinks =
                     // containerDoc.noShadow =
                     // containerDoc.layout_disableBrushing =
@@ -92,7 +92,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     containerDoc._isLightbox = !containerDoc._isLightbox;
                     //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
                     const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
-                    //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
+                    //dv.Docuemnt.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
                     containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.link_displayLine = false)));
                 });
             }
@@ -106,7 +106,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             on => 'Switch between title styles',
             on => (on ? <MdSubtitlesOff /> : <MdSubtitles />), // {currentIcon}, //(on ?  <MdSubtitles/> :)  , //,'text-width', on ?  <MdSubtitles/> : <MdSubtitlesOff/>,
             (dv, doc) => {
-                const tdoc = dv?.rootDoc || doc;
+                const tdoc = dv?.Document || doc;
                 const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : '';
                 tdoc._layout_showTitle = newtitle ? newtitle : undefined;
             }
@@ -201,7 +201,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     //         on => 'window-restore',
     //         onClick => {
     //             SelectionManager.Views().forEach(dv => {
-    //                 const containerDoc = dv.rootDoc;
+    //                 const containerDoc = dv.Document;
     //                 //containerDoc.followAllLinks =
     //                 // containerDoc.noShadow =
     //                 // containerDoc.disableDocBrushing =
@@ -210,7 +210,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     //                 containerDoc._isLightbox = !containerDoc._isLightbox;
     //                 //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
     //                 const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
-    //                 //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
+    //                 //dv.Document.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
     //                 containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false)));
     //             });
     //         }
@@ -236,7 +236,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             '_layout_showCaption',
             on => `${on ? 'Hide' : 'Show'} caption footer`,
             on => (on ? <MdClosedCaptionDisabled /> : <MdClosedCaption />), //'closed-captioning',
-            (dv, doc) => ((dv?.rootDoc || doc)._layout_showCaption = (dv?.rootDoc || doc)._layout_showCaption === undefined ? 'caption' : undefined)
+            (dv, doc) => ((dv?.Document || doc)._layout_showCaption = (dv?.Document || doc)._layout_showCaption === undefined ? 'caption' : undefined)
         );
     }
 
@@ -247,7 +247,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             '_chromeHidden',
             on => `${on ? 'Show' : 'Hide'} editing UI`,
             on => (on ? <TbEditCircle /> : <TbEditCircleOff />), // 'edit',
-            (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden)
+            (dv, doc) => ((dv?.Document || doc)._chromeHidden = !(dv?.Document || doc)._chromeHidden)
         );
     }
 
@@ -443,7 +443,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
 
     @undoBatch
     editOnClickScript = () => {
-        if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, 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/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index d157e7b1c..196250167 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -21,7 +21,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
     @computed get _docs() {
         if (!this.props.DocView) return [];
         const target = this.props.DocView.props.Document;
-        const targetContext = this.props.DocView.props.docViewPath().lastElement()?.rootDoc;
+        const targetContext = this.props.DocView.props.docViewPath().lastElement()?.Document;
         const embeddings = DocListCast(target.proto_embeddings);
         const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings));
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5c7af65ee..cde7eeb08 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -449,7 +449,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.rootDoc);
+        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;
@@ -565,7 +565,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @computed get editableTitle() {
         const titles = new Set<string>();
-        SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.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>
@@ -622,7 +622,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @action
     setTitle = (value: string | number) => {
         if (SelectionManager.Views().length > 1) {
-            SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true));
+            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);
@@ -1172,7 +1172,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return (
             <PropertiesSection title="Filters" isOpen={this.openFilters} setIsOpen={bool => (this.openFilters = bool)} onDoubleClick={() => this.CloseAll()}>
                 <div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
-                    <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
+                    <FilterPanel Document={this.selectedDoc ?? Doc.ActiveDashboard!} />
                 </div>
             </PropertiesSection>
         );
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 4a8a62277..fa2768d79 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -249,7 +249,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 default:
                     return doc.z
                         ? `#9c9396  ${StrCast(doc?.layout_boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
-                        : props?.docViewPath().lastElement()?.rootDoc._freeform_useClusters
+                        : props?.docViewPath().lastElement()?.Document._freeform_useClusters
                         ? `${backgroundCol()} ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : NumCast(doc.group, -1) !== -1
                         ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
@@ -270,7 +270,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
         case StyleProp.Decorations:
             const lock = () => {
-                if (props?.docViewPath().lastElement()?.rootDoc?._type_collection === CollectionViewType.Freeform) {
+                if (props?.docViewPath().lastElement()?.Document?._type_collection === CollectionViewType.Freeform) {
                     return doc?.pointerEvents !== 'none' ? null : (
                         <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                             <FontAwesomeIcon icon='lock' size="lg" />
@@ -315,9 +315,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                             color={SettingsManager.userColor}
                             background={showFilterIcon}
                             items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[]), ...(props?.DocumentView?[props?.DocumentView?.()]:[])]
-                                    .filter(dv => StrListCast(dv.rootDoc.childFilters).length || StrListCast(dv.rootDoc.childRangeFilters).length)
+                                    .filter(dv => StrListCast(dv.Document.childFilters).length || StrListCast(dv.Document.childRangeFilters).length)
                                     .map(dv => ({
-                                text: StrCast(dv.rootDoc.title),
+                                text: StrCast(dv.Document.title),
                                 val: dv as any,
                                 style: {color:SettingsManager.userColor, background:SettingsManager.userBackgroundColor},
                             } as IListItemProps)) }
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index edf9f1f2c..01915f2f7 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -407,9 +407,9 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.ctrlKey || docView.rootDoc._createDocOnCR) && ['Enter'].includes(e.key)) {
+        if (docView && (e.ctrlKey || docView.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
             e.stopPropagation?.();
-            const newDoc = Doc.MakeCopy(docView.rootDoc, true);
+            const newDoc = Doc.MakeCopy(docView.Document, true);
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
             return this.addDocument?.(newDoc);
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 582baa7b5..c1333c90e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -285,11 +285,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== 'Tab';
             const layout_fieldKey = StrCast(docView.LayoutFieldKey);
-            const newDoc = Doc.MakeCopy(docView.rootDoc, true);
-            const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
+            const newDoc = Doc.MakeCopy(docView.Document, true);
+            const dataField = docView.Document[Doc.LayoutFieldKey(newDoc)];
             newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
-                newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
+            if (layout_fieldKey !== 'layout' && docView.Document[layout_fieldKey] instanceof Doc) {
+                newDoc[layout_fieldKey] = docView.Document[layout_fieldKey];
             }
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 9dcd09b17..43653ed75 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -104,8 +104,8 @@ export function CollectionSubView<X>(moreProps?: X) {
                 // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
                 // For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection.
                 // Since the data field is actually an image, we set the list of documents to the singleton of root document's proto which will be an image.
-                const rootDoc = Cast(this.props.Document.rootDocument, Doc, null);
-                rawdocs = rootDoc && !this.props.isAnnotationOverlay ? [Doc.GetProto(rootDoc)] : [];
+                const rootDocument = Cast(this.props.Document.rootDocument, Doc, null);
+                rawdocs = rootDocument && !this.props.isAnnotationOverlay ? [Doc.GetProto(rootDocument)] : [];
             }
 
             const childDocs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && (this.props.ignoreUnrendered || !d.layout_unrendered)).map(d => d as Doc);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index dc2ee63f5..898b453f4 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -257,7 +257,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             <div
                 className="collectionView"
                 onContextMenu={this.onContextMenu}
-                style={{ pointerEvents: this.props.DocumentView?.()?.props.docViewPath().lastElement()?.rootDoc?._type_collection === CollectionViewType.Freeform && this.layoutDoc._lockedPosition ? 'none' : undefined }}>
+                style={{ pointerEvents: this.props.DocumentView?.()?.props.docViewPath().lastElement()?.Document?._type_collection === CollectionViewType.Freeform && this.layoutDoc._lockedPosition ? 'none' : undefined }}>
                 {this.renderSubView(this.collectionViewType, props)}
             </div>
         );
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 6f8476f10..a5cf89217 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -378,9 +378,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             case undefined:
             case OpenWhere.lightbox:   if (this.layoutDoc?._isLightbox) {
                                             const lightboxView = !doc.annotationOn && DocCast(doc.embedContainer) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.embedContainer)) : undefined;
-                                            const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)];
+                                            const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.Document)];
                                             if (lightboxView && (!data || data instanceof List)) {
-                                                lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List<Doc>([doc]);
+                                                lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.Document)] = new List<Doc>([doc]);
                                                 return true;
                                             }
                                        }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ee5f9fd95..6cd111ac4 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -215,7 +215,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             this.treeViewOpen = !this.treeViewOpen;
         } else {
             // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns,  (2) has no context field ... otherwise make a new embedding
-            const bestEmbedding = docView.rootDoc.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) ? docView.rootDoc : Doc.BestEmbedding(docView.rootDoc);
+            const bestEmbedding = docView.Document.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) ? docView.Document : Doc.BestEmbedding(docView.Document);
             this.props.addDocTab(bestEmbedding, OpenWhere.lightbox);
         }
     };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index aca6df3c9..5e0cf1d23 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -37,11 +37,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         this._anchorDisposer = reaction(
             () => [
                 this.props.A.props.ScreenToLocalTransform(),
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
+                Cast(Cast(Cast(this.props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+                Cast(Cast(Cast(this.props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
                 this.props.B.props.ScreenToLocalTransform(),
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
+                Cast(Cast(Cast(this.props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+                Cast(Cast(Cast(this.props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
             ],
             action(() => {
                 this._start = Date.now();
@@ -223,8 +223,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
         const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
         const pt2normalized = [pt2norm[0] / pt2normlen, pt2norm[1] / pt2normlen];
-        const aActive = A.isSelected() || A.rootDoc[Brushed];
-        const bActive = B.isSelected() || B.rootDoc[Brushed];
+        const aActive = A.isSelected() || A.Document[Brushed];
+        const bActive = B.isSelected() || B.Document[Brushed];
 
         const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX);
         const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 307aa8882..fb29fb0d3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1790,7 +1790,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 brushedView={this.brushedView}
                 isAnnotationOverlay={this.isAnnotationOverlay}
                 transform={this.PanZoomCenterXf}
-                transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))}
+                transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.Document._viewTransition, 'string', null))}
                 viewDefDivClick={this.props.viewDefDivClick}>
                 {this.underlayViews}
                 {this.contentViews}
@@ -1931,7 +1931,7 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
     const browseTransitionTime = 500;
     SelectionManager.DeselectAll();
     dv &&
-        DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
+        DocumentManager.Instance.showDocument(dv.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
             if (!focused) {
                 const selfFfview = !dv.Document.isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
                 let containers = dv.props.docViewPath();
@@ -1960,19 +1960,19 @@ ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
 });
 ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
     SelectionManager.Views().forEach(view =>
-        view.props.pinToPres(view.rootDoc, {
-            currentFrame: Cast(view.rootDoc.currentFrame, 'number', null),
+        view.props.pinToPres(view.Document, {
+            currentFrame: Cast(view.Document.currentFrame, 'number', null),
             pinData: {
                 poslayoutview: pinContent,
                 dataview: pinContent,
             },
-            pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()),
+            pinViewport: MarqueeView.CurViewBounds(view.Document, view.props.PanelWidth(), view.props.PanelHeight()),
         })
     );
 });
 ScriptingGlobals.add(function bringToFront() {
-    SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc));
+    SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document));
 });
 ScriptingGlobals.add(function sendToBack(doc: Doc) {
-    SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc, true));
+    SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true));
 });
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 96eabd88c..a367e3e73 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -126,8 +126,8 @@ export class CollectionLinearView extends CollectionSubView() {
                     Currently playing:
                     {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => (
                         <>
-                            <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.rootDoc, { willZoomCentered: true })}>
-                                {clip.rootDoc.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? '  ' : ',')}
+                            <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.Document, { willZoomCentered: true })}>
+                                {clip.Document.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? '  ' : ',')}
                             </span>
                             <FontAwesomeIcon icon={!clip.ComponentView?.IsPlaying?.() ? 'play' : 'pause'} size="lg" onPointerDown={() => clip.ComponentView?.TogglePause?.()} />{' '}
                             <FontAwesomeIcon icon="times" size="lg" onPointerDown={() => clip.ComponentView?.Pause?.()} />{' '}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 22b80e21d..0b292a445 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -416,8 +416,8 @@ export class CollectionSchemaView extends CollectionSubView() {
     @action
     clearSelection = () => SelectionManager.DeselectAll();
 
-    selectRows = (rootDoc: Doc, lastSelected: Doc) => {
-        const index = this.rowIndex(rootDoc);
+    selectRows = (doc: Doc, lastSelected: Doc) => {
+        const index = this.rowIndex(doc);
         const lastSelectedRow = this.rowIndex(lastSelected);
         const startRow = Math.min(lastSelectedRow, index);
         const endRow = Math.max(lastSelectedRow, index);
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index f2ab4ff4b..0ad76db35 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -41,22 +41,22 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
     } else if (selectedViews.length) {
         if (checkResult) {
             const selView = selectedViews.lastElement();
-            const fieldKey = selView.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
-            const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
-            const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
-            return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber)[fieldKey] ?? 'transparent';
+            const fieldKey = selView.Document.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
+            const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const contentFrameNumber = Cast(selView.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+            return CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey] ?? 'transparent';
         }
         selectedViews.some(dv => dv.ComponentView instanceof InkingStroke) && SetActiveFillColor(color ?? 'transparent');
         selectedViews.forEach(dv => {
-            const fieldKey = dv.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
-            const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
-            const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+            const fieldKey = dv.Document.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
+            const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const contentFrameNumber = Cast(dv.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
             if (contentFrameNumber !== undefined) {
                 const obj: { [key: string]: Opt<string> } = {};
                 obj[fieldKey] = color;
-                CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, obj);
+                CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.Document, obj);
             } else {
-                dv.rootDoc['_' + fieldKey] = color;
+                dv.Document['_' + fieldKey] = color;
             }
         });
     } else {
@@ -377,12 +377,12 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
  **/
 ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
     const selected = SelectionManager.Views().lastElement();
-    if (selected?.rootDoc.type === DocumentType.WEB) {
+    if (selected?.Document.type === DocumentType.WEB) {
         if (checkResult) {
-            return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href);
+            return StrCast(selected.Document.data, Cast(selected.Document.data, WebField, null)?.url?.href);
         }
         selected.ComponentView?.setData?.(url);
-        //selected.rootDoc.data = new WebField(url);
+        //selected.Document.data = new WebField(url);
     }
 });
 ScriptingGlobals.add(function webForward(checkResult?: boolean) {
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 9dc133e28..3082c632d 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -54,7 +54,7 @@ export class LinkMenu extends React.Component<Props> {
     };
 
     render() {
-        const sourceDoc = this.props.docView.rootDoc;
+        const sourceDoc = this.props.docView.Document;
         const sourceAnchor = this.props.docView.anchorViewDoc ?? sourceDoc;
         const style = this.props.style ?? (dv => ({ left: dv?.left || 0, top: this.props.docView.topMost ? undefined : (dv?.bottom || 0) + 15, bottom: this.props.docView.topMost ? 20 : undefined, maxWidth: 200 }))(this.props.docView.getBounds());
 
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index c1a5a634c..5453ed07b 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -46,14 +46,14 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
         const groupItems = Array.from(set.keys()).map(linkDoc => {
             const sourceDoc =
                 this.props.docView.anchorViewDoc ??
-                (this.props.docView.rootDoc.type === DocumentType.LINK //
+                (this.props.docView.Document.type === DocumentType.LINK //
                     ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
                         ? DocCast(linkDoc.link_anchor_1)
                         : DocCast(linkDoc.link_anchor_2)
                     : this.props.sourceDoc);
             const destDoc = !sourceDoc
                 ? undefined
-                : this.props.docView.rootDoc.type === DocumentType.LINK
+                : this.props.docView.Document.type === DocumentType.LINK
                 ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
                     ? DocCast(linkDoc.link_anchor_2)
                     : DocCast(linkDoc.link_anchor_1)
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index bf2a4e1a9..611771fff 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -82,7 +82,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             },
             emptyFunction,
             action(() => {
-                const trail = DocCast(this.props.docView.rootDoc.presentationTrail);
+                const trail = DocCast(this.props.docView.Document.presentationTrail);
                 if (trail) {
                     Doc.ActivePresentation = trail;
                     DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight);
diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
index 96846673b..a085e4a66 100644
--- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
+++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
@@ -19,7 +19,7 @@ export const Recommendation = (props: IRecommendation) => {
                 if (source == 'Dash' && docId) {
                     const docView = DocumentManager.Instance.getDocumentViewsById(docId).lastElement();
                     if (docView) {
-                        doc = docView.rootDoc;
+                        doc = docView.Document;
                     }
                 } else if (data) {
                     switch (type) {
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 4c7ea4002..2a20d935b 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -60,7 +60,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }}>
                 <SketchPicker
                     onChange={c => Doc.ActiveTool === InkTool.None && ColorBox.switchColor(c)}
-                    color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())}
+                    color={StrCast(SelectionManager.Views()?.[0]?.Document?._backgroundColor, ActiveInkColor())}
                     presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
                 />
 
@@ -74,8 +74,8 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                             SetActiveInkWidth(e.target.value);
                             SelectionManager.Views()
-                                .filter(i => StrCast(i.rootDoc.type) === DocumentType.INK)
-                                .map(i => (i.rootDoc.stroke_width = Number(e.target.value)));
+                                .filter(i => StrCast(i.Document.type) === DocumentType.INK)
+                                .map(i => (i.Document.stroke_width = Number(e.target.value)));
                         }}
                     />
                 </div>
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index cc2c44435..a626772e4 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -112,7 +112,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @computed get renderVizView() {
         const scale = this.props.NativeDimScaling?.() || 1;
         const sharedProps = {
-            rootDoc: this.Document,
+            Document: this.Document,
             layoutDoc: this.layoutDoc,
             records: this.records,
             axes: this.axes,
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index e67e2bf31..a1bd316f0 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -10,14 +10,13 @@ import { List } from '../../../../../fields/List';
 import { listSpec } from '../../../../../fields/Schema';
 import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
 import { Docs } from '../../../../documents/Documents';
-import { LinkManager } from '../../../../util/LinkManager';
 import { undoable } from '../../../../util/UndoManager';
 import { PinProps, PresBox } from '../../trails';
 import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
 import './Chart.scss';
 
 export interface HistogramProps {
-    rootDoc: Doc;
+    Document: Doc;
     layoutDoc: Doc;
     axes: string[];
     records: { [key: string]: any }[];
@@ -83,9 +82,9 @@ export class Histogram extends React.Component<HistogramProps> {
     }
 
     @computed get parentViz() {
-        return DocCast(this.props.rootDoc.dataViz_parentViz);
-        // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
-        //     .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+        return DocCast(this.props.Document.dataViz_parentViz);
+        // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links
+        //     .filter(link => link.link_anchor_1 == this.props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link
         //     .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
 
@@ -115,7 +114,7 @@ export class Histogram extends React.Component<HistogramProps> {
         const anchor = Docs.Create.ConfigDocument({
             title: 'histogram doc selection' + this._currSelected,
         });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.Document);
         return anchor;
     };
 
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index ad34a0c99..7e0391879 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -64,9 +64,9 @@ export class LineChart extends React.Component<LineChartProps> {
     }
     @computed get parentViz() {
         return DocCast(this.props.Document.dataViz_parentViz);
-        // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
+        // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links
         //     .filter(link => {
-        //         return link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz;
+        //         return link.link_anchor_1 == this.props.Document.dataViz_parentViz;
         //     }) // get links where this chart doc is the target of the link
         //     .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 7303eb184..4dbd67485 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -15,7 +15,7 @@ import './Chart.scss';
 import { Checkbox } from '@material-ui/core';
 
 export interface PieChartProps {
-    rootDoc: Doc;
+    Document: Doc;
     layoutDoc: Doc;
     axes: string[];
     records: { [key: string]: any }[];
@@ -76,9 +76,9 @@ export class PieChart extends React.Component<PieChartProps> {
     }
 
     @computed get parentViz() {
-        return DocCast(this.props.rootDoc.dataViz_parentViz);
-        // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
-        //     .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+        return DocCast(this.props.Document.dataViz_parentViz);
+        // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links
+        //     .filter(link => link.link_anchor_1 == this.props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link
         //     .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
 
@@ -101,7 +101,7 @@ export class PieChart extends React.Component<PieChartProps> {
             //
             title: 'piechart doc selection' + this._currSelected,
         });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.Document);
         return anchor;
     };
 
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 147dfb182..9ac06cf3c 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -14,7 +14,7 @@ import { DATA_VIZ_TABLE_ROW_HEIGHT } from '../../../global/globalCssVariables.sc
 import './Chart.scss';
 
 interface TableBoxProps {
-    rootDoc: Doc;
+    Document: Doc;
     layoutDoc: Doc;
     records: { [key: string]: any }[];
     selectAxes: (axes: string[]) => void;
@@ -57,9 +57,9 @@ export class TableBox extends React.Component<TableBoxProps> {
     }
 
     @computed get parentViz() {
-        return DocCast(this.props.rootDoc.dataViz_parentViz);
-        // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
-        //     .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+        return DocCast(this.props.Document.dataViz_parentViz);
+        // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links
+        //     .filter(link => link.link_anchor_1 == this.props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link
         //     .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
 
@@ -119,12 +119,12 @@ export class TableBox extends React.Component<TableBoxProps> {
             e,
             e => {
                 // dragging off a column to create a brushed DataVizBox
-                const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
+                const sourceAnchorCreator = () => this.props.docView?.()!.Document!;
                 const targetCreator = (annotationOn: Doc | undefined) => {
-                    const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
+                    const embedding = Doc.MakeEmbedding(this.props.docView?.()!.Document!);
                     embedding._dataViz = DataVizView.TABLE;
                     embedding._dataViz_axes = new List<string>([col, col]);
-                    embedding._dataViz_parentViz = this.props.rootDoc;
+                    embedding._dataViz_parentViz = this.props.Document;
                     embedding.annotationOn = annotationOn;
                     embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
                     embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor;
@@ -138,7 +138,7 @@ export class TableBox extends React.Component<TableBoxProps> {
                                 e.linkDocument.link_displayLine = true;
                                 e.linkDocument.link_matchEmbeddings = true;
                                 e.linkDocument.link_displayArrow = true;
-                                // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
+                                // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.Document;
                                 // e.annoDragData.linkSourceDoc.followLinkZoom = false;
                             }
                         },
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index bccbd66e8..49592d993 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -33,7 +33,7 @@ export class DocumentIcon extends React.Component<{ view: DocumentView; index: n
                     background: SettingsManager.userBackgroundColor,
                     transform: `translate(${(left + right) / 2}px, ${top}px)`,
                 }}>
-                <Tooltip title={<>{this.props.view.rootDoc.title}</>}>
+                <Tooltip title={<>{this.props.view.Document.title}</>}>
                     <p>d{this.props.index}</p>
                 </Tooltip>
             </div>
@@ -59,7 +59,7 @@ export class DocumentIconContainer extends React.Component {
                                 const match = node.text.match(/d([0-9]+)/);
                                 if (match) {
                                     const m = parseInt(match[1]);
-                                    const doc = DocumentIcon.DocViews[m].rootDoc;
+                                    const doc = DocumentIcon.DocViews[m].Document;
                                     usedDocuments.add(m);
                                     return factory.createIdentifier(`idToDoc("${doc[Id]}")`);
                                 }
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 50a7f5d7b..ce3650749 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -69,7 +69,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             this.onLinkButtonMoved,
             emptyFunction,
             action((e, doubleTap) => {
-                doubleTap && DocumentView.showBackLinks(this.props.View.rootDoc);
+                doubleTap && DocumentView.showBackLinks(this.props.View.Document);
             }),
             undefined,
             undefined,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b6e90cbf4..81f1a4f03 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1385,11 +1385,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     };
     public setViewTransition = (transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) => {
         this.layoutDoc._viewTransition = `${transProp} ${timeInMs}ms`;
-        if (dataTrans) this.rootDoc._dataTransition = `${transProp} ${timeInMs}ms`;
+        if (dataTrans) this.Document._dataTransition = `${transProp} ${timeInMs}ms`;
         this.ViewTimer && clearTimeout(this.ViewTimer);
         return (this.ViewTimer = setTimeout(() => {
             this.layoutDoc._viewTransition = undefined;
-            this.rootDoc._dataTransition = 'inherit';
+            this.Document._dataTransition = 'inherit';
             afterTrans?.();
         }, timeInMs + 10));
     };
@@ -1427,7 +1427,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     get topMost() {
         return this.props.renderDepth === 0;
     }
-    get rootDoc() {
+    get Document() {
         return this.Document;
     }
     get dataDoc() {
@@ -1440,7 +1440,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.docView?._componentView;
     }
     get allLinks() {
-        return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.rootDoc);
+        return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document);
     }
     get LayoutFieldKey() {
         return this.docView?.LayoutFieldKey || 'layout';
diff --git a/src/client/views/nodes/FontIconBox/ButtonInterface.ts b/src/client/views/nodes/FontIconBox/ButtonInterface.ts
index 0aa2ac8e1..1c034bfbe 100644
--- a/src/client/views/nodes/FontIconBox/ButtonInterface.ts
+++ b/src/client/views/nodes/FontIconBox/ButtonInterface.ts
@@ -1,12 +1,12 @@
-import { Doc } from "../../../../fields/Doc";
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { ButtonType } from "./FontIconBox";
+import { Doc } from '../../../../fields/Doc';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { ButtonType } from './FontIconBox';
 
 export interface IButtonProps {
     type: string | ButtonType;
-    rootDoc: Doc;
+    Document: Doc;
     label: any;
     icon: IconProp;
     color: string;
     backgroundColor: string;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 30cd65cb4..743075c89 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -35,8 +35,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.CollectionFreeFormView) {
             const a_invXf = this.anchor1.props.ScreenToLocalTransform().inverse();
             const b_invXf = this.anchor2.props.ScreenToLocalTransform().inverse();
-            const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(this.anchor1.rootDoc._width), NumCast(this.anchor1.rootDoc._height)) };
-            const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(this.anchor2.rootDoc._width), NumCast(this.anchor2.rootDoc._height)) };
+            const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(this.anchor1.Document._width), NumCast(this.anchor1.Document._height)) };
+            const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(this.anchor2.Document._width), NumCast(this.anchor2.Document._height)) };
 
             const pts = [] as number[][];
             pts.push([(a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2]);
@@ -65,8 +65,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     const this_xf = parxf?.screenToLocalXf ?? Transform.Identity; //this.props.ScreenToLocalTransform();
                     const a_invXf = a.props.ScreenToLocalTransform().inverse();
                     const b_invXf = b.props.ScreenToLocalTransform().inverse();
-                    const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(a.rootDoc._width), NumCast(a.rootDoc._height)) };
-                    const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(b.rootDoc._width), NumCast(b.rootDoc._height)) };
+                    const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(a.Document._width), NumCast(a.Document._height)) };
+                    const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(b.Document._width), NumCast(b.Document._height)) };
                     const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) };
                     const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) };
 
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 8d3cd1d96..14a8a387a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -386,8 +386,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.selectedPin = pinDoc;
         this.bingSearchBarContents = pinDoc.map;
 
-        // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
-        // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
+        // Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'match');
+        // Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'match');
         Doc.setDocFilter(this.layoutDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
 
         this.recolorPin(this.selectedPin, 'green');
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 130ac40c8..a914084f9 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -147,7 +147,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     createPivotForField = (e: React.MouseEvent) => {
         let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement();
         if (container) {
-            const embedding = Doc.MakeEmbedding(container.rootDoc);
+            const embedding = Doc.MakeEmbedding(container.Document);
             embedding._type_collection = CollectionViewType.Time;
             const colHdrKey = '_' + container.LayoutFieldKey + '_columnHeaders';
             let list = Cast(embedding[colHdrKey], listSpec(SchemaHeaderField));
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f3929533d..8a927560d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1511,7 +1511,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             (this._editorView as any).TextView = this;
         }
 
-        const selectOnLoad = this.Document[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()));
+        const selectOnLoad = DocCast(this.Document.rootDocument, this.Document)[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()));
         if (this._editorView && selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
             const selLoadChar = FormattedTextBox.SelectOnLoadChar;
             FormattedTextBox.SelectOnLoad = '';
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 816a89489..791a1bfe7 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -239,7 +239,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                 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.rootDoc._text_fontSize) && activeSizes.add(StrCast(dv.rootDoc._text_fontSize)));
+            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) };
     }
@@ -359,7 +359,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                 this.view.focus();
             }
         } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) {
-            SelectionManager.Views().forEach(dv => (dv.rootDoc._text_fontSize = fontSize));
+            SelectionManager.Views().forEach(dv => (dv.Document._text_fontSize = fontSize));
         } else Doc.UserDoc().fontSize = fontSize;
         this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
-- 
cgit v1.2.3-70-g09d2


From be2569d8640f1693eb27f124ad3dd8062ada4837 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Dec 2023 00:41:05 -0500
Subject: more updates to mobx 6.  updated typescript to v5. updated pdf-dist

---
 package-lock.json                                  | 1521 ++------------------
 package.json                                       |    9 +-
 src/client/views/ContextMenu.tsx                   |    1 -
 src/client/views/DashboardView.tsx                 |    7 +-
 src/client/views/MainView.tsx                      |    7 +-
 src/client/views/PropertiesDocContextSelector.tsx  |   27 +-
 src/client/views/SidebarAnnos.tsx                  |   77 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |    2 +-
 .../collectionSchema/SchemaTableCell.tsx           |    6 +-
 src/client/views/linking/LinkMenu.tsx              |    4 +-
 src/client/views/linking/LinkMenuItem.tsx          |    6 +-
 src/client/views/nodes/DocumentView.tsx            |    2 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |    4 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   96 +-
 src/client/views/nodes/PDFBox.tsx                  |    3 +-
 src/client/views/nodes/WebBox.tsx                  |  154 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |   49 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |    4 +-
 .../formattedText/FormattedTextBoxComment.tsx      |    4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   32 +-
 src/server/ApiManagers/PDFManager.ts               |   50 +-
 src/server/DashUploadUtils.ts                      |   25 +-
 src/typings/index.d.ts                             |    1 +
 webpack.config.js                                  |   22 +-
 24 files changed, 477 insertions(+), 1636 deletions(-)

(limited to 'src/client/views/DashboardView.tsx')

diff --git a/package-lock.json b/package-lock.json
index 9afc22828..272851e13 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
                 "@azure/storage-blob": "^12.17.0",
                 "@babel/preset-env": "^7.23.5",
                 "@babel/preset-react": "^7.23.3",
+                "@bundled-es-modules/pdfjs-dist": "^3.6.172-alpha.1",
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
                 "@ffmpeg/core": "0.12.4",
@@ -35,7 +36,7 @@
                 "@types/google-maps": "^3.2.6",
                 "@types/reveal": "^4.2.0",
                 "@types/supercluster": "^7.1.3",
-                "@types/web": "0.0.125",
+                "@types/web": "^0.0.127",
                 "@webscopeio/react-textarea-autocomplete": "^4.9.2",
                 "adm-zip": "^0.5.10",
                 "archiver": "^6.0.1",
@@ -87,7 +88,7 @@
                 "flexlayout-react": "^0.7.15",
                 "fluent-ffmpeg": "^2.1.2",
                 "forever-agent": "^0.6.1",
-                "fork-ts-checker-webpack-plugin": "^1.6.0",
+                "fork-ts-checker-webpack-plugin": "^9.0.2",
                 "formidable": "3.5.1",
                 "function-plot": "^1.23.3",
                 "golden-layout": "^2.6.0",
@@ -135,7 +136,7 @@
                 "path-browserify": "^1.0.1",
                 "pdf-parse": "^1.1.1",
                 "pdfjs": "^2.4.7",
-                "pdfjs-dist": "^2.14.305",
+                "pdfjs-dist": "^4.0.269",
                 "probe-image-size": "^7.2.3",
                 "process": "^0.11.10",
                 "prosemirror-commands": "^1.5.2",
@@ -275,7 +276,7 @@
                 "ts-loader": "^9.5.1",
                 "ts-node": "^10.9.1",
                 "ts-node-dev": "^2.0.0",
-                "typescript": "^4.7.4",
+                "typescript": "^5.3.3",
                 "webpack-dev-server": "^4.15.1",
                 "webpack-hot-middleware": "^2.25.4"
             }
@@ -2245,6 +2246,11 @@
                 "node": ">=6.9.0"
             }
         },
+        "node_modules/@bundled-es-modules/pdfjs-dist": {
+            "version": "3.6.172-alpha.1",
+            "resolved": "https://registry.npmjs.org/@bundled-es-modules/pdfjs-dist/-/pdfjs-dist-3.6.172-alpha.1.tgz",
+            "integrity": "sha512-edQXn/5UR1LU1rPoFaZCBk22X8COSO4xctUjakkX6YtZ8BYT9J4EStU8X4bqX7ht2FLC3J21YMQ6RPDmut/l2g=="
+        },
         "node_modules/@cspotcode/source-map-support": {
             "version": "0.8.1",
             "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -5076,9 +5082,9 @@
             "dev": true
         },
         "node_modules/@types/web": {
-            "version": "0.0.125",
-            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.125.tgz",
-            "integrity": "sha512-TGZ4jAe1Nj4x+/wZICAZhAM8K3RIqti90ye0onlprDarBH0nLImnyZ2S729X+mzab3EMBu3KWd/KNwJbKHRV4g=="
+            "version": "0.0.127",
+            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.127.tgz",
+            "integrity": "sha512-5PR8ULDp5pYzBWke+VKihf2TkE2H5+RyyGJSDFAk+R1dWUKpwPoW/OiWv0q7DaLeJ/qvXPoKSuV331VsaEneew=="
         },
         "node_modules/@types/webidl-conversions": {
             "version": "7.0.3",
@@ -5776,30 +5782,6 @@
                 "dequal": "^2.0.3"
             }
         },
-        "node_modules/arr-diff": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-            "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/arr-flatten": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
-            "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/arr-union": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-            "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/array-back": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz",
@@ -5981,14 +5963,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/array-unique": {
-            "version": "0.3.2",
-            "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-            "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/array.prototype.findlastindex": {
             "version": "1.2.3",
             "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz",
@@ -6148,14 +6122,6 @@
                 "node": "*"
             }
         },
-        "node_modules/assign-symbols": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-            "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/ast-types-flow": {
             "version": "0.0.8",
             "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@@ -6176,17 +6142,6 @@
             "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
             "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
         },
-        "node_modules/async-each": {
-            "version": "1.0.6",
-            "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz",
-            "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==",
-            "funding": [
-                {
-                    "type": "individual",
-                    "url": "https://paulmillr.com/funding/"
-                }
-            ]
-        },
         "node_modules/asynciterator.prototype": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz",
@@ -6201,17 +6156,6 @@
             "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
             "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
         },
-        "node_modules/atob": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
-            "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
-            "bin": {
-                "atob": "bin/atob.js"
-            },
-            "engines": {
-                "node": ">= 4.5.0"
-            }
-        },
         "node_modules/available-typed-arrays": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
@@ -6293,71 +6237,6 @@
                 "babel-node": "lib/cli.js"
             }
         },
-        "node_modules/babel-code-frame": {
-            "version": "6.26.0",
-            "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
-            "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==",
-            "dependencies": {
-                "chalk": "^1.1.3",
-                "esutils": "^2.0.2",
-                "js-tokens": "^3.0.2"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/ansi-regex": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-            "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/ansi-styles": {
-            "version": "2.2.1",
-            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-            "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/chalk": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-            "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
-            "dependencies": {
-                "ansi-styles": "^2.2.1",
-                "escape-string-regexp": "^1.0.2",
-                "has-ansi": "^2.0.0",
-                "strip-ansi": "^3.0.0",
-                "supports-color": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/js-tokens": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-            "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg=="
-        },
-        "node_modules/babel-code-frame/node_modules/strip-ansi": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-            "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
-            "dependencies": {
-                "ansi-regex": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/supports-color": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-            "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
-            "engines": {
-                "node": ">=0.8.0"
-            }
-        },
         "node_modules/babel-eslint": {
             "version": "10.1.0",
             "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -6543,51 +6422,11 @@
             "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
             "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
         },
-        "node_modules/base": {
-            "version": "0.11.2",
-            "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
-            "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
-            "dependencies": {
-                "cache-base": "^1.0.1",
-                "class-utils": "^0.3.5",
-                "component-emitter": "^1.2.1",
-                "define-property": "^1.0.0",
-                "isobject": "^3.0.1",
-                "mixin-deep": "^1.2.0",
-                "pascalcase": "^0.1.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/base-64": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
             "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
         },
-        "node_modules/base/node_modules/define-property": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-            "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
-            "dependencies": {
-                "is-descriptor": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/base/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/Base64": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
@@ -6700,15 +6539,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/bindings": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-            "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-            "optional": true,
-            "dependencies": {
-                "file-uri-to-path": "1.0.0"
-            }
-        },
         "node_modules/bingmaps-react": {
             "version": "1.2.10",
             "resolved": "https://registry.npmjs.org/bingmaps-react/-/bingmaps-react-1.2.10.tgz",
@@ -9895,25 +9725,6 @@
                 "node": ">= 0.8"
             }
         },
-        "node_modules/cache-base": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
-            "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
-            "dependencies": {
-                "collection-visit": "^1.0.0",
-                "component-emitter": "^1.2.1",
-                "get-value": "^2.0.6",
-                "has-value": "^1.0.0",
-                "isobject": "^3.0.1",
-                "set-value": "^2.0.0",
-                "to-object-path": "^0.3.0",
-                "union-value": "^1.0.0",
-                "unset-value": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/cacheable-lookup": {
             "version": "7.0.0",
             "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
@@ -10229,20 +10040,6 @@
             "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
             "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw=="
         },
-        "node_modules/class-utils": {
-            "version": "0.3.6",
-            "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
-            "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
-            "dependencies": {
-                "arr-union": "^3.1.0",
-                "define-property": "^0.2.5",
-                "isobject": "^3.0.0",
-                "static-extend": "^0.1.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/classnames": {
             "version": "2.3.2",
             "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
@@ -10372,18 +10169,6 @@
                 "node": ">=6"
             }
         },
-        "node_modules/collection-visit": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-            "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
-            "dependencies": {
-                "map-visit": "^1.0.0",
-                "object-visit": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/color": {
             "version": "4.2.3",
             "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -10502,14 +10287,6 @@
             "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
             "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="
         },
-        "node_modules/component-emitter": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
-            "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        },
         "node_modules/compress-commons": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz",
@@ -10770,14 +10547,6 @@
                 "node": ">= 0.8"
             }
         },
-        "node_modules/copy-descriptor": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-            "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/core-js": {
             "version": "3.34.0",
             "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.34.0.tgz",
@@ -11884,17 +11653,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/define-property": {
-            "version": "0.2.5",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-            "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
-            "dependencies": {
-                "is-descriptor": "^0.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/delaunator": {
             "version": "5.0.0",
             "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
@@ -12374,12 +12132,6 @@
                 "url": "https://github.com/fb55/domhandler?sponsor=1"
             }
         },
-        "node_modules/dommatrix": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-1.0.3.tgz",
-            "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==",
-            "deprecated": "dommatrix is no longer maintained. Please use @thednp/dommatrix."
-        },
         "node_modules/domutils": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
@@ -14544,36 +14296,6 @@
             "resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz",
             "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw=="
         },
-        "node_modules/expand-brackets": {
-            "version": "2.1.4",
-            "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-            "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
-            "dependencies": {
-                "debug": "^2.3.3",
-                "define-property": "^0.2.5",
-                "extend-shallow": "^2.0.1",
-                "posix-character-classes": "^0.1.0",
-                "regex-not": "^1.0.0",
-                "snapdragon": "^0.8.1",
-                "to-regex": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/expand-brackets/node_modules/debug": {
-            "version": "2.6.9",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-            "dependencies": {
-                "ms": "2.0.0"
-            }
-        },
-        "node_modules/expand-brackets/node_modules/ms": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-            "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
-        },
         "node_modules/expand-tilde": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
@@ -14814,17 +14536,6 @@
             "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
             "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
         },
-        "node_modules/extend-shallow": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-            "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
-            "dependencies": {
-                "is-extendable": "^0.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/external-editor": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
@@ -14851,47 +14562,6 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/extglob": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-            "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-            "dependencies": {
-                "array-unique": "^0.3.2",
-                "define-property": "^1.0.0",
-                "expand-brackets": "^2.1.4",
-                "extend-shallow": "^2.0.1",
-                "fragment-cache": "^0.2.1",
-                "regex-not": "^1.0.0",
-                "snapdragon": "^0.8.1",
-                "to-regex": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/extglob/node_modules/define-property": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-            "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
-            "dependencies": {
-                "is-descriptor": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/extglob/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/extsprintf": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
@@ -15089,12 +14759,6 @@
                 "url": "https://github.com/sindresorhus/file-type?sponsor=1"
             }
         },
-        "node_modules/file-uri-to-path": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
-            "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
-            "optional": true
-        },
         "node_modules/fill-range": {
             "version": "7.0.1",
             "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -15410,14 +15074,6 @@
                 "for-each-property": "0.0.4"
             }
         },
-        "node_modules/for-in": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-            "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/foreground-child": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -15453,299 +15109,189 @@
             }
         },
         "node_modules/fork-ts-checker-webpack-plugin": {
-            "version": "1.6.0",
-            "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.6.0.tgz",
-            "integrity": "sha512-vqOY5gakcoon2s12V7MMe01OPwfgqulUWFzm+geQaPPOBKjW1I7aqqoBVlU0ECn97liMB0ECs16pRdIGe9qdRw==",
+            "version": "9.0.2",
+            "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.0.2.tgz",
+            "integrity": "sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==",
             "dependencies": {
-                "babel-code-frame": "^6.22.0",
-                "chalk": "^2.4.1",
-                "chokidar": "^2.0.4",
-                "micromatch": "^3.1.10",
+                "@babel/code-frame": "^7.16.7",
+                "chalk": "^4.1.2",
+                "chokidar": "^3.5.3",
+                "cosmiconfig": "^8.2.0",
+                "deepmerge": "^4.2.2",
+                "fs-extra": "^10.0.0",
+                "memfs": "^3.4.1",
                 "minimatch": "^3.0.4",
-                "semver": "^5.6.0",
-                "tapable": "^1.0.0",
-                "worker-rpc": "^0.1.0"
+                "node-abort-controller": "^3.0.1",
+                "schema-utils": "^3.1.1",
+                "semver": "^7.3.5",
+                "tapable": "^2.2.1"
             },
             "engines": {
-                "node": ">=6.11.5",
+                "node": ">=12.13.0",
                 "yarn": ">=1.0.0"
+            },
+            "peerDependencies": {
+                "typescript": ">3.6.0",
+                "webpack": "^5.11.0"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/anymatch": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-            "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-            "dependencies": {
-                "micromatch": "^3.1.4",
-                "normalize-path": "^2.1.1"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/anymatch/node_modules/normalize-path": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-            "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+            "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
             "dependencies": {
-                "remove-trailing-separator": "^1.0.1"
+                "color-convert": "^2.0.1"
             },
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/binary-extensions": {
-            "version": "1.13.1",
-            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
-            "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/argparse": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+            "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/braces": {
-            "version": "2.3.2",
-            "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-            "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+            "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
             "dependencies": {
-                "arr-flatten": "^1.1.0",
-                "array-unique": "^0.3.2",
-                "extend-shallow": "^2.0.1",
-                "fill-range": "^4.0.0",
-                "isobject": "^3.0.1",
-                "repeat-element": "^1.1.2",
-                "snapdragon": "^0.8.1",
-                "snapdragon-node": "^2.0.1",
-                "split-string": "^3.0.2",
-                "to-regex": "^3.0.1"
+                "ansi-styles": "^4.1.0",
+                "supports-color": "^7.1.0"
             },
             "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": {
-            "version": "2.1.8",
-            "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
-            "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
-            "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies",
-            "dependencies": {
-                "anymatch": "^2.0.0",
-                "async-each": "^1.0.1",
-                "braces": "^2.3.2",
-                "glob-parent": "^3.1.0",
-                "inherits": "^2.0.3",
-                "is-binary-path": "^1.0.0",
-                "is-glob": "^4.0.0",
-                "normalize-path": "^3.0.0",
-                "path-is-absolute": "^1.0.0",
-                "readdirp": "^2.2.1",
-                "upath": "^1.1.1"
+                "node": ">=10"
             },
-            "optionalDependencies": {
-                "fsevents": "^1.2.7"
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/define-property": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-            "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+            "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
             "dependencies": {
-                "is-descriptor": "^1.0.2",
-                "isobject": "^3.0.1"
+                "color-name": "~1.1.4"
             },
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=7.0.0"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-            "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+            "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+        },
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": {
+            "version": "8.3.6",
+            "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+            "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
             "dependencies": {
-                "extend-shallow": "^2.0.1",
-                "is-number": "^3.0.0",
-                "repeat-string": "^1.6.1",
-                "to-regex-range": "^2.1.0"
+                "import-fresh": "^3.3.0",
+                "js-yaml": "^4.1.0",
+                "parse-json": "^5.2.0",
+                "path-type": "^4.0.0"
             },
             "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/fsevents": {
-            "version": "1.2.13",
-            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
-            "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
-            "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2",
-            "hasInstallScript": true,
-            "optional": true,
-            "os": [
-                "darwin"
-            ],
-            "dependencies": {
-                "bindings": "^1.5.0",
-                "nan": "^2.12.1"
+                "node": ">=14"
             },
-            "engines": {
-                "node": ">= 4.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/glob-parent": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-            "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
-            "dependencies": {
-                "is-glob": "^3.1.0",
-                "path-dirname": "^1.0.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/glob-parent/node_modules/is-glob": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-            "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
-            "dependencies": {
-                "is-extglob": "^2.1.0"
+            "funding": {
+                "url": "https://github.com/sponsors/d-fischer"
             },
-            "engines": {
-                "node": ">=0.10.0"
+            "peerDependencies": {
+                "typescript": ">=4.9.5"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-binary-path": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-            "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
-            "dependencies": {
-                "binary-extensions": "^1.0.0"
-            },
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/deepmerge": {
+            "version": "4.3.1",
+            "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+            "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
             "engines": {
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": {
+            "version": "10.1.0",
+            "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+            "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
             "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
+                "graceful-fs": "^4.2.0",
+                "jsonfile": "^6.0.1",
+                "universalify": "^2.0.0"
             },
             "engines": {
-                "node": ">= 0.4"
+                "node": ">=12"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=8"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-            "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/js-yaml": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+            "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
             "dependencies": {
-                "kind-of": "^3.0.2"
+                "argparse": "^2.0.1"
             },
-            "engines": {
-                "node": ">=0.10.0"
+            "bin": {
+                "js-yaml": "bin/js-yaml.js"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/jsonfile": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+            "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
             "dependencies": {
-                "is-buffer": "^1.1.5"
+                "universalify": "^2.0.0"
             },
-            "engines": {
-                "node": ">=0.10.0"
+            "optionalDependencies": {
+                "graceful-fs": "^4.1.6"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch": {
-            "version": "3.1.10",
-            "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-            "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": {
+            "version": "7.5.4",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+            "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
             "dependencies": {
-                "arr-diff": "^4.0.0",
-                "array-unique": "^0.3.2",
-                "braces": "^2.3.1",
-                "define-property": "^2.0.2",
-                "extend-shallow": "^3.0.2",
-                "extglob": "^2.0.4",
-                "fragment-cache": "^0.2.1",
-                "kind-of": "^6.0.2",
-                "nanomatch": "^1.2.9",
-                "object.pick": "^1.3.0",
-                "regex-not": "^1.0.0",
-                "snapdragon": "^0.8.1",
-                "to-regex": "^3.0.2"
+                "lru-cache": "^6.0.0"
             },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
+            "bin": {
+                "semver": "bin/semver.js"
             },
             "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/readable-stream": {
-            "version": "2.3.8",
-            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
-            "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
-            "dependencies": {
-                "core-util-is": "~1.0.0",
-                "inherits": "~2.0.3",
-                "isarray": "~1.0.0",
-                "process-nextick-args": "~2.0.0",
-                "safe-buffer": "~5.1.1",
-                "string_decoder": "~1.1.1",
-                "util-deprecate": "~1.0.1"
+                "node": ">=10"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": {
-            "version": "2.2.1",
-            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
-            "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": {
+            "version": "7.2.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+            "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
             "dependencies": {
-                "graceful-fs": "^4.1.11",
-                "micromatch": "^3.1.10",
-                "readable-stream": "^2.0.2"
+                "has-flag": "^4.0.0"
             },
             "engines": {
-                "node": ">=0.10"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
-            "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
-            "engines": {
-                "node": ">=6"
+                "node": ">=8"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/to-regex-range": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-            "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
-            "dependencies": {
-                "is-number": "^3.0.0",
-                "repeat-string": "^1.6.1"
-            },
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/universalify": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+            "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">= 10.0.0"
             }
         },
         "node_modules/form-data": {
@@ -15810,17 +15356,6 @@
                 "node": ">= 0.6"
             }
         },
-        "node_modules/fragment-cache": {
-            "version": "0.2.1",
-            "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-            "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
-            "dependencies": {
-                "map-cache": "^0.2.2"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/fresh": {
             "version": "0.5.2",
             "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -16193,14 +15728,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/get-value": {
-            "version": "2.0.6",
-            "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-            "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/getpass": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -16529,25 +16056,6 @@
                 "node": ">= 0.4.0"
             }
         },
-        "node_modules/has-ansi": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-            "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
-            "dependencies": {
-                "ansi-regex": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-ansi/node_modules/ansi-regex": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-            "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/has-bigints": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -16616,69 +16124,6 @@
             "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
             "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
         },
-        "node_modules/has-value": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-            "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
-            "dependencies": {
-                "get-value": "^2.0.6",
-                "has-values": "^1.0.0",
-                "isobject": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-values": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-            "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
-            "dependencies": {
-                "is-number": "^3.0.0",
-                "kind-of": "^4.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-values/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/has-values/node_modules/is-number": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-            "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
-            "dependencies": {
-                "kind-of": "^3.0.2"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-values/node_modules/is-number/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-values/node_modules/kind-of": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-            "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/hasown": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
@@ -17824,17 +17269,6 @@
                 "node": ">= 0.10"
             }
         },
-        "node_modules/is-accessor-descriptor": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz",
-            "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==",
-            "dependencies": {
-                "hasown": "^2.0.0"
-            },
-            "engines": {
-                "node": ">= 0.10"
-            }
-        },
         "node_modules/is-alphabetical": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@@ -17986,17 +17420,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/is-data-descriptor": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz",
-            "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==",
-            "dependencies": {
-                "hasown": "^2.0.0"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/is-date-object": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
@@ -18020,18 +17443,6 @@
                 "url": "https://github.com/sponsors/wooorm"
             }
         },
-        "node_modules/is-descriptor": {
-            "version": "0.1.7",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
-            "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/is-docker": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
@@ -18067,14 +17478,6 @@
                 "node": ">=0.4.0"
             }
         },
-        "node_modules/is-extendable": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-            "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/is-extglob": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -19361,25 +18764,6 @@
             "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
             "dev": true
         },
-        "node_modules/map-cache": {
-            "version": "0.2.2",
-            "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-            "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/map-visit": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-            "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
-            "dependencies": {
-                "object-visit": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/markdown-table": {
             "version": "3.0.3",
             "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
@@ -19766,11 +19150,6 @@
                 "node": ">= 0.6"
             }
         },
-        "node_modules/microevent.ts": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz",
-            "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g=="
-        },
         "node_modules/micromark": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz",
@@ -20452,29 +19831,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/mixin-deep": {
-            "version": "1.3.2",
-            "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
-            "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
-            "dependencies": {
-                "for-in": "^1.0.2",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/mixin-deep/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/mkdirp": {
             "version": "0.5.4",
             "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
@@ -21404,74 +20760,6 @@
             "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
             "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ=="
         },
-        "node_modules/nanomatch": {
-            "version": "1.2.13",
-            "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
-            "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
-            "dependencies": {
-                "arr-diff": "^4.0.0",
-                "array-unique": "^0.3.2",
-                "define-property": "^2.0.2",
-                "extend-shallow": "^3.0.2",
-                "fragment-cache": "^0.2.1",
-                "is-windows": "^1.0.2",
-                "kind-of": "^6.0.2",
-                "object.pick": "^1.3.0",
-                "regex-not": "^1.0.0",
-                "snapdragon": "^0.8.1",
-                "to-regex": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/nanomatch/node_modules/define-property": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-            "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-            "dependencies": {
-                "is-descriptor": "^1.0.2",
-                "isobject": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/nanomatch/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/nanomatch/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
-        "node_modules/nanomatch/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/natural-compare": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -21541,6 +20829,11 @@
                 "tslib": "^2.0.3"
             }
         },
+        "node_modules/node-abort-controller": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
+            "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="
+        },
         "node_modules/node-domexception": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@@ -24533,35 +23826,6 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/object-copy": {
-            "version": "0.1.0",
-            "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-            "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
-            "dependencies": {
-                "copy-descriptor": "^0.1.0",
-                "define-property": "^0.2.5",
-                "kind-of": "^3.0.3"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/object-copy/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/object-copy/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/object-inspect": {
             "version": "1.13.1",
             "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
@@ -24623,17 +23887,6 @@
                 "pipe-functions": "^1.2.0"
             }
         },
-        "node_modules/object-visit": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-            "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
-            "dependencies": {
-                "isobject": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/object.assign": {
             "version": "4.1.0",
             "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
@@ -24722,17 +23975,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/object.pick": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-            "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
-            "dependencies": {
-                "isobject": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/object.values": {
             "version": "1.1.7",
             "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz",
@@ -25136,14 +24378,6 @@
                 "tslib": "^2.0.3"
             }
         },
-        "node_modules/pascalcase": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-            "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/passport": {
             "version": "0.7.0",
             "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
@@ -25215,11 +24449,6 @@
             "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
             "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
         },
-        "node_modules/path-dirname": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-            "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q=="
-        },
         "node_modules/path-exists": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
@@ -25294,6 +24523,15 @@
                 "node": ">=8"
             }
         },
+        "node_modules/path2d-polyfill": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz",
+            "integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==",
+            "optional": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
         "node_modules/pathval": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
@@ -25336,20 +24574,15 @@
             }
         },
         "node_modules/pdfjs-dist": {
-            "version": "2.16.105",
-            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz",
-            "integrity": "sha512-J4dn41spsAwUxCpEoVf6GVoz908IAA3mYiLmNxg8J9kfRXc2jxpbUepcP0ocp0alVNLFthTAM8DZ1RaHh8sU0A==",
-            "dependencies": {
-                "dommatrix": "^1.0.3",
-                "web-streams-polyfill": "^3.2.1"
-            },
-            "peerDependencies": {
-                "worker-loader": "^3.0.8"
+            "version": "4.0.269",
+            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.0.269.tgz",
+            "integrity": "sha512-jjWO56tcOjnmPqDf8PmXDeZ781AGvpHMYI3HhNtaFKTRXXPaD1ArSrhVe38/XsrIQJ0onISCND/vuXaWJkiDWw==",
+            "engines": {
+                "node": ">=18"
             },
-            "peerDependenciesMeta": {
-                "worker-loader": {
-                    "optional": true
-                }
+            "optionalDependencies": {
+                "canvas": "^2.11.2",
+                "path2d-polyfill": "^2.0.1"
             }
         },
         "node_modules/pdfjs/node_modules/pako": {
@@ -25515,14 +24748,6 @@
                 "node": ">=4.0.0"
             }
         },
-        "node_modules/posix-character-classes": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-            "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/postcss": {
             "version": "8.4.32",
             "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
@@ -26816,41 +26041,6 @@
                 "@babel/runtime": "^7.8.4"
             }
         },
-        "node_modules/regex-not": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
-            "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
-            "dependencies": {
-                "extend-shallow": "^3.0.2",
-                "safe-regex": "^1.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/regex-not/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/regex-not/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/regexp.prototype.flags": {
             "version": "1.5.1",
             "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
@@ -26998,11 +26188,6 @@
                 "url": "https://opencollective.com/unified"
             }
         },
-        "node_modules/remove-trailing-separator": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-            "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
-        },
         "node_modules/renderkid": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
@@ -27100,22 +26285,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/repeat-element": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
-            "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/repeat-string": {
-            "version": "1.6.1",
-            "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-            "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
-            "engines": {
-                "node": ">=0.10"
-            }
-        },
         "node_modules/request": {
             "version": "2.88.2",
             "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
@@ -27308,12 +26477,6 @@
                 "node": ">=4"
             }
         },
-        "node_modules/resolve-url": {
-            "version": "0.2.1",
-            "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-            "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
-            "deprecated": "https://github.com/lydell/resolve-url#deprecated"
-        },
         "node_modules/responselike": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
@@ -27341,14 +26504,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/ret": {
-            "version": "0.1.15",
-            "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
-            "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
-            "engines": {
-                "node": ">=0.12"
-            }
-        },
         "node_modules/retry": {
             "version": "0.13.1",
             "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
@@ -27631,14 +26786,6 @@
             "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
             "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
         },
-        "node_modules/safe-regex": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-            "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
-            "dependencies": {
-                "ret": "~0.1.10"
-            }
-        },
         "node_modules/safe-regex-test": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
@@ -27981,20 +27128,6 @@
                 "node": ">= 0.4"
             }
         },
-        "node_modules/set-value": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
-            "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
-            "dependencies": {
-                "extend-shallow": "^2.0.1",
-                "is-extendable": "^0.1.1",
-                "is-plain-object": "^2.0.3",
-                "split-string": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/setimmediate": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -28176,100 +27309,6 @@
                 "node": ">=6"
             }
         },
-        "node_modules/snapdragon": {
-            "version": "0.8.2",
-            "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
-            "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
-            "dependencies": {
-                "base": "^0.11.1",
-                "debug": "^2.2.0",
-                "define-property": "^0.2.5",
-                "extend-shallow": "^2.0.1",
-                "map-cache": "^0.2.2",
-                "source-map": "^0.5.6",
-                "source-map-resolve": "^0.5.0",
-                "use": "^3.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon-node": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
-            "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
-            "dependencies": {
-                "define-property": "^1.0.0",
-                "isobject": "^3.0.0",
-                "snapdragon-util": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon-node/node_modules/define-property": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-            "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
-            "dependencies": {
-                "is-descriptor": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon-node/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
-        "node_modules/snapdragon-util": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
-            "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
-            "dependencies": {
-                "kind-of": "^3.2.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon-util/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/snapdragon-util/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon/node_modules/debug": {
-            "version": "2.6.9",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-            "dependencies": {
-                "ms": "2.0.0"
-            }
-        },
-        "node_modules/snapdragon/node_modules/ms": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-            "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
-        },
         "node_modules/socket.io": {
             "version": "4.7.2",
             "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz",
@@ -28440,19 +27479,6 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/source-map-resolve": {
-            "version": "0.5.3",
-            "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
-            "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
-            "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
-            "dependencies": {
-                "atob": "^2.1.2",
-                "decode-uri-component": "^0.2.0",
-                "resolve-url": "^0.2.1",
-                "source-map-url": "^0.4.0",
-                "urix": "^0.1.0"
-            }
-        },
         "node_modules/source-map-support": {
             "version": "0.5.21",
             "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
@@ -28470,12 +27496,6 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/source-map-url": {
-            "version": "0.4.1",
-            "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
-            "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
-            "deprecated": "See https://github.com/lydell/source-map-url#deprecated"
-        },
         "node_modules/space-separated-tokens": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
@@ -28582,40 +27602,6 @@
             "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
             "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE="
         },
-        "node_modules/split-string": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
-            "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
-            "dependencies": {
-                "extend-shallow": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/split-string/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/split-string/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/sprintf-js": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -28658,18 +27644,6 @@
                 "standard-error": ">= 1.1.0 < 2"
             }
         },
-        "node_modules/static-extend": {
-            "version": "0.1.2",
-            "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-            "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
-            "dependencies": {
-                "define-property": "^0.2.5",
-                "object-copy": "^0.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/statuses": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -29335,47 +28309,6 @@
                 "node": ">=4"
             }
         },
-        "node_modules/to-object-path": {
-            "version": "0.3.0",
-            "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-            "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
-            "dependencies": {
-                "kind-of": "^3.0.2"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/to-object-path/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/to-object-path/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/to-regex": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
-            "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
-            "dependencies": {
-                "define-property": "^2.0.2",
-                "extend-shallow": "^3.0.2",
-                "regex-not": "^1.0.2",
-                "safe-regex": "^1.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/to-regex-range": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -29387,53 +28320,6 @@
                 "node": ">=8.0"
             }
         },
-        "node_modules/to-regex/node_modules/define-property": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-            "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-            "dependencies": {
-                "is-descriptor": "^1.0.2",
-                "isobject": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/to-regex/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/to-regex/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
-        "node_modules/to-regex/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/toidentifier": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -30034,15 +28920,15 @@
             }
         },
         "node_modules/typescript": {
-            "version": "4.9.5",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-            "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+            "version": "5.3.3",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+            "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
             "bin": {
                 "tsc": "bin/tsc",
                 "tsserver": "bin/tsserver"
             },
             "engines": {
-                "node": ">=4.2.0"
+                "node": ">=14.17"
             }
         },
         "node_modules/typescript-collections": {
@@ -30179,20 +29065,6 @@
             "resolved": "https://registry.npmjs.org/uninstall/-/uninstall-0.0.0.tgz",
             "integrity": "sha512-pjP/0+A4gsbDVa8XH/S2GZdT9NPJW8NFMy3GI7HnsWG+NAmFSSj3QidNosXBI9cPtxxNExEDdhKFO6sli8K3mA=="
         },
-        "node_modules/union-value": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
-            "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
-            "dependencies": {
-                "arr-union": "^3.1.0",
-                "get-value": "^2.0.6",
-                "is-extendable": "^0.1.1",
-                "set-value": "^2.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/unist-util-is": {
             "version": "6.0.0",
             "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
@@ -30303,50 +29175,6 @@
                 "node": ">= 0.8"
             }
         },
-        "node_modules/unset-value": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-            "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
-            "dependencies": {
-                "has-value": "^0.3.1",
-                "isobject": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/unset-value/node_modules/has-value": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-            "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
-            "dependencies": {
-                "get-value": "^2.0.3",
-                "has-values": "^0.1.4",
-                "isobject": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-            "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
-            "dependencies": {
-                "isarray": "1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/unset-value/node_modules/has-values": {
-            "version": "0.1.4",
-            "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-            "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/untildify": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
@@ -30356,15 +29184,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/upath": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
-            "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
-            "engines": {
-                "node": ">=4",
-                "yarn": "*"
-            }
-        },
         "node_modules/update-browserslist-db": {
             "version": "1.0.13",
             "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
@@ -30402,12 +29221,6 @@
                 "punycode": "^2.1.0"
             }
         },
-        "node_modules/urix": {
-            "version": "0.1.0",
-            "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-            "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
-            "deprecated": "Please see https://github.com/lydell/urix#deprecated"
-        },
         "node_modules/url": {
             "version": "0.11.3",
             "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz",
@@ -30500,14 +29313,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/use": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
-            "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/use-isomorphic-layout-effect": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
@@ -31472,14 +30277,6 @@
                 "node": ">=4.0.0"
             }
         },
-        "node_modules/worker-rpc": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
-            "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==",
-            "dependencies": {
-                "microevent.ts": "~0.1.1"
-            }
-        },
         "node_modules/workerpool": {
             "version": "6.2.1",
             "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
diff --git a/package.json b/package.json
index 42b3c1ce2..e1efce5a3 100644
--- a/package.json
+++ b/package.json
@@ -89,7 +89,7 @@
         "ts-loader": "^9.5.1",
         "ts-node": "^10.9.1",
         "ts-node-dev": "^2.0.0",
-        "typescript": "^4.7.4",
+        "typescript": "^5.3.3",
         "webpack-dev-server": "^4.15.1",
         "webpack-hot-middleware": "^2.25.4"
     },
@@ -97,6 +97,7 @@
         "@azure/storage-blob": "^12.17.0",
         "@babel/preset-env": "^7.23.5",
         "@babel/preset-react": "^7.23.3",
+        "@bundled-es-modules/pdfjs-dist": "^3.6.172-alpha.1",
         "@emotion/react": "^11.11.1",
         "@emotion/styled": "^11.11.0",
         "@ffmpeg/core": "0.12.4",
@@ -121,7 +122,7 @@
         "@types/google-maps": "^3.2.6",
         "@types/reveal": "^4.2.0",
         "@types/supercluster": "^7.1.3",
-        "@types/web": "0.0.125",
+        "@types/web": "^0.0.127",
         "@webscopeio/react-textarea-autocomplete": "^4.9.2",
         "adm-zip": "^0.5.10",
         "archiver": "^6.0.1",
@@ -173,7 +174,7 @@
         "flexlayout-react": "^0.7.15",
         "fluent-ffmpeg": "^2.1.2",
         "forever-agent": "^0.6.1",
-        "fork-ts-checker-webpack-plugin": "^1.6.0",
+        "fork-ts-checker-webpack-plugin": "^9.0.2",
         "formidable": "3.5.1",
         "function-plot": "^1.23.3",
         "golden-layout": "^2.6.0",
@@ -221,7 +222,7 @@
         "path-browserify": "^1.0.1",
         "pdf-parse": "^1.1.1",
         "pdfjs": "^2.4.7",
-        "pdfjs-dist": "^2.14.305",
+        "pdfjs-dist": "^4.0.269",
         "probe-image-size": "^7.2.3",
         "process": "^0.11.10",
         "prosemirror-commands": "^1.5.2",
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 249e2c3a3..831925054 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -204,7 +204,6 @@ export class ContextMenu extends React.Component {
     }
 
     render() {
-        console.log('DISPLAY = ' + this._display);
         return (
             <div
                 className="contextMenu-cont"
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 04fb1fe80..35c0d617b 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, ColorPicker, EditableText, 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 { FaPlus } from 'react-icons/fa';
@@ -39,6 +39,11 @@ enum DashboardGroup {
 export class DashboardView extends React.Component {
     public static _urlState: HistoryUtil.DocUrl;
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     @observable private openModal = false;
     @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
     @observable private newDashboardName = '';
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dae2490c9..a8f4020ee 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -56,7 +56,7 @@ import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 import GenerativeFill from './nodes/generativeFill/GenerativeFill';
 import { ImageBox } from './nodes/ImageBox';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
-import { LinkDocPreview } from './nodes/LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview';
 import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu';
 import { MapBox } from './nodes/MapBox/MapBox';
 import { RadialMenu } from './nodes/RadialMenu';
@@ -968,9 +968,6 @@ export class MainView extends React.Component {
         );
     }
 
-    @computed get linkDocPreview() {
-        return LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null;
-    }
     @observable mapBoxHackBool = false;
     @computed get mapBoxHack() {
         return this.mapBoxHackBool ? null : (
@@ -1039,7 +1036,7 @@ export class MainView extends React.Component {
                 {this._hideUI ? null : <TopBar />}
                 {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
                 {DocButtonState.Instance.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocButtonState.Instance.LinkEditorDocView = undefined))} docView={DocButtonState.Instance.LinkEditorDocView} /> : null}
-                {this.linkDocPreview}
+                {LinkInfo.Instance?.LinkInfo ? <LinkDocPreview {...LinkInfo.Instance.LinkInfo} /> : null}
 
                 {((page: string) => {
                     // prettier-ignore
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 196250167..5bde9d3c4 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -1,4 +1,4 @@
-import { computed } from 'mobx';
+import { computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../fields/Doc';
@@ -8,6 +8,7 @@ import { DocFocusOrOpen } from '../util/DocumentManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { DocumentView, OpenWhere } from './nodes/DocumentView';
 import './PropertiesDocContextSelector.scss';
+import { copyProps } from '../../Utils';
 
 type PropertiesDocContextSelectorProps = {
     DocView?: DocumentView;
@@ -18,10 +19,20 @@ type PropertiesDocContextSelectorProps = {
 
 @observer
 export class PropertiesDocContextSelector extends React.Component<PropertiesDocContextSelectorProps> {
+    _prevProps: PropertiesDocContextSelectorProps;
+    @observable _props: PropertiesDocContextSelectorProps;
+    constructor(props: PropertiesDocContextSelectorProps) {
+        super(props);
+        this._props = this._prevProps = props;
+        makeObservable(this);
+    }
+    componentDidUpdate() {
+        copyProps(this);
+    }
     @computed get _docs() {
-        if (!this.props.DocView) return [];
-        const target = this.props.DocView.props.Document;
-        const targetContext = this.props.DocView.props.docViewPath().lastElement()?.Document;
+        if (!this._props.DocView) return [];
+        const target = this._props.DocView._props.Document;
+        const targetContext = this._props.DocView._props.docViewPath().lastElement()?.Document;
         const embeddings = DocListCast(target.proto_embeddings);
         const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings));
@@ -40,23 +51,23 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
         );
 
         return doclayouts
-            .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document))
+            .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.Document))
             .filter(doc => !Doc.IsSystem(doc))
             .filter(doc => doc !== targetContext)
             .map(doc => ({ col: doc, target }));
     }
 
     getOnClick = (col: Doc, target: Doc) => {
-        if (!this.props.DocView) return;
+        if (!this._props.DocView) return;
         col = Doc.IsDataProto(col) ? Doc.MakeDelegate(col) : col;
-        DocFocusOrOpen(Doc.GetProto(this.props.DocView.props.Document), undefined, col);
+        DocFocusOrOpen(Doc.GetProto(this._props.DocView.Document), undefined, col);
     };
 
     render() {
         if (this._docs.length < 1) return undefined;
         return (
             <div>
-                {this.props.hideTitle ? null : <p key="contexts">Contexts:</p>}
+                {this._props.hideTitle ? null : <p key="contexts">Contexts:</p>}
                 {this._docs.map(doc => (
                     <p key={doc.col[Id] + doc.target[Id]}>
                         <a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a>
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 2a52d86c2..19473de2b 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -1,11 +1,11 @@
-import { computed } from 'mobx';
+import { computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, Field, FieldResult, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { RichTextField } from '../../fields/RichTextField';
 import { DocCast, NumCast, StrCast } from '../../fields/Types';
-import { emptyFunction, returnAll, returnFalse, returnOne, returnTrue, returnZero } from '../../Utils';
+import { copyProps, emptyFunction, returnAll, returnFalse, returnOne, returnTrue, returnZero } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { LinkManager } from '../util/LinkManager';
@@ -35,14 +35,21 @@ interface ExtraProps {
 }
 @observer
 export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
-    constructor(props: Readonly<FieldViewProps & ExtraProps>) {
+    _prevProps: FieldViewProps & ExtraProps;
+    @observable _props: FieldViewProps & ExtraProps;
+    constructor(props: FieldViewProps & ExtraProps) {
         super(props);
-        // this.props.dataDoc[this.sidebarKey] = new List<Doc>();  // bcz: can't do this here.  it blows away existing things and isn't a robust solution for making sure the field exists -- instead this should happen when the document is created and/or shared
+        this._props = this._prevProps = props;
+        makeObservable(this);
+        // this._props.dataDoc[this.sidebarKey] = new List<Doc>();  // bcz: can't do this here.  it blows away existing things and isn't a robust solution for making sure the field exists -- instead this should happen when the document is created and/or shared
+    }
+    componentDidUpdate() {
+        copyProps(this);
     }
     _stackRef = React.createRef<CollectionStackingView>();
     @computed get allMetadata() {
         const keys = new Map<string, FieldResult<Field>>();
-        DocListCast(this.props.Document[this.sidebarKey]).forEach(doc =>
+        DocListCast(this._props.Document[this.sidebarKey]).forEach(doc =>
             SearchUtil.documentKeys(doc)
                 .filter(key => key[0] && key[0] !== '_' && key[0] === key[0].toUpperCase())
                 .map(key => keys.set(key, doc[key]))
@@ -51,7 +58,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     }
     @computed get allHashtags() {
         const keys = new Set<string>();
-        DocListCast(this.props.Document[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag)));
+        DocListCast(this._props.Document[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag)));
         return Array.from(keys.keys())
             .filter(key => key[0])
             .filter(key => !key.startsWith('_') && (key[0] === '#' || key[0] === key[0].toUpperCase()))
@@ -59,7 +66,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     }
     @computed get allUsers() {
         const keys = new Set<string>();
-        DocListCast(this.props.Document[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
+        DocListCast(this._props.Document[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
         return Array.from(keys.keys()).sort();
     }
 
@@ -69,7 +76,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             .join(' ');
         const target = Docs.Create.TextDocument(startup, {
             title: '-note-',
-            annotationOn: this.props.Document,
+            annotationOn: this._props.Document,
             _width: 200,
             _height: 50,
             _layout_fitWidth: true,
@@ -147,10 +154,10 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         return target;
     };
     makeDocUnfiltered = (doc: Doc) => {
-        if (DocListCast(this.props.Document[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+        if (DocListCast(this._props.Document[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
             if (this.childFilters()) {
                 // if any child filters exist, get rid of them
-                this.props.layoutDoc._childFilters = new List<string>();
+                this._props.layoutDoc._childFilters = new List<string>();
             }
             return true;
         }
@@ -158,27 +165,27 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     };
 
     get sidebarKey() {
-        return this.props.fieldKey + '_sidebar';
+        return this._props.fieldKey + '_sidebar';
     }
     filtersHeight = () => 38;
     screenToLocalTransform = () =>
-        this.props
+        this._props
             .ScreenToLocalTransform()
-            .translate(Doc.NativeWidth(this.props.dataDoc), 0)
-            .scale(this.props.NativeDimScaling?.() || 1);
+            .translate(Doc.NativeWidth(this._props.dataDoc), 0)
+            .scale(this._props.NativeDimScaling?.() || 1);
     panelWidth = () =>
-        !this.props.showSidebar
+        !this._props.showSidebar
             ? 0
-            : this.props.usePanelWidth // [DocumentType.RTF, DocumentType.MAP].includes(this.props.layoutDoc.type as any)
-            ? this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1)
-            : ((NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth()) / NumCast(this.props.nativeWidth);
-    panelHeight = () => this.props.PanelHeight() - this.filtersHeight();
-    addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey);
-    moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
-    removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
-    childFilters = () => StrListCast(this.props.layoutDoc._childFilters);
+            : this._props.usePanelWidth // [DocumentType.RTF, DocumentType.MAP].includes(this._props.layoutDoc.type as any)
+              ? this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1)
+              : ((NumCast(this._props.nativeWidth) - Doc.NativeWidth(this._props.dataDoc)) * this._props.PanelWidth()) / NumCast(this._props.nativeWidth);
+    panelHeight = () => this._props.PanelHeight() - this.filtersHeight();
+    addDocument = (doc: Doc | Doc[]) => this._props.sidebarAddDocument(doc, this.sidebarKey);
+    moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this._props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
+    removeDocument = (doc: Doc | Doc[]) => this._props.removeDocument(doc, this.sidebarKey);
+    childFilters = () => StrListCast(this._props.layoutDoc._childFilters);
     layout_showTitle = () => 'title';
-    setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
+    setHeightCallback = (height: number) => this._props.setHeight?.(height + this.filtersHeight());
     sortByLinkAnchorY = (a: Doc, b: Doc) => {
         const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].link_anchor_1).y;
         const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].link_anchor_1).y;
@@ -188,7 +195,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         const renderTag = (tag: string) => {
             const active = this.childFilters().includes(`tags${Doc.FilterSep}${tag}${Doc.FilterSep}check`);
             return (
-                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, 'tags', tag, 'check', true, undefined, e.shiftKey)}>
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, 'tags', tag, 'check', true, undefined, e.shiftKey)}>
                     {tag}
                 </div>
             );
@@ -196,7 +203,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         const renderMeta = (tag: string, dflt: FieldResult<Field>) => {
             const active = this.childFilters().includes(`${tag}${Doc.FilterSep}${Doc.FilterAny}${Doc.FilterSep}exists`);
             return (
-                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}>
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}>
                     {tag}
                 </div>
             );
@@ -204,20 +211,20 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         const renderUsers = (user: string) => {
             const active = this.childFilters().includes(`author:${user}:check`);
             return (
-                <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, 'author', user, 'check', true, undefined, e.shiftKey)}>
+                <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, 'author', user, 'check', true, undefined, e.shiftKey)}>
                     {user}
                 </div>
             );
         };
         // TODO: Calculation of the topbar is hardcoded and different for text nodes - it should all be the same and all be part of SidebarAnnos
-        return !this.props.showSidebar ? null : (
+        return !this._props.showSidebar ? null : (
             <div
                 style={{
                     position: 'absolute',
-                    pointerEvents: this.props.isContentActive() ? 'all' : undefined,
-                    top: this.props.Document.type !== DocumentType.RTF && StrCast(this.props.Document._layout_showTitle) === 'title' ? 15 : 0,
+                    pointerEvents: this._props.isContentActive() ? 'all' : undefined,
+                    top: this._props.Document.type !== DocumentType.RTF && StrCast(this._props.Document._layout_showTitle) === 'title' ? 15 : 0,
                     right: 0,
-                    background: this.props.styleProvider?.(this.props.Document, this.props, StyleProp.WidgetColor),
+                    background: this._props.styleProvider?.(this._props.Document, this._props, StyleProp.WidgetColor),
                     width: `100%`,
                     height: '100%',
                 }}>
@@ -231,7 +238,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
 
                 <div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
                     <CollectionStackingView
-                        {...this.props}
+                        {...this._props}
                         setContentView={emptyFunction}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
@@ -246,14 +253,14 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         NativeDimScaling={returnOne}
                         //childlayout_showTitle={this.layout_showTitle}
                         isAnyChildContentActive={returnFalse}
-                        childDocumentsActive={this.props.isContentActive}
-                        whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                        childDocumentsActive={this._props.isContentActive}
+                        whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
                         childHideDecorationTitle={true}
                         removeDocument={this.removeDocument}
                         moveDocument={this.moveDocument}
                         addDocument={this.addDocument}
                         ScreenToLocalTransform={this.screenToLocalTransform}
-                        renderDepth={this.props.renderDepth + 1}
+                        renderDepth={this._props.renderDepth + 1}
                         type_collection={CollectionViewType.Stacking}
                         fieldKey={this.sidebarKey}
                         pointerEvents={returnAll}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 154c914ad..d16599bab 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1441,7 +1441,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return anchor;
     };
 
-    infoUI = () => <CollectionFreeFormInfoUI Document={this.Document} Freeform={this} />;
+    infoUI = () => (this.Document.annotationOn ? null : <CollectionFreeFormInfoUI Document={this.Document} Freeform={this} />);
 
     componentDidMount() {
         this._props.setContentView?.(this);
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index af0fe73c3..bcfe2c232 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -121,7 +121,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                 <EditableView
                     oneLine={this._props.oneLine}
                     allowCRs={this._props.allowCRs}
-                    contents={<FieldView {...fieldProps} />}
+                    contents={undefined}
+                    fieldContents={fieldProps}
                     editing={this.selected ? undefined : false}
                     GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
                     SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
@@ -333,7 +334,8 @@ export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
                     })}
                 />
                 <EditableView
-                    contents={<FieldView {...fieldProps} />}
+                    contents={undefined}
+                    fieldContents={fieldProps}
                     editing={this.selected ? undefined : false}
                     GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
                     SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 91d812f35..af22f41e1 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
 import { Doc } from '../../../fields/Doc';
 import { LinkManager } from '../../util/LinkManager';
 import { DocumentView } from '../nodes/DocumentView';
-import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { LinkInfo } from '../nodes/LinkDocPreview';
 import './LinkMenu.scss';
 import { LinkMenuGroup } from './LinkMenuGroup';
 import * as React from 'react';
@@ -38,7 +38,7 @@ export class LinkMenu extends React.Component<Props> {
     }
 
     onPointerDown = action((e: PointerEvent) => {
-        LinkDocPreview.Clear();
+        LinkInfo.Clear();
         if (!this._linkMenuRef.current?.contains(e.target as any) && !this._editorRef.current?.contains(e.target as any)) {
             this.clear();
         }
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 858cc2e5e..d77525e04 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -15,7 +15,7 @@ import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
 import { undoBatch } from '../../util/UndoManager';
 import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
-import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
 import * as React from 'react';
 
@@ -180,11 +180,11 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                         </div>
                         <div
                             className="linkMenu-text"
-                            onPointerLeave={LinkDocPreview.Clear}
+                            onPointerLeave={LinkInfo.Clear}
                             onPointerEnter={e =>
                                 this._props.linkDoc &&
                                 this._props.clearLinkEditor &&
-                                LinkDocPreview.SetLinkInfo({
+                                LinkInfo.SetLinkInfo({
                                     docProps: this._props.docView._props,
                                     linkSrc: this._props.sourceDoc,
                                     linkDoc: this._props.linkDoc,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b3acb08e2..abe59feb5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -136,7 +136,7 @@ export interface DocComponentView {
     componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
     dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void;
     incrementalRendering?: () => void;
-    infoUI?: () => JSX.Element;
+    infoUI?: () => JSX.Element | null;
     getCenter?: (xf: Transform) => { X: number; Y: number };
     screenBounds?: () => Opt<{ left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } }>;
     ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 1b5161e47..33c9b2e08 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -11,7 +11,7 @@ import { ViewBoxBaseComponent } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LinkAnchorBox.scss';
-import { LinkDocPreview } from './LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from './LinkDocPreview';
 import * as React from 'react';
 // import globalCssVariables = require('../global/globalCssVariables.scss');
 const MEDIUM_GRAY = 'lightGray';
@@ -85,7 +85,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 title={targetTitle}
                 className={`linkAnchorBox-cont${small ? '-small' : ''}`}
                 onPointerEnter={e =>
-                    LinkDocPreview.SetLinkInfo({
+                    LinkInfo.SetLinkInfo({
                         docProps: this.props,
                         linkSrc: this.linkSource,
                         linkDoc: this.Document,
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index dd102edef..108930ad9 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -1,11 +1,11 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 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 { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
+import { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -20,6 +20,25 @@ import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView
 import './LinkDocPreview.scss';
 import * as React from 'react';
 
+export class LinkInfo {
+    private static _instance: Opt<LinkInfo>;
+    constructor() {
+        LinkInfo._instance = this;
+        makeObservable(this);
+    }
+    @observable public LinkInfo: Opt<LinkDocPreviewProps> = undefined;
+
+    public static get Instance() {
+        return LinkInfo._instance ?? new LinkInfo();
+    }
+    public static Clear() {
+        runInAction(() => LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = undefined));
+    }
+    public static SetLinkInfo(info?: LinkDocPreviewProps) {
+        runInAction(() => LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = info));
+    }
+}
+
 interface LinkDocPreviewProps {
     linkDoc?: Doc;
     linkSrc?: Doc;
@@ -31,35 +50,28 @@ interface LinkDocPreviewProps {
 }
 @observer
 export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
-    @action public static Clear() {
-        LinkDocPreview.LinkInfo = undefined;
-    }
-    @action public static SetLinkInfo(info?: LinkDocPreviewProps) {
-        LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info);
+    _prevProps: LinkDocPreviewProps;
+    @observable _props: LinkDocPreviewProps;
+    constructor(props: LinkDocPreviewProps) {
+        super(props);
+        this._props = this._prevProps = props;
+        makeObservable(this);
     }
 
-    static _instance: Opt<LinkDocPreview>;
-
     _infoRef = React.createRef<HTMLDivElement>();
     _linkDocRef = React.createRef<HTMLDivElement>();
-    @observable public static LinkInfo: Opt<LinkDocPreviewProps>;
-    @observable _targetDoc: Opt<Doc>;
-    @observable _markerTargetDoc: Opt<Doc>;
-    @observable _linkDoc: Opt<Doc>;
-    @observable _linkSrc: Opt<Doc>;
+    @observable _targetDoc: Opt<Doc> = undefined;
+    @observable _markerTargetDoc: Opt<Doc> = undefined;
+    @observable _linkDoc: Opt<Doc> = undefined;
+    @observable _linkSrc: Opt<Doc> = undefined;
     @observable _toolTipText = '';
     @observable _hrefInd = 0;
 
-    constructor(props: any) {
-        super(props);
-        LinkDocPreview._instance = this;
-    }
-
     @action
     init() {
-        var linkTarget = this.props.linkDoc;
-        this._linkSrc = this.props.linkSrc;
-        this._linkDoc = this.props.linkDoc;
+        var linkTarget = this._props.linkDoc;
+        this._linkSrc = this._props.linkSrc;
+        this._linkDoc = this._props.linkDoc;
         const link_anchor_1 = DocCast(this._linkDoc?.link_anchor_1);
         const link_anchor_2 = DocCast(this._linkDoc?.link_anchor_2);
         if (link_anchor_1 && link_anchor_2) {
@@ -73,27 +85,27 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         this.updateHref();
     }
     componentDidUpdate(props: any) {
-        if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init();
+        copyProps(this);
+        if (props.linkSrc !== this._props.linkSrc || props.linkDoc !== this._props.linkDoc || props.hrefs !== this._props.hrefs) this.init();
     }
     componentDidMount() {
         this.init();
         document.addEventListener('pointerdown', this.onPointerDown, true);
     }
 
-    @action
     componentWillUnmount() {
-        LinkDocPreview.SetLinkInfo(undefined);
+        LinkInfo.Clear();
         document.removeEventListener('pointerdown', this.onPointerDown, true);
     }
 
     onPointerDown = (e: PointerEvent) => {
-        !this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
+        !this._linkDocRef.current?.contains(e.target as any) && LinkInfo.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
     };
 
     @action
     updateHref() {
-        if (this.props.hrefs?.length) {
-            const href = this.props.hrefs[this._hrefInd];
+        if (this._props.hrefs?.length) {
+            const href = this._props.hrefs[this._hrefInd];
             if (href.indexOf(Doc.localServerPath()) !== 0) {
                 // link to a web page URL -- try to show a preview
                 if (href.startsWith('https://en.wikipedia.org/wiki/')) {
@@ -123,7 +135,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                                 this._markerTargetDoc = linkTarget;
                                 this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
                             }
-                            if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
+                            if (LinkInfo.Instance?.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
                         }
                     })
                 );
@@ -143,7 +155,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
             action(() => {
                 LinkManager.currentLink = this._linkDoc;
                 LinkManager.currentLinkAnchor = this._linkSrc;
-                this.props.docProps.DocumentView?.().select(false);
+                this._props.docProps.DocumentView?.().select(false);
                 if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
                     SettingsManager.Instance.propertiesWidth = 250;
                 }
@@ -157,7 +169,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
             returnFalse,
             emptyFunction,
             action(() => {
-                const nextHrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1);
+                const nextHrefInd = (this._hrefInd + 1) % (this._props.hrefs?.length || 1);
                 if (nextHrefInd !== this._hrefInd) {
                     this._linkDoc = undefined;
                     this._hrefInd = nextHrefInd;
@@ -168,19 +180,19 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     };
 
     followLink = () => {
-        LinkDocPreview.Clear();
+        LinkInfo.Clear();
         if (this._linkDoc && this._linkSrc) {
             LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false);
-        } else if (this.props.hrefs?.length) {
+        } else if (this._props.hrefs?.length) {
             const webDoc =
-                Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ??
-                Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
+                Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this._props.hrefs[0]).keys()).lastElement() ??
+                Docs.Create.WebDocument(this._props.hrefs[0], { title: this._props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
             DocumentManager.Instance.showDocument(webDoc, {
                 openLocation: OpenWhere.lightbox,
                 willPan: true,
                 zoomTime: 500,
             });
-            //this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
+            //this._props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
         }
     };
 
@@ -216,7 +228,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 </div>
                 <div className="linkDocPreview-buttonBar" style={{ float: 'right' }}>
                     <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
-                        <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
+                        <div className="linkDocPreview-button" style={{ background: (this._props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
                             <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" />
                         </div>
                     </Tooltip>
@@ -228,7 +240,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     @computed get docPreview() {
         return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
             <div className="linkDocPreview-inner">
-                {!this.props.showHeader ? null : this.previewHeader}
+                {!this._props.showHeader ? null : this.previewHeader}
                 <div
                     className="linkDocPreview-preview-wrapper"
                     onPointerDown={e =>
@@ -238,7 +250,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             (e, down, delta) => {
                                 if (Math.abs(e.clientX - down[0]) + Math.abs(e.clientY - down[1]) > 100) {
                                     DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
-                                    LinkDocPreview.Clear();
+                                    LinkInfo.Clear();
                                     return true;
                                 }
                                 return false;
@@ -256,11 +268,11 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                         <DocumentView
                             ref={r => {
                                 const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
-                                targetanchor && this._targetDoc !== targetanchor && r?.props.focus?.(targetanchor, {});
+                                targetanchor && this._targetDoc !== targetanchor && r?._props.focus?.(targetanchor, {});
                             }}
                             Document={this._targetDoc!}
                             moveDocument={returnFalse}
-                            styleProvider={this.props.docProps?.styleProvider}
+                            styleProvider={this._props.docProps?.styleProvider}
                             docViewPath={returnEmptyDoclist}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
@@ -299,7 +311,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 className="linkDocPreview"
                 ref={this._linkDocRef}
                 onPointerDown={this.followLinkPointerDown}
-                style={{ borderColor: SettingsManager.userColor, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+                style={{ borderColor: SettingsManager.userColor, left: this._props.location[0], top: this._props.location[1], width: this.width() + borders, height: this.height() + borders + (this._props.showHeader ? 37 : 0) }}>
                 {this.docPreview}
             </div>
         );
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 2a884cef8..a453210eb 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -611,8 +611,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>();
     render() {
         TraceMobx();
-        const pdfView = this.renderPdfView;
-
+        const pdfView = !this._pdf ? null : this.renderPdfView;
         const href = this.pdfUrl?.url.href;
         if (!pdfView && href) {
             if (PDFBox.pdfcache.get(href)) setTimeout(action(() => (this._pdf = PDFBox.pdfcache.get(href))));
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c722399c1..d74af9b27 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,6 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, override, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import * as WebRequest from 'web-request';
 import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -12,7 +13,7 @@ import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast, WebCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, copyProps, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
@@ -33,10 +34,9 @@ import { SidebarAnnos } from '../SidebarAnnos';
 import { StyleProp } from '../StyleProvider';
 import { DocComponentView, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
-import { LinkDocPreview } from './LinkDocPreview';
+import { LinkInfo } from './LinkDocPreview';
 import { PinProps, PresBox } from './trails';
 import './WebBox.scss';
-import * as React from 'react';
 const { CreateImage } = require('./WebBoxRenderer');
 const _global = (window /* browser */ || global) /* node */ as any;
 const htmlToText = require('html-to-text');
@@ -95,12 +95,20 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
     @computed get webField() {
-        return Cast(this.Document[this.props.fieldKey], WebField)?.url;
+        return Cast(this.Document[this._props.fieldKey], WebField)?.url;
     }
 
-    constructor(props: any) {
+    _prevProps: ViewBoxAnnotatableProps & FieldViewProps;
+    @override _props: ViewBoxAnnotatableProps & FieldViewProps;
+    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
         super(props);
-        runInAction(() => (this._webUrl = this._url)); // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
+        this._props = this._prevProps = 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.
+    }
+
+    componentDidUpdate() {
+        copyProps(this);
     }
 
     @action
@@ -139,7 +147,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this._iframe) return;
         const scrollTop = NumCast(this.layoutDoc._layout_scrollTop);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
-        const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
+        const nativeHeight = (nativeWidth * this._props.PanelHeight()) / this._props.PanelWidth();
         var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
         if (!htmlString) {
             htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
@@ -170,8 +178,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             });
     };
 
-    async componentDidMount() {
-        this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
+    componentDidMount() {
+        this._props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
         runInAction(() => {
             this._annotationKeySuffix = () => (this._urlHash ? this._urlHash + '_' : '') + 'annotations';
@@ -200,8 +208,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             () => this.layoutDoc._layout_autoHeight,
             layout_autoHeight => {
                 if (layout_autoHeight) {
-                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
+                    this.layoutDoc._nativeHeight = NumCast(this.Document[this._props.fieldKey + '_nativeHeight']);
+                    this._props.setHeight?.(NumCast(this.Document[this._props.fieldKey + '_nativeHeight']) * (this._props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -218,10 +226,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 }
             } // else it's an HTMLfield
         } else if (this.webField && !this.dataDoc.text) {
-            const result = await WebRequest.get(Utils.CorsProxy(this.webField.href));
-            if (result) {
-                this.dataDoc.text = htmlToText.fromString(result.content);
-            }
+            WebRequest.get(Utils.CorsProxy(this.webField.href)) //
+                .then(result => result && (this.dataDoc.text = htmlToText.convert(result.content)));
         }
 
         this._disposers.scrollReaction = reaction(
@@ -236,7 +242,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             { fireImmediately: true }
         );
     }
-    @action componentWillUnmount() {
+    componentWillUnmount() {
         this._iframetimeout && clearTimeout(this._iframetimeout);
         this._iframetimeout = undefined;
         Object.values(this._disposers).forEach(disposer => disposer?.());
@@ -251,7 +257,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
         if (this._mainCont.current && selRange) {
-            if (this.dataDoc[this.props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this.props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
+            if (this.dataDoc[this._props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this._props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
             const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
@@ -259,7 +265,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (rect && rect.width !== this._mainCont.current.clientWidth) {
                     const annoBox = document.createElement('div');
                     annoBox.className = 'marqueeAnnotator-annotationBox';
-                    const scale = this._url ? 1 : this.props.ScreenToLocalTransform().Scale;
+                    const scale = this._url ? 1 : this._props.ScreenToLocalTransform().Scale;
                     // transforms the positions from screen onto the pdf div
                     annoBox.style.top = ((rect.top - mainrect.translateY) * scale + (this._url ? this._mainCont.current.scrollTop : NumCast(this.layoutDoc.layout_scrollTop))).toString();
                     annoBox.style.left = ((rect.left - mainrect.translateX) * scale).toString();
@@ -282,7 +288,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     focus = (anchor: Doc, options: DocFocusOptions) => {
         if (anchor !== this.Document && this._outerRef.current) {
-            const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
+            const windowHeight = this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
             const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), NumCast(anchor._height), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + NumCast(anchor._height), this._scrollHeight));
             if (scrollTo !== undefined) {
                 if (this._initialScroll === undefined) {
@@ -298,8 +304,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     getView = (doc: Doc) => {
-        if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this.props.DocumentView?.()));
-        if (this.Document.layout_fieldKey === 'layout_icon') this.props.DocumentView?.().iconify();
+        if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this._props.DocumentView?.()));
+        if (this.Document.layout_fieldKey === 'layout_icon') this._props.DocumentView?.().iconify();
         const webUrl = WebCast(doc.config_data)?.url;
         if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href);
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
@@ -307,11 +313,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
-        if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.Document[this._props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(false);
             return true;
         }
-        return this.props.addDocTab(doc, where);
+        return this._props.addDocTab(doc, where);
     };
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         let ele: Opt<HTMLDivElement> = undefined;
@@ -344,10 +350,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     iframeUp = (e: PointerEvent) => {
         this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
         this._textAnnotationCreator = undefined;
-        this.props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
+        this._props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
         if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
             const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
-            const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
+            const scale = (this._props.NativeDimScaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
                 this._selectionText = sel.toString();
@@ -355,7 +361,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
                 // Changing which document to add the annotation to (the currently selected WebBox)
-                GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+                GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
                 GPTPopup.Instance.addDoc = this.sidebarAddDocument;
             }
         }
@@ -393,23 +399,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
             (!sel.isCollapsed || this.marqueeing) && AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
             // Changing which document to add the annotation to (the currently selected WebBox)
-            GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+            GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
             GPTPopup.Instance.addDoc = this.sidebarAddDocument;
         }
     };
     @action
     iframeDown = (e: PointerEvent) => {
-        this.props.select(false);
+        this._props.select(false);
         const locpt = { 
-            x: (e.clientX / NumCast(this.Document.nativeWidth)) * this.props.PanelWidth(), 
-            y: ((e.clientY - NumCast(this.layoutDoc.layout_scrollTop))/ NumCast(this.Document.nativeHeight)) * this.props.PanelHeight() }; // prettier-ignore
-        const scrclick = this.props.DocumentView?.()._props.ScreenToLocalTransform().inverse().transformPoint(locpt.x, locpt.y)!;
-        const scrcent = this.props
+            x: (e.clientX / NumCast(this.Document.nativeWidth)) * this._props.PanelWidth(), 
+            y: ((e.clientY - NumCast(this.layoutDoc.layout_scrollTop))/ NumCast(this.Document.nativeHeight)) * this._props.PanelHeight() }; // prettier-ignore
+        const scrclick = this._props.DocumentView?.()._props.ScreenToLocalTransform().inverse().transformPoint(locpt.x, locpt.y)!;
+        const scrcent = this._props
             .DocumentView?.()
-            .props.ScreenToLocalTransform()
+            ._props.ScreenToLocalTransform()
             .inverse()
             .transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2)!;
-        const theclickoff = Utils.rotPt(scrclick[0] - scrcent[0], scrclick[1] - scrcent[1], -this.props.ScreenToLocalTransform().Rotate);
+        const theclickoff = Utils.rotPt(scrclick[0] - scrcent[0], scrclick[1] - scrcent[1], -this._props.ScreenToLocalTransform().Rotate);
         const theclick = [theclickoff.x + scrcent[0], theclickoff.y + scrcent[1]];
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
@@ -489,7 +495,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._scrollHeight = Math.max(this._scrollHeight, iframeContent.body.scrollHeight || 0);
                 if (this._scrollHeight) {
                     this.Document.nativeHeight = Math.min(NumCast(this.Document.nativeHeight), this._scrollHeight);
-                    this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(this.layoutDoc._width) * NumCast(this.dataDoc.nativeHeight)) / NumCast(this.dataDoc.nativeWidth));
+                    this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(this.layoutDoc._width) * NumCast(this.Document.nativeHeight)) / NumCast(this.Document.nativeWidth));
                 }
             };
             const swidth = Math.max(NumCast(this.layoutDoc.nativeWidth), iframeContent.body.scrollWidth || 0);
@@ -564,7 +570,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             clearStyleSheetRules(WebBox.webStyleSheet);
             this._scrollTimer = undefined;
             const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
-            if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
+            if (!LinkInfo.Instance?.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()))) {
                 this.layoutDoc.thumb = undefined;
                 this.layoutDoc.thumbScrollTop = undefined;
                 this.layoutDoc.thumbNativeWidth = undefined;
@@ -709,7 +715,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             funcs.push({
                 description: (!this.layoutDoc.layout_reflowHorizontal ? 'Force' : 'Prevent') + ' Reflow',
                 event: () => {
-                    const nw = !this.layoutDoc.layout_reflowHorizontal ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
+                    const nw = !this.layoutDoc.layout_reflowHorizontal ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this._props.NativeDimScaling?.() || 1);
                     this.layoutDoc.layout_reflowHorizontal = !nw;
                     if (nw) {
                         Doc.SetInPlace(this.layoutDoc, this.fieldKey + '_nativeWidth', nw, true);
@@ -734,7 +740,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (sel?.empty) sel.empty(); // Chrome
         else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         this.marqueeing = [e.clientX, e.clientY];
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -770,7 +776,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 setTimeout(() => {
                     // if menu comes up right away, the down event can still be active causing a menu item to be selected
                     this.specificContextMenu(undefined as any);
-                    this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
+                    this._props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
                 });
             }
         }
@@ -779,7 +785,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable lighttext = false;
 
     @computed get urlContent() {
-        if (this.props.ScreenToLocalTransform().Scale > 25) return <div></div>;
+        if (this._props.ScreenToLocalTransform().Scale > 25) return <div></div>;
         setTimeout(
             action(() => {
                 if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
@@ -788,7 +794,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._webPageHasBeenRendered = true;
             })
         );
-        const field = this.dataDoc[this.props.fieldKey];
+        const field = this.dataDoc[this._props.fieldKey];
         if (field instanceof HtmlField) {
             return (
                 <span
@@ -845,14 +851,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             e,
             action((e, down, delta) => {
                 this._draggingSidebar = true;
-                const localDelta = this.props
+                const localDelta = this._props
                     .ScreenToLocalTransform()
-                    .scale(this.props.NativeDimScaling?.() || 1)
+                    .scale(this._props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
                 const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
                 const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight']);
                 const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
+                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this._props.NativeDimScaling?.() || 1)) / nativeWidth;
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
@@ -916,7 +922,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     });
     @action
     onZoomWheel = (e: React.WheelEvent) => {
-        if (this.props.isContentActive(true)) {
+        if (this._props.isContentActive(true)) {
             e.stopPropagation();
         }
     };
@@ -924,14 +930,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this.SidebarShown) return 0;
         if (this._previewWidth) return WebBox.sidebarResizerWidth + WebBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
         const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
-        return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+        return WebBox.sidebarResizerWidth + nativeDiff * (this._props.NativeDimScaling?.() || 1);
     };
     _innerCollectionView: CollectionFreeFormView | undefined;
     zoomScaling = () => this._innerCollectionView?.zoomScaling() ?? 1;
     setInnerContent = (component: DocComponentView) => (this._innerCollectionView = component as CollectionFreeFormView);
 
     @computed get content() {
-        const interactive = this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
+        const interactive = this._props.isContentActive() && this._props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
         return (
             <div
                 className={'webBox-cont' + (interactive ? '-interactive' : '')}
@@ -959,7 +965,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 {this.inlineTextAnnotations
                     .sort((a, b) => NumCast(a.y) - NumCast(b.y))
                     .map(anno => (
-                        <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+                        <Annotation {...this._props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
                     ))}
             </div>
         );
@@ -969,13 +975,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     renderAnnotations = (childFilters: () => string[]) => (
         <CollectionFreeFormView
-            {...this.props}
+            {...this._props}
             setContentView={this.setInnerContent}
             NativeWidth={returnZero}
             NativeHeight={returnZero}
             originTopLeft={false}
             isAnnotationOverlayScrollable={true}
-            renderDepth={this.props.renderDepth + 1}
+            renderDepth={this._props.renderDepth + 1}
             isAnnotationOverlay={true}
             fieldKey={this.annotationKey}
             setPreviewCursor={this.setPreviewCursor}
@@ -1004,11 +1010,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get renderTransparentAnnotations() {
         return this.renderAnnotations(this.transparentFilter);
     }
-    childPointerEvents = () => (this.props.isContentActive() ? 'all' : undefined);
+    childPointerEvents = () => (this._props.isContentActive() ? 'all' : undefined);
     @computed get webpage() {
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
-        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
-        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as any);
+        const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
         return (
             <div
                 className="webBox-outerContent"
@@ -1021,7 +1027,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 onWheel={this.onZoomWheel}
                 onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
                 onPointerDown={this.onMarqueeDown}>
-                <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight > this.props.PanelHeight() && this._scrollHeight) || '100%', pointerEvents }}>
+                <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight > this._props.PanelHeight() && this._scrollHeight) || '100%', pointerEvents }}>
                     {this.content}
                     <div style={{ display: SnappingManager.GetCanEmbed() ? 'none' : undefined, mixBlendMode: 'multiply' }}>{this.renderTransparentAnnotations}</div>
                     {this.renderOpaqueAnnotations}
@@ -1033,7 +1039,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @computed get searchUI() {
         return (
-            <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
+            <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this._props.isContentActive() ? 'flex' : 'none' }}>
                 <div className="webBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
                     <button className="webBox-overlayButton" title={'search'} />
                     <input
@@ -1067,27 +1073,27 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
-    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
-    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
+    panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
+    panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
+    scrollXf = () => this._props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
-    transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
-    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.OpaqueBackgroundFilter])];
+    transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter];
+    opaqueFilter = () => [...this._props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.OpaqueBackgroundFilter])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (this.inlineTextAnnotations.includes(doc)) return 'none';
         }
-        return this.props.styleProvider?.(doc, props, property);
+        return this._props.styleProvider?.(doc, props, property);
     };
     pointerEvents = () =>
-        !this._draggingSidebar && this.props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
+        !this._draggingSidebar && this._props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
             ? 'all' //
             : 'none';
-    annotationPointerEvents = () => (this.props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
+    annotationPointerEvents = () => (this._props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
     render() {
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
-        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
-        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as any);
+        const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
         return (
             <div
                 className="webBox"
@@ -1096,7 +1102,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     pointerEvents: this.pointerEvents(), //
                     position: SnappingManager.GetIsDragging() ? 'absolute' : undefined,
                 }}>
-                <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }} />
+                <div className="webBox-background" style={{ backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) }} />
                 <div
                     className="webBox-container"
                     style={{
@@ -1115,9 +1121,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             anchorMenuClick={this.anchorMenuClick}
                             scrollTop={NumCast(this.layoutDoc.layout_scrollTop)}
                             annotationLayerScrollTop={0}
-                            scaling={this.props.NativeDimScaling}
+                            scaling={this._props.NativeDimScaling}
                             addDocument={this.addDocumentWrapper}
-                            docView={this.props.DocumentView!}
+                            docView={this._props.DocumentView!}
                             finishMarquee={this.finishMarquee}
                             savedAnnotations={this.savedAnnotationsCreator}
                             selectionText={this.selectionText}
@@ -1135,25 +1141,25 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     }}
                     onPointerDown={e => this.sidebarBtnDown(e, false)}
                 />
-                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
+                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this._props.PanelWidth()}%` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
-                        {...this.props}
+                        {...this._props}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                         fieldKey={this.fieldKey + '_' + this._urlHash}
                         Document={this.Document}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
                         setHeight={emptyFunction}
-                        nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.NativeDimScaling?.() || 1)}
+                        nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this._props.NativeDimScaling?.() || 1)}
                         showSidebar={this.SidebarShown}
                         sidebarAddDocument={this.sidebarAddDocument}
                         moveDocument={this.moveDocument}
                         removeDocument={this.removeDocument}
                     />
                 </div>
-                {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.sidebarHandle}
-                {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.searchUI}
+                {!this._props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.sidebarHandle}
+                {!this._props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.searchUI}
             </div>
         );
     }
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 19e14d5a7..e2cceb906 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, observable } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
@@ -8,7 +8,7 @@ import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
 import { Cast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
+import { copyProps, emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
@@ -70,10 +70,10 @@ export class DashFieldView {
             } catch {}
         });
     }
-    @action deselectNode() {
+    deselectNode() {
         this.dom.classList.remove('ProseMirror-selectednode');
     }
-    @action selectNode() {
+    selectNode() {
         this.dom.classList.add('ProseMirror-selectednode');
     }
 }
@@ -100,17 +100,24 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     @observable _dashDoc: Doc | undefined = undefined;
     @observable _expanded = false;
 
+    _prevProps: IDashFieldViewInternal;
+    @observable _props: IDashFieldViewInternal;
     constructor(props: IDashFieldViewInternal) {
         super(props);
-        this._fieldKey = this.props.fieldKey;
-        this._textBoxDoc = this.props.tbox.Document;
+        this._props = this._prevProps = props;
+        makeObservable(this);
+        this._fieldKey = this._props.fieldKey;
+        this._textBoxDoc = this._props.tbox.Document;
 
-        if (this.props.docId) {
-            DocServer.GetRefField(this.props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
+        if (this._props.docId) {
+            DocServer.GetRefField(this._props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
         } else {
-            this._dashDoc = this.props.tbox.Document;
+            this._dashDoc = this._props.tbox.Document;
         }
     }
+    componentDidUpdate(prevProps: Readonly<IDashFieldViewInternal>, prevState: Readonly<{}>, snapshot?: any): void {
+        copyProps(this);
+    }
     componentWillUnmount() {
         this._reactionDisposer?.();
     }
@@ -119,18 +126,18 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     // set the display of the field's value (checkbox for booleans, span of text for strings)
     @computed get fieldValueContent() {
         return !this._dashDoc ? null : (
-            <div onClick={action(e => (this._expanded = !this.props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: this.props.hideKey ? this.props.tbox.props.PanelWidth() - 20 : undefined }}>
+            <div onClick={action(e => (this._expanded = !this._props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: this._props.hideKey ? this._props.tbox._props.PanelWidth() - 20 : undefined }}>
                 <SchemaTableCell
                     Document={this._dashDoc}
                     col={0}
                     deselectCell={emptyFunction}
                     selectCell={emptyFunction}
-                    maxWidth={this.props.hideKey ? undefined : this.return100}
-                    columnWidth={this.props.hideKey ? () => this.props.tbox._props.PanelWidth() - 20 : returnZero}
+                    maxWidth={this._props.hideKey ? undefined : this.return100}
+                    columnWidth={this._props.hideKey ? () => this._props.tbox._props.PanelWidth() - 20 : returnZero}
                     selectedCell={() => [this._dashDoc!, 0]}
                     fieldKey={this._fieldKey}
                     rowHeight={returnZero}
-                    isRowActive={() => this._expanded && this.props.editable}
+                    isRowActive={() => this._expanded && this._props.editable}
                     padding={0}
                     getFinfo={emptyFunction}
                     setColumnValues={returnFalse}
@@ -145,7 +152,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     }
 
     createPivotForField = (e: React.MouseEvent) => {
-        let container = this.props.tbox._props.DocumentView?.()._props.docViewPath().lastElement();
+        let container = this._props.tbox._props.DocumentView?.()._props.docViewPath().lastElement();
         if (container) {
             const embedding = Doc.MakeEmbedding(container.Document);
             embedding._type_collection = CollectionViewType.Time;
@@ -157,7 +164,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
             list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
             embedding._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey;
-            this.props.tbox._props.addDocTab(embedding, OpenWhere.addRight);
+            this._props.tbox._props.addDocTab(embedding, OpenWhere.addRight);
         }
     };
 
@@ -175,17 +182,17 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             <div
                 className="dashFieldView"
                 style={{
-                    width: this.props.width,
-                    height: this.props.height,
-                    pointerEvents: this.props.tbox._props.isSelected() || this.props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
+                    width: this._props.width,
+                    height: this._props.height,
+                    pointerEvents: this._props.tbox._props.isSelected() || this._props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
                 }}>
-                {this.props.hideKey ? null : (
+                {this._props.hideKey ? null : (
                     <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
                         {this._fieldKey}
                     </span>
                 )}
 
-                {this.props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
+                {this._props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
             </div>
         );
     }
@@ -198,7 +205,7 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
         super(props);
         DashFieldViewMenu.Instance = this;
     }
-    @action
+
     showFields = (e: React.MouseEvent) => {
         DashFieldViewMenu.createFieldView(e);
         DashFieldViewMenu.Instance.fadeOut(true);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 244de7849..6b1df4560 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -55,7 +55,7 @@ import { StyleProp } from '../../StyleProvider';
 import { media_state } from '../AudioBox';
 import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
-import { LinkDocPreview } from '../LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../LinkDocPreview';
 import { PinProps, PresBox } from '../trails';
 import { DashDocCommentView } from './DashDocCommentView';
 import { DashDocView } from './DashDocView';
@@ -1855,7 +1855,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         e.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
     };
     onScroll = (e: React.UIEvent) => {
-        if (!LinkDocPreview.LinkInfo && this._scrollRef.current) {
+        if (!LinkInfo.Instance?.LinkInfo && this._scrollRef.current) {
             if (!this._props.dontSelectOnLoad) {
                 this._ignoreScroll = true;
                 this.layoutDoc._layout_scrollTop = this._scrollRef.current.scrollTop;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 4212d46eb..be8736525 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -3,7 +3,7 @@ import { EditorState, NodeSelection } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { Doc } from '../../../../fields/Doc';
 import { DocServer } from '../../../DocServer';
-import { LinkDocPreview } from '../LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../LinkDocPreview';
 import { FormattedTextBox } from './FormattedTextBox';
 import './FormattedTextBoxComment.scss';
 import { schema } from './schema_rts';
@@ -133,7 +133,7 @@ export class FormattedTextBoxComment {
             const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef;
             //nbef &&
             naft &&
-                LinkDocPreview.SetLinkInfo({
+                LinkInfo.SetLinkInfo({
                     docProps: textBox.props,
                     linkSrc: textBox.Document,
                     linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined,
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 62df77c0a..db815eae3 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,6 +1,5 @@
 import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import * as Pdfjs from 'pdfjs-dist';
 import 'pdfjs-dist/web/pdf_viewer.css';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Height } from '../../../fields/DocSymbols';
@@ -17,20 +16,20 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
-import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../nodes/LinkDocPreview';
 import { StyleProp } from '../StyleProvider';
 import { AnchorMenu } from './AnchorMenu';
 import { Annotation } from './Annotation';
 import { GPTPopup } from './GPTPopup/GPTPopup';
 import './PDFViewer.scss';
 import * as React from 'react';
-const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer');
-const pdfjsLib = require('pdfjs-dist');
+import * as Pdfjs from 'pdfjs-dist';
+import * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
 // The workerSrc property shall be specified.
-pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.16.105/build/pdf.worker.js';
+Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.0.269/build/pdf.worker.mjs';
 
 interface IViewerProps extends FieldViewProps {
     Document: Doc;
@@ -52,11 +51,6 @@ interface IViewerProps extends FieldViewProps {
 @observer
 export class PDFViewer extends React.Component<IViewerProps> {
     static _annotationStyle: any = addStyleSheet();
-    @observable private _pageSizes: { width: number; height: number }[] = [];
-    @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @observable private _textSelecting = true;
-    @observable private _showWaiting = true;
-    @observable private Index: number = -1;
 
     _prevProps: IViewerProps;
     @observable _props: IViewerProps;
@@ -65,6 +59,13 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this._prevProps = this._props = props;
         makeObservable(this);
     }
+
+    @observable _pageSizes: { width: number; height: number }[] = [];
+    @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+    @observable _textSelecting = true;
+    @observable _showWaiting = true;
+    @observable Index: number = -1;
+
     componentDidUpdate() {
         copyProps(this);
     }
@@ -101,7 +102,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
 
-    componentDidMount = async () => {
+    componentDidMount() {
         runInAction(() => (this._showWaiting = true));
         this.setupPdfJsViewer();
         this._mainCont.current?.addEventListener('scroll', e => ((e.target as any).scrollLeft = 0));
@@ -126,7 +127,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
             { fireImmediately: true }
         );
-    };
+    }
 
     componentWillUnmount = () => {
         Object.values(this._disposers).forEach(disposer => disposer?.());
@@ -262,7 +263,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
         document.removeEventListener('copy', this.copy);
         document.addEventListener('copy', this.copy);
-        const eventBus = new PDFJSViewer.EventBus(true);
+        const eventBus = new PDFJSViewer.EventBus();
         eventBus._on('pagesinit', this.pagesinit);
         eventBus._on(
             'pagerendered',
@@ -272,10 +273,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
         const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus });
         this._pdfViewer = new PDFJSViewer.PDFViewer({
             container: this._mainCont.current,
-            viewer: this._viewer.current,
+            viewer: this._viewer.current || undefined,
             linkService: pdfLinkService,
             findController: pdfFindController,
-            renderer: 'canvas',
             eventBus,
         });
         pdfLinkService.setViewer(this._pdfViewer);
@@ -313,7 +313,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     onScroll = (e: React.UIEvent<HTMLElement>) => {
         if (this._mainCont.current && !this._forcedScroll) {
             this._ignoreScroll = true; // the pdf scrolled, so we need to tell the Doc to scroll but we don't want the doc to then try to set the PDF scroll pos (which would interfere with the smooth scroll animation)
-            if (!LinkDocPreview.LinkInfo) {
+            if (!LinkInfo.Instance?.LinkInfo) {
                 this._props.layoutDoc._layout_scrollTop = this._mainCont.current.scrollTop;
             }
             this._ignoreScroll = false;
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index e419d3ac4..d634a241d 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -1,31 +1,26 @@
-import ApiManager, { Registration } from "./ApiManager";
-import { Method } from "../RouteManager";
-import RouteSubscriber from "../RouteSubscriber";
-import { existsSync, createReadStream, createWriteStream } from "fs";
-import * as Pdfjs from 'pdfjs-dist/legacy/build/pdf';
-import { createCanvas } from "canvas";
-const imageSize = require("probe-image-size");
-import * as express from "express";
-import * as path from "path";
-import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from "./UploadManager";
-import { red } from "colors";
-import { resolve } from "path";
+import ApiManager, { Registration } from './ApiManager';
+import { Method } from '../RouteManager';
+import RouteSubscriber from '../RouteSubscriber';
+import { existsSync, createReadStream, createWriteStream } from 'fs';
+//import * as Pdfjs from 'pdfjs-dist';
+import { createCanvas } from 'canvas';
+const imageSize = require('probe-image-size');
+import * as express from 'express';
+import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from './UploadManager';
+import { red } from 'colors';
+import { resolve } from 'path';
 
 export default class PDFManager extends ApiManager {
-
     protected initialize(register: Registration): void {
-
         register({
             method: Method.POST,
-            subscription: new RouteSubscriber("thumbnail"),
+            subscription: new RouteSubscriber('thumbnail'),
             secureHandler: async ({ req, res }) => {
                 const { coreFilename, pageNum, subtree } = req.body;
                 return getOrCreateThumbnail(coreFilename, pageNum, res, subtree);
-            }
+            },
         });
-
     }
-
 }
 
 async function getOrCreateThumbnail(coreFilename: string, pageNum: number, res: express.Response, subtree?: string): Promise<void> {
@@ -51,7 +46,7 @@ async function getOrCreateThumbnail(coreFilename: string, pageNum: number, res:
 }
 
 async function CreateThumbnail(coreFilename: string, pageNum: number, res: express.Response, subtree?: string) {
-    const part1 = subtree ?? "";
+    const part1 = subtree ?? '';
     const filename = `${part1}${coreFilename}.pdf`;
     const sourcePath = resolve(pathToDirectory(Directory.pdfs), filename);
     const documentProxy = await Pdfjs.getDocument(sourcePath).promise;
@@ -62,7 +57,7 @@ async function CreateThumbnail(coreFilename: string, pageNum: number, res: expre
     const renderContext = {
         canvasContext: context,
         canvasFactory: factory,
-        viewport
+        viewport,
     };
     await page.render(renderContext).promise;
     const pngStream = canvas.createPNGStream();
@@ -71,11 +66,11 @@ async function CreateThumbnail(coreFilename: string, pageNum: number, res: expre
     const out = createWriteStream(pngFile);
     pngStream.pipe(out);
     return new Promise<void>((resolve, reject) => {
-        out.on("finish", () => {
+        out.on('finish', () => {
             dispatchThumbnail(res, viewport, resolved);
             resolve();
         });
-        out.on("error", error => {
+        out.on('error', error => {
             console.log(red(`In PDF thumbnail creation, encountered the following error when piping ${pngFile}:`));
             console.log(error);
             reject();
@@ -87,30 +82,29 @@ function dispatchThumbnail(res: express.Response, { width, height }: Pdfjs.PageV
     res.send({
         path: clientPathToFile(Directory.pdf_thumbnails, thumbnailName),
         width,
-        height
+        height,
     });
 }
 
 class NodeCanvasFactory {
-
     create = (width: number, height: number) => {
         const canvas = createCanvas(width, height);
         const context = canvas.getContext('2d');
         return {
             canvas,
-            context
+            context,
         };
-    }
+    };
 
     reset = (canvasAndContext: any, width: number, height: number) => {
         canvasAndContext.canvas.width = width;
         canvasAndContext.canvas.height = height;
-    }
+    };
 
     destroy = (canvasAndContext: any) => {
         canvasAndContext.canvas.width = 0;
         canvasAndContext.canvas.height = 0;
         canvasAndContext.canvas = null;
         canvasAndContext.context = null;
-    }
+    };
 }
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index dd2a857d9..46f521bc9 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -607,19 +607,22 @@ export namespace DashUploadUtils {
         )); // prettier-ignore
 
         const fileIn = fs.readFileSync(sourcePath);
-        return autorotate.rotate(fileIn, { quality: 30 }).then(({ buffer }: { buffer: any }) =>
-            Jimp.read(buffer)
-                .then(async img => {
-                    await Promise.all( sizes.filter(({ width }) => width)  .map(({ width, suffix }) => 
+        let buffer: any;
+        try {
+            const { buffer2 } = await autorotate.rotate(fileIn, { quality: 30 });
+            buffer = buffer2;
+        } catch (e) {}
+        return Jimp.read(buffer ?? fileIn)
+            .then(async img => {
+                await Promise.all( sizes.filter(({ width }) => width)  .map(({ width, suffix }) => 
                         img = img.resize(width, Jimp.AUTO).write(outputPath(suffix))
                     )); // prettier-ignore
-                    return writtenFiles;
-                })
-                .catch(e => {
-                    console.log('ERROR' + e);
-                    return writtenFiles;
-                })
-        );
+                return writtenFiles;
+            })
+            .catch(e => {
+                console.log('ERROR' + e);
+                return writtenFiles;
+            });
     }
 
     /**
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 284aae338..710b0211e 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -8,6 +8,7 @@ declare module 'bezier-curve';
 declare module 'fit-curve';
 declare module 'react-audio-waveform';
 declare module 'iink-js';
+declare module 'pdfjs-dist/web/pdf_viewer';
 
 declare module 'reveal';
 declare module 'react-reveal';
diff --git a/webpack.config.js b/webpack.config.js
index 57dfd222b..c1464ac2a 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -9,17 +9,17 @@ const plugins = [
     new HtmlWebpackPlugin({
         title: 'Caching',
     }),
-    new ForkTsCheckerWebpackPlugin({
-        // typescript: {
-        //     //useTypescriptIncrementalApi: true,
-        //     //tslint: true,
-        //     //memoryLimit: 4096,
-        // },
-        tslint: true,
-        memoryLimit: 4096,
-        useTypescriptIncrementalApi: true,
-        tslintAutoFix: true,
-    }),
+    // new ForkTsCheckerWebpackPlugin({
+    //     typescript: {
+    //         // useTypescriptIncrementalApi: true,
+    //         // tslint: true,
+    //         memoryLimit: 4096,
+    //     },
+    //     // tslint: true,
+    //     // memoryLimit: 4096,
+    //     // useTypescriptIncrementalApi: true,
+    //     // tslintAutoFix: true,
+    // }),
     new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
     new webpack.ProvidePlugin({ process: 'process/browser' }),
     new webpack.HotModuleReplacementPlugin(),
-- 
cgit v1.2.3-70-g09d2


From 1cf241544f8063e3d71406238a584299b6ced794 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 13 Dec 2023 21:17:50 -0500
Subject: cleaned up props/_props handling by inherting from
 ObservableReactComponent

---
 .vscode/settings.json                              |  4 +-
 src/Utils.ts                                       |  7 ++-
 src/client/util/DocumentManager.ts                 |  3 +-
 src/client/util/SelectionManager.ts                | 22 +++----
 src/client/views/AntimodeMenu.tsx                  | 13 +----
 src/client/views/ContextMenu.tsx                   |  5 +-
 src/client/views/ContextMenuItem.tsx               | 15 ++---
 src/client/views/DashboardView.tsx                 |  4 +-
 src/client/views/DocComponent.tsx                  | 43 ++++++--------
 src/client/views/DocumentButtonBar.tsx             |  8 +--
 src/client/views/DocumentDecorations.tsx           |  6 +-
 src/client/views/EditableView.tsx                  | 14 ++---
 src/client/views/MainView.tsx                      | 47 +++++++--------
 src/client/views/MarqueeAnnotator.tsx              | 20 +++----
 src/client/views/ObservableReactComponent.tsx      | 21 +++++++
 src/client/views/OverlayView.tsx                   | 18 ++----
 src/client/views/PreviewCursor.tsx                 | 13 +++--
 src/client/views/PropertiesDocContextSelector.tsx  | 17 ++----
 src/client/views/PropertiesView.tsx                | 26 ++++-----
 src/client/views/ScriptingRepl.tsx                 | 29 +++-------
 src/client/views/SidebarAnnos.tsx                  | 25 ++++----
 .../views/collections/CollectionCarousel3DView.tsx |  6 +-
 .../views/collections/CollectionCarouselView.tsx   |  7 ++-
 .../views/collections/CollectionDockingView.tsx    | 15 ++---
 .../collections/CollectionMasonryViewFieldRow.tsx  | 18 +++---
 src/client/views/collections/CollectionMenu.tsx    |  2 +-
 .../views/collections/CollectionNoteTakingView.tsx |  8 ++-
 .../views/collections/CollectionPileView.tsx       |  7 ++-
 .../collections/CollectionStackedTimeline.tsx      | 22 ++-----
 .../views/collections/CollectionStackingView.tsx   | 15 ++---
 .../CollectionStackingViewFieldColumn.tsx          | 20 +++----
 src/client/views/collections/CollectionSubView.tsx | 19 +++---
 .../views/collections/CollectionTimeView.tsx       |  7 ++-
 .../views/collections/CollectionTreeView.tsx       | 16 ++----
 src/client/views/collections/CollectionView.tsx    | 26 ++++-----
 src/client/views/collections/TabDocView.tsx        | 41 ++++++-------
 src/client/views/collections/TreeView.tsx          | 23 ++++----
 .../CollectionFreeFormInfoState.tsx                | 20 +++----
 .../CollectionFreeFormInfoUI.tsx                   | 33 ++++-------
 .../CollectionFreeFormLinkView.tsx                 | 15 ++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 13 +----
 .../collectionFreeForm/MarqueeOptionsMenu.tsx      |  1 -
 .../collections/collectionFreeForm/MarqueeView.tsx | 23 ++++----
 .../collectionGrid/CollectionGridView.tsx          |  7 ++-
 .../collectionLinear/CollectionLinearView.tsx      | 19 ++----
 .../CollectionMulticolumnView.tsx                  | 11 +++-
 .../CollectionMultirowView.tsx                     | 11 +++-
 .../collectionSchema/CollectionSchemaView.tsx      | 37 +++++-------
 .../collections/collectionSchema/SchemaRowBox.tsx  | 14 ++---
 .../collectionSchema/SchemaTableCell.tsx           | 67 +++++++++-------------
 src/client/views/linking/LinkMenu.tsx              |  9 +--
 src/client/views/linking/LinkMenuItem.tsx          | 26 ++++-----
 src/client/views/nodes/AudioBox.tsx                | 20 +++----
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 29 ++++------
 src/client/views/nodes/ComparisonBox.tsx           | 20 ++-----
 src/client/views/nodes/DocumentContentsView.tsx    | 32 ++++-------
 src/client/views/nodes/DocumentIcon.tsx            | 26 ++++-----
 src/client/views/nodes/DocumentLinksButton.tsx     | 19 ++----
 src/client/views/nodes/DocumentView.tsx            | 36 ++++--------
 src/client/views/nodes/EquationBox.tsx             | 14 +----
 src/client/views/nodes/FontIconBox/FontIconBox.tsx | 14 ++---
 src/client/views/nodes/FunctionPlotBox.tsx         | 14 +----
 src/client/views/nodes/ImageBox.tsx                | 12 +---
 src/client/views/nodes/KeyValueBox.tsx             | 27 ++++-----
 src/client/views/nodes/KeyValuePair.tsx            | 29 ++++------
 src/client/views/nodes/LabelBox.tsx                | 14 ++---
 src/client/views/nodes/LinkAnchorBox.tsx           | 19 ++----
 src/client/views/nodes/LinkBox.tsx                 | 15 ++---
 src/client/views/nodes/LinkDocPreview.tsx          | 21 +++----
 src/client/views/nodes/MapBox/MapAnchorMenu.tsx    |  5 +-
 src/client/views/nodes/MapBox/MapBox.tsx           | 22 +++----
 src/client/views/nodes/PDFBox.tsx                  | 12 +---
 src/client/views/nodes/VideoBox.tsx                | 13 +----
 src/client/views/nodes/WebBox.tsx                  | 11 +---
 src/client/views/nodes/audio/AudioWaveform.tsx     | 17 ++----
 .../views/nodes/formattedText/DashFieldView.tsx    | 18 +++---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 15 ++---
 .../views/nodes/formattedText/RichTextMenu.tsx     | 44 +++++---------
 src/client/views/nodes/trails/PresElementBox.tsx   | 17 ++----
 src/client/views/pdf/AnchorMenu.tsx                | 14 ++---
 src/client/views/pdf/PDFViewer.tsx                 | 22 +++----
 81 files changed, 578 insertions(+), 911 deletions(-)
 create mode 100644 src/client/views/ObservableReactComponent.tsx

(limited to 'src/client/views/DashboardView.tsx')

diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8849f30dd..5239d22c0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -13,5 +13,7 @@
     "editor.defaultFormatter": "esbenp.prettier-vscode",
     "[typescriptreact]": {
         "editor.defaultFormatter": "esbenp.prettier-vscode"
-    }
+    },
+    "terminal.integrated.shellIntegration.showWelcome": false,
+    "terminal.integrated.shellIntegration.enabled": true
 }
diff --git a/src/Utils.ts b/src/Utils.ts
index d54760100..3a2bbf9a1 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -440,7 +440,12 @@ export namespace Utils {
         socket.on(message, (room: any) => handler(socket, room));
     }
 }
-export function copyProps(thing: { _prevProps: any; props: any; _props: any }) {
+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
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7fcda75cc..4816f3317 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,4 +1,4 @@
-import { action, computed, makeObservable, observable, ObservableSet, observe, reaction } from 'mobx';
+import { action, computed, makeObservable, observable, ObservableSet, observe } from 'mobx';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { AclAdmin, AclEdit, Animation } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
@@ -13,7 +13,6 @@ import { LightboxView } from '../views/LightboxView';
 import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
 import { KeyValueBox } from '../views/nodes/KeyValueBox';
 import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
-import { LoadingBox } from '../views/nodes/LoadingBox';
 import { PresBox } from '../views/nodes/trails';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 7cf0d62d0..4bd6647c0 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,4 +1,4 @@
-import { action, makeObservable, observable } from 'mobx';
+import { action, makeObservable, observable, runInAction } from 'mobx';
 import { Doc, Opt } from '../../fields/Doc';
 import { DocViews } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
@@ -26,13 +26,13 @@ export class SelectionManager {
     }
 
     @action
-    public static SelectSchemaViewDoc(doc: Opt<Doc>, deselectAllFirst?: boolean) {
+    public static SelectSchemaViewDoc = (doc: Opt<Doc>, deselectAllFirst?: boolean) => {
         if (deselectAllFirst) this.DeselectAll();
         this.Instance.SelectedSchemaDocument = doc;
-    }
+    };
 
     @action
-    public static SelectView(docView: DocumentView, extendSelection: boolean): void {
+    public static SelectView = (docView: DocumentView | undefined, extendSelection: boolean): void => {
         if (!docView) this.DeselectAll();
         else if (!docView.SELECTED) {
             if (!extendSelection) this.DeselectAll();
@@ -40,29 +40,29 @@ export class SelectionManager {
             docView.SELECTED = true;
             docView._props.whenChildContentsActiveChanged(true);
         }
-    }
+    };
 
     @action
-    public static DeselectView(docView?: DocumentView): void {
+    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);
         }
-    }
-    @action
-    public static DeselectAll(except?: Doc): void {
+    };
+
+    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;
+        runInAction(() => (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
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index f89624941..4c82b10fd 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -1,32 +1,25 @@
 import { action, makeObservable, observable, runInAction } from 'mobx';
 import * as React from 'react';
 import { SettingsManager } from '../util/SettingsManager';
-import { copyProps } from '../../Utils';
 import './AntimodeMenu.scss';
+import { ObservableReactComponent } from './ObservableReactComponent';
 export interface AntimodeMenuProps {}
 
 /**
  * This is an abstract class that serves as the base for a PDF-style or Marquee-style
  * menu. To use this class, look at PDFMenu.tsx or MarqueeOptionsMenu.tsx for an example.
  */
-export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Component<T, {}> {
+export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends ObservableReactComponent<T> {
     protected _offsetY: number = 0;
     protected _offsetX: number = 0;
     protected _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     protected _dragging: boolean = false;
 
-    _prevProps: React.PropsWithChildren<T>;
-    @observable _props: React.PropsWithChildren<T>;
-    constructor(props: React.PropsWithChildren<T>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     @observable protected _top: number = -300;
     @observable protected _left: number = -300;
     @observable protected _opacity: number = 0;
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 118a2c5f5..e55bf24a1 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -6,9 +6,10 @@ import { StrCast } from '../../fields/Types';
 import { SettingsManager } from '../util/SettingsManager';
 import './ContextMenu.scss';
 import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem';
+import { ObservableReactComponent } from './ObservableReactComponent';
 
 @observer
-export class ContextMenu extends React.Component {
+export class ContextMenu extends ObservableReactComponent<{}> {
     static Instance: ContextMenu;
 
     private _ignoreUp = false;
@@ -36,8 +37,8 @@ export class ContextMenu extends React.Component {
 
     constructor(props: any) {
         super(props);
-        makeObservable(this);
         ContextMenu.Instance = this;
+        makeObservable(this);
     }
 
     public setIgnoreEvents(ignore: boolean) {
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 59b223c14..3c9d821a9 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -5,7 +5,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { UndoManager } from '../util/UndoManager';
 import { SettingsManager } from '../util/SettingsManager';
-import { copyProps } from '../../Utils';
+import { ObservableReactComponent } from './ObservableReactComponent';
 
 export interface OriginalMenuProps {
     description: string;
@@ -27,24 +27,17 @@ export interface SubmenuProps {
 export type ContextMenuProps = OriginalMenuProps | SubmenuProps;
 
 @observer
-export class ContextMenuItem extends React.Component<ContextMenuProps & { selected?: boolean }> {
+export class ContextMenuItem extends ObservableReactComponent<ContextMenuProps & { selected?: boolean }> {
     @observable private _items: Array<ContextMenuProps> = [];
     @observable private overItem = false;
 
-    _prevProps: ContextMenuProps & { selected?: boolean };
-    @observable _props: ContextMenuProps & { selected?: boolean };
-    constructor(props: ContextMenuProps & { selected?: boolean }) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     componentDidMount() {
-        runInAction(() => this._items.length = 0);
+        runInAction(() => (this._items.length = 0));
         if ((this.props as SubmenuProps)?.subitems) {
             (this.props as SubmenuProps).subitems?.forEach(i => runInAction(() => this._items.push(i)));
         }
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 35c0d617b..85cee83d4 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -27,6 +27,7 @@ import './DashboardView.scss';
 import { Colors } from './global/globalEnums';
 import { MainViewModal } from './MainViewModal';
 import { ButtonType } from './nodes/FontIconBox/FontIconBox';
+import { ObservableReactComponent } from './ObservableReactComponent';
 
 enum DashboardGroup {
     MyDashboards,
@@ -36,9 +37,8 @@ enum DashboardGroup {
 // DashboardView is the view with the dashboard previews, rendered when the app first loads
 
 @observer
-export class DashboardView extends React.Component {
+export class DashboardView extends ObservableReactComponent<{}> {
     public static _urlState: HistoryUtil.DocUrl;
-
     constructor(props: any) {
         super(props);
         makeObservable(this);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 2ce0c085a..235b0dc68 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,17 +1,17 @@
-import { action, computed, makeObservable, observable, runInAction, untracked } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
+import * as React from 'react';
+import { returnFalse } from '../../Utils';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
-import { Cast } from '../../fields/Types';
 import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util';
-import { returnFalse } from '../../Utils';
-import { DocUtils } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
-import { DocumentView } from './nodes/DocumentView';
-import * as React from 'react';
+import { DocUtils } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
+import { ObservableReactComponent } from './ObservableReactComponent';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
+import { DocumentView } from './nodes/DocumentView';
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 export interface DocComponentProps {
@@ -20,18 +20,14 @@ export interface DocComponentProps {
     LayoutTemplateString?: string;
 }
 export function DocComponent<P extends DocComponentProps>() {
-    class Component extends React.Component<React.PropsWithChildren<P>> {
-        @observable _props!: React.PropsWithChildren<P>;
-        constructor(props: React.PropsWithChildren<P>) {
+    class Component extends ObservableReactComponent<React.PropsWithChildren<P>> {
+        constructor(props: any) {
             super(props);
-            this._props = props;
             makeObservable(this);
         }
-        componentDidUpdate() {
-            // untracked(() => (this._props = this.props));
-        }
+
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
-        @computed get Document() {
+        get Document() {
             return this._props.Document;
         }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
@@ -57,24 +53,18 @@ interface ViewBoxBaseProps {
     renderDepth: number;
 }
 export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
-    class Component extends React.Component<React.PropsWithChildren<P>> {
-        @observable _props: React.PropsWithChildren<P>;
-        constructor(props: React.PropsWithChildren<P>) {
-            super(props);
-            this._props = props;
-            makeObservable(this);
-        }
+    class Component extends ObservableReactComponent<React.PropsWithChildren<P>> {
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         //@computed get Document(): T { return schemaCtor(this.props.Document); }
         get Document() {
             return this._props.Document;
         }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        get layoutDoc() {
+        @computed get layoutDoc() {
             return Doc.Layout(this.Document);
         }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
-        get dataDoc() {
+        @computed get dataDoc() {
             return this.Document.isTemplateForField || this.Document.isTemplateDoc ? this._props.TemplateDataDocument ?? this.Document[DocData] : this.Document[DocData];
         }
         // key where data is stored
@@ -99,13 +89,12 @@ export interface ViewBoxAnnotatableProps {
     isAnnotationOverlay?: boolean;
 }
 export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() {
-    class Component extends React.Component<React.PropsWithChildren<P>> {
-        @observable _props: React.PropsWithChildren<P>;
-        constructor(props: React.PropsWithChildren<P>) {
+    class Component extends ObservableReactComponent<React.PropsWithChildren<P>> {
+        constructor(props: any) {
             super(props);
-            this._props = props;
             makeObservable(this);
         }
+
         @observable _annotationKeySuffix = () => 'annotations';
         @observable _isAnyChildContentActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index a8ad4150e..50ca3af06 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -28,6 +28,7 @@ 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;
@@ -42,12 +43,11 @@ enum UtilityButtonState {
 }
 
 @observer
-export class DocumentButtonBar extends React.Component<{ views: () => (DocumentView | undefined)[]; stack?: any }, {}> {
+export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: any }> {
     private _dragRef = React.createRef<HTMLDivElement>();
     private _pullAnimating = false;
     private _pushAnimating = false;
     private _pullColorAnimating = false;
-
     @observable private pushIcon: IconProp = 'arrow-alt-circle-up';
     @observable private pullIcon: IconProp = 'arrow-alt-circle-down';
     @observable private pullColor: string = 'white';
@@ -60,12 +60,10 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     public static hasPushedHack = false;
     public static hasPulledHack = false;
 
-    @observable _props: { views: () => (DocumentView | undefined)[] };
     constructor(props: { views: () => (DocumentView | undefined)[] }) {
         super(props);
-        this._props = props;
+        DocumentButtonBar.Instance = this;
         makeObservable(this);
-        runInAction(() => (DocumentButtonBar.Instance = this));
     }
 
     public startPullOutcome = action((success: boolean) => {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 8bcc1cb56..7003485d2 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -34,6 +34,7 @@ import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import * as React from 'react';
+import { ObservableReactComponent } from './ObservableReactComponent';
 
 interface DocumentDecorationsProps {
     PanelWidth: number;
@@ -42,7 +43,7 @@ interface DocumentDecorationsProps {
     boundsTop: number;
 }
 @observer
-export class DocumentDecorations extends React.Component<DocumentDecorationsProps> {
+export class DocumentDecorations extends ObservableReactComponent<DocumentDecorationsProps> {
     static Instance: DocumentDecorations;
     private _resizeHdlId = '';
     private _keyinput = React.createRef<HTMLInputElement>();
@@ -66,11 +67,10 @@ export class DocumentDecorations extends React.Component<DocumentDecorationsProp
     @observable private _showRotCenter = false; // whether to show a draggable green dot that represents the center of rotation
     @observable private _rotCenter = [0, 0]; // the center of rotation in object coordinates (0,0) = object center (not top left!)
 
-    @observable _props: React.PropsWithChildren<DocumentDecorationsProps>;
     constructor(props: React.PropsWithChildren<DocumentDecorationsProps>) {
         super(props);
-        this._props = props;
         makeObservable(this);
+
         DocumentDecorations.Instance = this;
         document.addEventListener('pointermove', // show decorations whenever pointer moves outside of selection bounds.
             action(e => {
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 3db77be71..836a184eb 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -1,13 +1,13 @@
-import * as React from 'react';
 import { action, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import * as Autosuggest from 'react-autosuggest';
 import { ObjectField } from '../../fields/ObjectField';
 import './EditableView.scss';
 import { DocumentIconContainer } from './nodes/DocumentIcon';
-import { OverlayView } from './OverlayView';
-import { copyProps } from '../../Utils';
 import { FieldView, FieldViewProps } from './nodes/FieldView';
+import { ObservableReactComponent } from './ObservableReactComponent';
+import { OverlayView } from './OverlayView';
 
 export interface EditableProps {
     /**
@@ -60,18 +60,15 @@ export interface EditableProps {
  * of the content, and set the value based on the entered string.
  */
 @observer
-export class EditableView extends React.Component<EditableProps> {
+export class EditableView extends ObservableReactComponent<EditableProps> {
     private _ref = React.createRef<HTMLDivElement>();
     private _inputref: HTMLInputElement | HTMLTextAreaElement | null = null;
     _overlayDisposer?: () => void;
     _editingDisposer?: IReactionDisposer;
     @observable _editing: boolean = false;
 
-    _prevProps: EditableProps;
-    @observable _props: EditableProps;
     constructor(props: EditableProps) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         this._editing = this._props.editing ? true : false;
     }
@@ -96,8 +93,7 @@ export class EditableView extends React.Component<EditableProps> {
         );
     }
 
-    componentDidUpdate() {
-        copyProps(this);
+    componentDidUpdate(prevProps: Readonly<EditableProps>) {
         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/MainView.tsx b/src/client/views/MainView.tsx
index 4f96b9234..f65675792 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,20 +8,19 @@ import { observer } from 'mobx-react';
 import 'normalize.css';
 import * as React from 'react';
 import '../../../node_modules/browndash-components/dist/styles/global.min.css';
+import { Utils, emptyFunction, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../Utils';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { DocCast, StrCast } from '../../fields/Types';
-import { emptyFunction, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils';
-import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
-import { Docs } from '../documents/Documents';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { Docs } from '../documents/Documents';
 import { CaptureManager } from '../util/CaptureManager';
 import { DocumentManager } from '../util/DocumentManager';
 import { DragManager } from '../util/DragManager';
 import { GroupManager } from '../util/GroupManager';
 import { HistoryUtil } from '../util/History';
 import { Hypothesis } from '../util/HypothesisUtils';
-import { ReportManager } from '../util/reportManager/ReportManager';
 import { RTFMarkup } from '../util/RTFMarkup';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { SelectionManager } from '../util/SelectionManager';
@@ -30,13 +29,7 @@ import { SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { Transform } from '../util/Transform';
-import { TimelineMenu } from './animationtimeline/TimelineMenu';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
-import { CollectionLinearView } from './collections/collectionLinear';
-import { CollectionMenu } from './collections/CollectionMenu';
-import { TabDocView } from './collections/TabDocView';
-import './collections/TreeView.scss';
+import { ReportManager } from '../util/reportManager/ReportManager';
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
 import { DashboardView } from './DashboardView';
@@ -45,15 +38,24 @@ import { DocumentDecorations } from './DocumentDecorations';
 import { GestureOverlay } from './GestureOverlay';
 import { KeyManager } from './GlobalKeyHandler';
 import { LightboxView } from './LightboxView';
-import { LinkMenu } from './linking/LinkMenu';
 import './MainView.scss';
+import { ObservableReactComponent } from './ObservableReactComponent';
+import { OverlayView } from './OverlayView';
+import { PreviewCursor } from './PreviewCursor';
+import { PropertiesView } from './PropertiesView';
+import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
+import { TimelineMenu } from './animationtimeline/TimelineMenu';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionMenu } from './collections/CollectionMenu';
+import { TabDocView } from './collections/TabDocView';
+import './collections/TreeView.scss';
+import { CollectionFreeFormLinksView } from './collections/collectionFreeForm';
+import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
+import { CollectionLinearView } from './collections/collectionLinear';
+import { LinkMenu } from './linking/LinkMenu';
 import { AudioBox } from './nodes/AudioBox';
 import { DocButtonState } from './nodes/DocumentLinksButton';
 import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
-import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView';
-import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
-import GenerativeFill from './nodes/generativeFill/GenerativeFill';
 import { ImageBox } from './nodes/ImageBox';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview';
@@ -61,20 +63,19 @@ import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu';
 import { MapBox } from './nodes/MapBox/MapBox';
 import { RadialMenu } from './nodes/RadialMenu';
 import { TaskCompletionBox } from './nodes/TaskCompletedBox';
+import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
+import GenerativeFill from './nodes/generativeFill/GenerativeFill';
 import { PresBox } from './nodes/trails';
-import { OverlayView } from './OverlayView';
 import { AnchorMenu } from './pdf/AnchorMenu';
 import { GPTPopup } from './pdf/GPTPopup/GPTPopup';
-import { PreviewCursor } from './PreviewCursor';
-import { PropertiesView } from './PropertiesView';
-import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
-import { CollectionFreeFormLinksView } from './collections/collectionFreeForm';
 const { default: { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } } = require('./global/globalCssVariables.module.scss'); // prettier-ignore
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
-export class MainView extends React.Component {
+export class MainView extends ObservableReactComponent<{}> {
     public static Instance: MainView;
     public static Live: boolean = false;
     private _docBtnRef = React.createRef<HTMLDivElement>();
@@ -1064,7 +1065,7 @@ export class MainView extends React.Component {
                 <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
                 <CollectionFreeFormLinksView />
                 <OverlayView />
-                {this.mapBoxHack}
+                {/* {this.mapBoxHack} */}
                 <GPTPopup key="gptpopup" />
                 <GenerativeFill imageEditorOpen={ImageBox.imageEditorOpen} imageEditorSource={ImageBox.imageEditorSource} imageRootDoc={ImageBox.imageRootDoc} addDoc={ImageBox.addDoc} />
                 {/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 3f0db4258..2e27d1f70 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -15,6 +15,7 @@ import './MarqueeAnnotator.scss';
 import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { AnchorMenu } from './pdf/AnchorMenu';
+import { ObservableReactComponent } from './ObservableReactComponent';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
@@ -38,23 +39,18 @@ export interface MarqueeAnnotatorProps {
     highlightDragSrcColor?: string;
 }
 @observer
-export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
+export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorProps> {
     private _start: { x: number; y: number } = { x: 0, y: 0 };
-    @observable private _width: number = 0;
-    @observable private _height: number = 0;
-    @computed get top() { return Math.min(this._start.y, this._start.y + this._height); } // prettier-ignore
-    @computed get left() { return Math.min(this._start.x, this._start.x + this._width);} // prettier-ignore
 
-    _prevProps: React.PropsWithChildren<MarqueeAnnotatorProps>;
-    @observable _props: React.PropsWithChildren<MarqueeAnnotatorProps>;
-    constructor(props: React.PropsWithChildren<MarqueeAnnotatorProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidMount() {
-        copyProps(this);
-    }
+
+    @observable private _width: number = 0;
+    @observable private _height: number = 0;
+    @computed get top() { return Math.min(this._start.y, this._start.y + this._height); } // prettier-ignore
+    @computed get left() { return Math.min(this._start.x, this._start.x + this._width);} // prettier-ignore
 
     @action
     static clearAnnotations(savedAnnotations: ObservableMap<number, HTMLDivElement[]>) {
diff --git a/src/client/views/ObservableReactComponent.tsx b/src/client/views/ObservableReactComponent.tsx
new file mode 100644
index 000000000..2d8dc9af9
--- /dev/null
+++ b/src/client/views/ObservableReactComponent.tsx
@@ -0,0 +1,21 @@
+import { 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
+ * menu. To use this class, look at PDFMenu.tsx or MarqueeOptionsMenu.tsx for an example.
+ */
+export abstract class ObservableReactComponent<T> extends React.Component<T, {}> {
+    @observable _props: React.PropsWithChildren<T>;
+    constructor(props: React.PropsWithChildren<T>) {
+        super(props);
+        this._props = props;
+        makeObservable(this);
+    }
+    componentDidUpdate(prevProps: Readonly<T>): void {
+        copyProps(this, prevProps);
+    }
+}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 673432a60..915c3c18f 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,9 +1,9 @@
-import { action, computed, makeObservable, observable, toJS } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import ReactLoading from 'react-loading';
-import { Utils, copyProps, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue, setupMoveUpEvents } from '../../Utils';
 import { Doc } from '../../fields/Doc';
 import { Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
@@ -12,6 +12,7 @@ import { DocumentType } from '../documents/DocumentTypes';
 import { DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { LightboxView } from './LightboxView';
+import { ObservableReactComponent } from './ObservableReactComponent';
 import './OverlayView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
 import { DocumentView, DocumentViewInternal } from './nodes/DocumentView';
@@ -34,19 +35,14 @@ export interface OverlayWindowProps {
 }
 
 @observer
-export class OverlayWindow extends React.Component<OverlayWindowProps> {
+export class OverlayWindow extends ObservableReactComponent<OverlayWindowProps> {
     @observable x: number = 0;
     @observable y: number = 0;
     @observable width: number = 0;
     @observable height: number = 0;
-
-    _prevProps: OverlayWindowProps;
-    @observable _props: OverlayWindowProps;
     constructor(props: OverlayWindowProps) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
-
         const opts = props.overlayOptions;
         this.x = opts.x;
         this.y = opts.y;
@@ -54,10 +50,6 @@ export class OverlayWindow extends React.Component<OverlayWindowProps> {
         this.height = opts.height || 200;
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     onPointerDown = (_: React.PointerEvent) => {
         document.removeEventListener('pointermove', this.onPointerMove);
         document.removeEventListener('pointerup', this.onPointerUp);
@@ -115,7 +107,7 @@ export class OverlayWindow extends React.Component<OverlayWindowProps> {
 }
 
 @observer
-export class OverlayView extends React.Component {
+export class OverlayView extends ObservableReactComponent<{}> {
     public static Instance: OverlayView;
     @observable.shallow _elements: JSX.Element[] = [];
 
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 6e1a2cfb1..5ec9a7d46 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -1,17 +1,18 @@
 import { action, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, Opt } from '../../fields/Doc';
 import { lightOrDark, returnFalse } from '../../Utils';
-import { Docs, DocumentOptions, DocUtils } from '../documents/Documents';
+import { Doc, Opt } from '../../fields/Doc';
+import { DocUtils, Docs, DocumentOptions } from '../documents/Documents';
 import { ImageUtils } from '../util/Import & Export/ImageUtils';
 import { Transform } from '../util/Transform';
-import { undoBatch, UndoManager } from '../util/UndoManager';
-import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { UndoManager, undoBatch } from '../util/UndoManager';
+import { ObservableReactComponent } from './ObservableReactComponent';
 import './PreviewCursor.scss';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 
 @observer
-export class PreviewCursor extends React.Component<{}> {
+export class PreviewCursor extends ObservableReactComponent<{}> {
     static _instance: PreviewCursor;
     public static get Instance() {
         return PreviewCursor._instance;
@@ -28,8 +29,8 @@ export class PreviewCursor extends React.Component<{}> {
     public Doc: Opt<Doc>;
     constructor(props: any) {
         super(props);
-        PreviewCursor._instance = this;
         makeObservable(this);
+        PreviewCursor._instance = this;
         this._clickPoint = observable([0, 0]);
         document.addEventListener('keydown', this.onKeyPress);
         document.addEventListener('paste', this.paste, true);
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 5bde9d3c4..a710e7816 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -1,14 +1,14 @@
-import { computed, makeObservable, observable } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { Cast, StrCast } from '../../fields/Types';
 import { DocFocusOrOpen } from '../util/DocumentManager';
+import { ObservableReactComponent } from './ObservableReactComponent';
+import './PropertiesDocContextSelector.scss';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { DocumentView, OpenWhere } from './nodes/DocumentView';
-import './PropertiesDocContextSelector.scss';
-import { copyProps } from '../../Utils';
 
 type PropertiesDocContextSelectorProps = {
     DocView?: DocumentView;
@@ -18,17 +18,12 @@ type PropertiesDocContextSelectorProps = {
 };
 
 @observer
-export class PropertiesDocContextSelector extends React.Component<PropertiesDocContextSelectorProps> {
-    _prevProps: PropertiesDocContextSelectorProps;
-    @observable _props: PropertiesDocContextSelectorProps;
-    constructor(props: PropertiesDocContextSelectorProps) {
+export class PropertiesDocContextSelector extends ObservableReactComponent<PropertiesDocContextSelectorProps> {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
+
     @computed get _docs() {
         if (!this._props.DocView) return [];
         const target = this._props.DocView._props.Document;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 442d09739..c857c2d6b 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,14 +1,15 @@
-import * as React from 'react';
 import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox, Tooltip } from '@mui/material';
 import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Type } from 'browndash-components';
 import { concat } from 'lodash';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { ColorResult, SketchPicker } from 'react-color';
 import * as Icons from 'react-icons/bs'; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
 import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
 import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
@@ -16,8 +17,7 @@ import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { ComputedField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
-import { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils';
+import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
@@ -26,19 +26,20 @@ import { SelectionManager } from '../util/SelectionManager';
 import { SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
 import { Transform } from '../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
+import { UndoManager, undoBatch, undoable } from '../util/UndoManager';
 import { EditableView } from './EditableView';
 import { FilterPanel } from './FilterPanel';
 import { InkStrokeProperties } from './InkStrokeProperties';
-import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
-import { KeyValueBox } from './nodes/KeyValueBox';
-import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
+import { ObservableReactComponent } from './ObservableReactComponent';
 import { PropertiesButtons } from './PropertiesButtons';
 import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector';
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import { PropertiesSection } from './PropertiesSection';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
+import { KeyValueBox } from './nodes/KeyValueBox';
+import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface PropertiesViewProps {
@@ -49,15 +50,12 @@ interface PropertiesViewProps {
 }
 
 @observer
-export class PropertiesView extends React.Component<PropertiesViewProps> {
+export class PropertiesView extends ObservableReactComponent<PropertiesViewProps> {
     private _widthUndo?: UndoManager.Batch;
 
     public static Instance: PropertiesView | undefined;
-    _prevProps: React.PropsWithChildren<PropertiesViewProps>;
-    @observable _props: React.PropsWithChildren<PropertiesViewProps>;
     constructor(props: React.PropsWithChildren<PropertiesViewProps>) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         PropertiesView.Instance = this;
     }
@@ -120,10 +118,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     componentWillUnmount() {
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index 8251d20dc..5f20bc745 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable, makeObservable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DocumentManager } from '../util/DocumentManager';
@@ -7,29 +7,23 @@ import { CompileScript, Transformer, ts } from '../util/Scripting';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { SettingsManager } from '../util/SettingsManager';
 import { undoable } from '../util/UndoManager';
-import { DocumentIconContainer } from './nodes/DocumentIcon';
+import { ObservableReactComponent } from './ObservableReactComponent';
 import { OverlayView } from './OverlayView';
 import './ScriptingRepl.scss';
-import { copyProps } from '../../Utils';
+import { DocumentIconContainer } from './nodes/DocumentIcon';
 
 interface ReplProps {
     scrollToBottom: () => void;
     value: { [key: string]: any };
     name?: string;
 }
-export class ScriptingObjectDisplay extends React.Component<ReplProps> {
+export class ScriptingObjectDisplay extends ObservableReactComponent<ReplProps> {
     @observable collapsed = true;
 
-    _prevProps: ReplProps;
-    @observable _props: ReplProps;
-    constructor(props: ReplProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate(): void {
-        copyProps(this);
-    }
 
     @action
     toggle = () => {
@@ -84,17 +78,12 @@ interface replValueProps {
     name?: string;
 }
 @observer
-export class ScriptingValueDisplay extends React.Component<replValueProps> {
-    _prevProps: replValueProps;
-    @observable _props: replValueProps;
-    constructor(props: replValueProps) {
+export class ScriptingValueDisplay extends ObservableReactComponent<replValueProps> {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
+
     render() {
         const val = this._props.name ? this._props.value[this._props.name] : this._props.value;
         if (typeof val === 'object') {
@@ -126,7 +115,7 @@ export class ScriptingValueDisplay extends React.Component<replValueProps> {
 }
 
 @observer
-export class ScriptingRepl extends React.Component {
+export class ScriptingRepl extends ObservableReactComponent<{}> {
     constructor(props: any) {
         super(props);
         makeObservable(this);
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 19473de2b..0f4a4260c 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -1,22 +1,23 @@
-import { computed, makeObservable, observable } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, returnAll, returnFalse, returnOne, returnZero } from '../../Utils';
 import { Doc, DocListCast, Field, FieldResult, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { RichTextField } from '../../fields/RichTextField';
 import { DocCast, NumCast, StrCast } from '../../fields/Types';
-import { copyProps, emptyFunction, returnAll, returnFalse, returnOne, returnTrue, returnZero } from '../../Utils';
-import { Docs, DocUtils } from '../documents/Documents';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { DocUtils, Docs } from '../documents/Documents';
 import { LinkManager } from '../util/LinkManager';
 import { SearchUtil } from '../util/SearchUtil';
 import { Transform } from '../util/Transform';
+import { ObservableReactComponent } from './ObservableReactComponent';
+import './SidebarAnnos.scss';
+import { StyleProp } from './StyleProvider';
 import { CollectionStackingView } from './collections/CollectionStackingView';
 import { FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import './SidebarAnnos.scss';
-import { StyleProp } from './StyleProvider';
-import * as React from 'react';
 
 interface ExtraProps {
     fieldKey: string;
@@ -34,18 +35,12 @@ interface ExtraProps {
     moveDocument: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean, annotationKey?: string) => boolean;
 }
 @observer
-export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
-    _prevProps: FieldViewProps & ExtraProps;
-    @observable _props: FieldViewProps & ExtraProps;
-    constructor(props: FieldViewProps & ExtraProps) {
+export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & ExtraProps> {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
-        // this._props.dataDoc[this.sidebarKey] = new List<Doc>();  // bcz: can't do this here.  it blows away existing things and isn't a robust solution for making sure the field exists -- instead this should happen when the document is created and/or shared
-    }
-    componentDidUpdate() {
-        copyProps(this);
     }
+
     _stackRef = React.createRef<CollectionStackingView>();
     @computed get allMetadata() {
         const keys = new Map<string, FieldResult<Field>>();
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index f03e38850..a11c53d4d 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { computed } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Utils, returnFalse, returnZero } from '../../../Utils';
@@ -19,6 +19,10 @@ export class CollectionCarousel3DView extends CollectionSubView() {
     @computed get scrollSpeed() {
         return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed
     }
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
 
     private _dropDisposer?: DragManager.DragDropDisposer;
 
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 299a4d5d3..5d09f14ef 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { computed } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { StopEvent, emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
@@ -16,6 +16,11 @@ import { CollectionSubView } from './CollectionSubView';
 export class CollectionCarouselView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     componentWillUnmount() {
         this._dropDisposer?.();
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4ddf9e69b..0d3ff8bba 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,5 +1,6 @@
-import { action, IReactionDisposer, makeObservable, observable, override, reaction, runInAction, untracked } from 'mobx';
+import { action, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
 import * as GoldenLayout from '../../../client/goldenLayout';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
@@ -10,7 +11,7 @@ import { List } from '../../../fields/List';
 import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { GetEffectiveAcl, inheritParentAcls } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, copyProps, emptyFunction, incrementTitleCopy } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, incrementTitleCopy } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
@@ -19,6 +20,7 @@ import { DragManager } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
 import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
 import { DashboardView } from '../DashboardView';
 import { LightboxView } from '../LightboxView';
@@ -30,8 +32,6 @@ import './CollectionDockingView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { TabDocView } from './TabDocView';
-import * as React from 'react';
-import { SettingsManager } from '../../util/SettingsManager';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
@@ -63,11 +63,8 @@ export class CollectionDockingView extends CollectionSubView() {
     private _goldenLayout: any = null;
     static _highlightStyleSheet: any = addStyleSheet();
 
-    _prevProps: SubCollectionViewProps;
-    @override _props: SubCollectionViewProps;
     constructor(props: SubCollectionViewProps) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         if (this._props.renderDepth < 0) runInAction(() => (CollectionDockingView.Instance = this));
         //Why is this here?
@@ -77,10 +74,6 @@ export class CollectionDockingView extends CollectionSubView() {
         this.Document.myTrails; // this is equivalent to having a prefetchProxy for myTrails which is needed for the My Trails button in the UI which assumes that Doc.ActiveDashboard.myTrails is legit...
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     /**
      * Switches from dragging a document around a freeform canvas to dragging it as a tab to be docked.
      *
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 0905aa078..5dba9e72a 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -1,12 +1,12 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, numberRange, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { ScriptField } from '../../../fields/ScriptField';
-import { emptyFunction, numberRange, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { CompileScript } from '../../util/Scripting';
@@ -14,6 +14,7 @@ import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
 import { EditableView } from '../EditableView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { CollectionStackingView } from './CollectionStackingView';
 import './CollectionStackingView.scss';
@@ -41,19 +42,18 @@ interface CMVFieldRowProps {
 }
 
 @observer
-export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowProps> {
+export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVFieldRowProps> {
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     @observable private _background = 'inherit';
     @observable private _createEmbeddingSelected: boolean = false;
     @observable private heading: string = '';
     @observable private color: string = '#f1efeb';
     @observable private collapsed: boolean = false;
     @observable private _paletteOn = false;
-    @observable _props: React.PropsWithChildren<CMVFieldRowProps>;
-    constructor(props: React.PropsWithChildren<CMVFieldRowProps>) {
-        super(props);
-        this._props = props;
-        makeObservable(this);
-    }
     private set _heading(value: string) {
         runInAction(() => this._props.headingObject && (this._props.headingObject.heading = this.heading = value));
     }
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 08fcd544e..3ade2ab56 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -9,7 +9,7 @@ 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 { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
+import { copyProps, 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';
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 702775307..f9d258490 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,5 +1,5 @@
 import * as React from 'react';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Field, Opt } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
@@ -44,6 +44,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     public DividerWidth = 16;
     @observable docsDraggedRowCol: number[] = [];
     @observable _scroll = 0;
+
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     @computed get chromeHidden() {
         return BoolCast(this.layoutDoc.chromeHidden) || this.props.onBrowseClick?.() ? true : false;
     }
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index a0f8b9c89..170b1f1ec 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { ScriptField } from '../../../fields/ScriptField';
@@ -19,6 +19,11 @@ export class CollectionPileView extends CollectionSubView() {
     _originalChrome: any = '';
     _disposers: { [name: string]: IReactionDisposer } = {};
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     componentDidMount() {
         if (this.layoutEngine() !== computePassLayout.name && this.layoutEngine() !== computeStarburstLayout.name) {
             this.Document._freeform_pileEngine = computePassLayout.name;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 1b14b31ac..d37a942d0 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
@@ -9,7 +9,7 @@ import { listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { Cast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
-import { copyProps, emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils';
+import { emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -26,6 +26,7 @@ import { AudioWaveform } from '../nodes/audio/AudioWaveform';
 import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../nodes/DocumentView';
 import { LabelBox } from '../nodes/LabelBox';
 import { VideoBox } from '../nodes/VideoBox';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import './CollectionStackedTimeline.scss';
 
 export type CollectionStackedTimelineProps = {
@@ -56,17 +57,10 @@ export enum TrimScope {
 export class CollectionStackedTimeline extends CollectionSubView<SubCollectionViewProps & CollectionStackedTimelineProps>() {
     @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
     @observable public static CurrentlyPlaying: DocumentView[];
-
-    _prevProps: React.PropsWithChildren<SubCollectionViewProps & CollectionStackedTimelineProps>;
-    @override _props: React.PropsWithChildren<SubCollectionViewProps & CollectionStackedTimelineProps>;
-    constructor(props: React.PropsWithChildren<SubCollectionViewProps & CollectionStackedTimelineProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     static LabelScript: ScriptField;
     static LabelPlayScript: ScriptField;
@@ -697,21 +691,15 @@ interface StackedTimelineAnchorProps {
 }
 
 @observer
-class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> {
+class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnchorProps> {
     _lastTimecode: number;
     _disposer: IReactionDisposer | undefined;
 
-    _prevProps: React.PropsWithChildren<StackedTimelineAnchorProps>;
-    @observable _props: React.PropsWithChildren<StackedTimelineAnchorProps>;
     constructor(props: React.PropsWithChildren<StackedTimelineAnchorProps>) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         this._lastTimecode = this._props.currentTimecode();
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     // updates marker document title to reflect correct timecodes
     computeTitle = () => {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d6105c184..16adf4b96 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,8 +1,8 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import * as CSS from 'csstype';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, Opt } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
@@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { copyProps, emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { CollectionViewType } from '../../documents/DocumentTypes';
 import { DragManager, dropActionType } from '../../util/DragManager';
@@ -31,7 +31,7 @@ import { StyleProp } from '../StyleProvider';
 import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
 import './CollectionStackingView.scss';
 import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { CollectionSubView } from './CollectionSubView';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionStackingViewProps = {
@@ -115,22 +115,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         return this._props.PanelWidth() - this.gridGap;
     }
 
-    _prevProps: React.PropsWithChildren<SubCollectionViewProps & Partial<collectionStackingViewProps>>;
-    _props: React.PropsWithChildren<SubCollectionViewProps & Partial<collectionStackingViewProps>>;
     constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
-
         if (this.colHeaderData === undefined) {
             // TODO: what is a layout doc? Is it literally how this document is supposed to be layed out?
             // here we're making an empty list of column headers (again, what Mehek showed us)
             this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
         }
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     // TODO: plj - these are the children
     children = (docs: Doc[]) => {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 7ea146479..2302bfbc3 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,15 +1,15 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction, untracked } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { RichTextField } from '../../../fields/RichTextField';
 import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, setupMoveUpEvents, returnFalse, returnEmptyString } from '../../../Utils';
+import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
@@ -19,9 +19,9 @@ import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
-import './CollectionStackingView.scss';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { Id } from '../../../fields/FieldSymbols';
+import './CollectionStackingView.scss';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 
 // So this is how we are storing a column
 interface CSVFieldColumnProps {
@@ -49,7 +49,7 @@ interface CSVFieldColumnProps {
 }
 
 @observer
-export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
+export class CollectionStackingViewFieldColumn extends ObservableReactComponent<CSVFieldColumnProps> {
     private dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -58,10 +58,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @observable _heading = '';
     @observable _color = '';
 
-    @observable _props!: CSVFieldColumnProps;
     constructor(props: CSVFieldColumnProps) {
         super(props);
-        this._props = props;
         makeObservable(this);
         this._heading = this._props.headingObject ? this._props.headingObject.heading : this._props.heading;
         this._color = this._props.headingObject ? this._props.headingObject.color : '#f1efeb';
@@ -80,10 +78,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         }
     };
 
-    componentDidUpdate() {
-        // untracked(() => (this._props = this.props));
-    }
-
     @action
     componentDidMount() {
         this._disposers.collapser = reaction(
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 79b15fdb1..0131af6f2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,5 +1,7 @@
-import { action, computed, makeObservable, observable, override, runInAction, untracked } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
+import * as React from 'react';
 import * as rp from 'request-promise';
+import { Utils, returnFalse } from '../../../Utils';
 import CursorField from '../../../fields/CursorField';
 import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
 import { AclPrivate } from '../../../fields/DocSymbols';
@@ -10,21 +12,19 @@ import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { returnFalse, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
-import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocUtils, Docs, DocumentOptions } from '../../documents/Documents';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../DocComponent';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { LoadingBox } from '../nodes/LoadingBox';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { CollectionView, CollectionViewProps } from './CollectionView';
-import * as React from 'react';
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     isAnyChildContentActive: () => boolean;
@@ -35,16 +35,11 @@ export function CollectionSubView<X>(moreProps?: X) {
         private dropDisposer?: DragManager.DragDropDisposer;
         private gestureDisposer?: GestureUtils.GestureEventDisposer;
         protected _mainCont?: HTMLDivElement;
-        @override _props: X & SubCollectionViewProps;
 
         constructor(props: any) {
             super(props);
-            this._props = props;
             makeObservable(this);
         }
-        componentDidUpdate() {
-            // untracked(() => (this._props = this.props));
-        }
 
         @observable _focusFilters: Opt<string[]>; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it
         @observable _focusRangeFilters: Opt<string[]>; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index e65f24702..c440a7e3f 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,5 +1,5 @@
 import { toUpper } from 'lodash';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt, StrListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
@@ -32,6 +32,11 @@ export class CollectionTimeView extends CollectionSubView() {
     @observable _viewDefDivClick: Opt<ScriptField>;
     @observable _focusPivotField: Opt<string>;
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     async componentDidMount() {
         this.props.setContentView?.(this);
         runInAction(() => {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 658d60523..7f8d42088 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,5 +1,6 @@
-import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
@@ -7,7 +8,7 @@ import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { copyProps, emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../Utils';
+import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
@@ -23,10 +24,9 @@ import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { CollectionSubView } from './CollectionSubView';
 import './CollectionTreeView.scss';
 import { TreeView } from './TreeView';
-import * as React from 'react';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionTreeViewProps = {
@@ -59,16 +59,10 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     private refList: Set<any> = new Set(); // list of tree view items to monitor for height changes
     private observer: any; // observer for monitoring tree view items.
 
-    _prevProps: React.PropsWithChildren<SubCollectionViewProps & Partial<collectionTreeViewProps>>;
-    @override _props: React.PropsWithChildren<SubCollectionViewProps & Partial<collectionTreeViewProps>>;
-    constructor(props: React.PropsWithChildren<SubCollectionViewProps & Partial<collectionTreeViewProps>>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     get dataDoc() {
         return this._props.TemplateDataDocument || this.Document;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e84c12e02..f1ef52e60 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,14 +1,14 @@
-import { IReactionDisposer, makeObservable, observable, override, reaction, runInAction, untracked } from 'mobx';
+import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { returnEmptyString } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { ObjectField } from '../../../fields/ObjectField';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { copyProps, returnEmptyString } from '../../../Utils';
-import { DocUtils } from '../../documents/Documents';
 import { CollectionViewType } from '../../documents/DocumentTypes';
+import { DocUtils } from '../../documents/Documents';
 import { dropActionType } from '../../util/DragManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { ContextMenu } from '../ContextMenu';
@@ -19,19 +19,19 @@ import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { CollectionCarousel3DView } from './CollectionCarousel3DView';
 import { CollectionCarouselView } from './CollectionCarouselView';
 import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { CollectionGridView } from './collectionGrid/CollectionGridView';
-import { CollectionLinearView } from './collectionLinear';
-import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
-import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
 import { CollectionNoteTakingView } from './CollectionNoteTakingView';
 import { CollectionPileView } from './CollectionPileView';
-import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
 import { CollectionStackingView } from './CollectionStackingView';
 import { SubCollectionViewProps } from './CollectionSubView';
 import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionTreeView } from './CollectionTreeView';
 import './CollectionView.scss';
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { CollectionGridView } from './collectionGrid/CollectionGridView';
+import { CollectionLinearView } from './collectionLinear';
+import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
+import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
+import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
 const path = require('path');
 
 interface CollectionViewProps_ extends FieldViewProps {
@@ -78,16 +78,10 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     private reactionDisposer: IReactionDisposer | undefined;
     @observable _isContentActive: boolean | undefined = undefined;
 
-    _prevProps: React.PropsWithChildren<ViewBoxAnnotatableProps & CollectionViewProps>;
-    @override _props: React.PropsWithChildren<ViewBoxAnnotatableProps & CollectionViewProps>;
     constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
-        runInAction(() => (this._annotationKeySuffix = returnEmptyString));
-    }
-    componentDidUpdate() {
-        copyProps(this);
+        this._annotationKeySuffix = returnEmptyString;
     }
 
     componentDidMount() {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 8bc0f62dc..2eb13fd2f 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -2,40 +2,41 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Popup, Type } from 'browndash-components';
 import { clamp } from 'lodash';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction, runInAction } from 'mobx';
+import { IReactionDisposer, ObservableSet, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
+import { DashColor, Utils, emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../Utils';
 import { Doc, Opt } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from '../../../fields/RefField';
+import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { copyProps, DashColor, emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoable, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoable } from '../../util/UndoManager';
 import { DashboardView } from '../DashboardView';
-import { Colors } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
+import { Colors } from '../global/globalEnums';
 import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
-import { DashFieldView } from '../nodes/formattedText/DashFieldView';
 import { KeyValueBox } from '../nodes/KeyValueBox';
+import { DashFieldView } from '../nodes/formattedText/DashFieldView';
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
-import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
-import * as React from 'react';
-import { Docs } from '../../documents/Documents';
-import { ComputedField } from '../../../fields/ScriptField';
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface TabDocViewProps {
@@ -44,16 +45,13 @@ interface TabDocViewProps {
     glContainer: any;
 }
 @observer
-export class TabDocView extends React.Component<TabDocViewProps> {
+export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
     static _allTabs = new ObservableSet<TabDocView>();
     _mainCont: HTMLDivElement | null = null;
     _tabReaction: IReactionDisposer | undefined;
 
-    _prevProps: React.PropsWithChildren<TabDocViewProps>;
-    @observable _props: React.PropsWithChildren<TabDocViewProps>;
-    constructor(props: React.PropsWithChildren<TabDocViewProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
@@ -341,9 +339,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         //     { fireImmediately: true });
         runInAction(() => TabDocView._allTabs.add(this));
     }
-    componentDidUpdate() {
+    componentDidUpdate(prevProps: Readonly<TabDocViewProps>) {
         this._view && DocumentManager.Instance.AddView(this._view);
-        copyProps(this);
     }
 
     componentWillUnmount() {
@@ -521,14 +518,14 @@ interface TabMiniThumbProps {
     miniTop: () => number;
     miniLeft: () => number;
 }
-@observer
+
 class TabMiniThumb extends React.Component<TabMiniThumbProps> {
     render() {
         return <div className="miniThumb" style={{ width: `${this.props.miniWidth()}% `, height: `${this.props.miniHeight()}% `, left: `${this.props.miniLeft()}% `, top: `${this.props.miniTop()}% ` }} />;
     }
 }
 @observer
-export class TabMinimapView extends React.Component<TabMinimapViewProps> {
+export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps> {
     static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc) {
             switch (property.split(':')[0]) {
@@ -556,12 +553,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
         }
     };
 
-    @observable _props: React.PropsWithChildren<TabMinimapViewProps>;
-    constructor(props: React.PropsWithChildren<TabMinimapViewProps>) {
-        super(props);
-        this._props = props;
-        makeObservable(this);
-    }
     @computed get renderBounds() {
         const compView = this._props.tabView()?.ComponentView as CollectionFreeFormView;
         const bounds = compView?.freeformData?.(true)?.bounds;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 224d74882..52ecdfbec 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,8 +1,10 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { IconButton, Size } from 'browndash-components';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Utils, emptyFunction, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick } from '../../../Utils';
 import { Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
@@ -12,9 +14,8 @@ import { listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { copyProps, emptyFunction, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocUtils, Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { LinkManager } from '../../util/LinkManager';
@@ -22,19 +23,19 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SettingsManager } from '../../util/SettingsManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoBatch, undoable } from '../../util/UndoManager';
 import { EditableView } from '../EditableView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { StyleProp } from '../StyleProvider';
 import { DocumentView, DocumentViewInternal, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
+import { KeyValueBox } from '../nodes/KeyValueBox';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { KeyValueBox } from '../nodes/KeyValueBox';
-import { StyleProp } from '../StyleProvider';
 import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
 import { CollectionView } from './CollectionView';
 import { TreeSort } from './TreeSort';
 import './TreeView.scss';
-import * as React from 'react';
 const { default: { TREE_BULLET_WIDTH } } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
 
 export interface TreeViewProps {
@@ -86,7 +87,7 @@ const treeBulletWidth = function () {
  * treeView_ExpandedView : name of field whose contents are being displayed as the document's subtree
  */
 @observer
-export class TreeView extends React.Component<TreeViewProps> {
+export class TreeView extends ObservableReactComponent<TreeViewProps> {
     static _editTitleOnLoad: Opt<{ id: string; parent: TreeView | CollectionTreeView | undefined }>;
     static _openTitleScript: Opt<ScriptField | undefined>;
     static _openLevelScript: Opt<ScriptField | undefined>;
@@ -254,11 +255,8 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     static GetRunningChildren = new Map<Doc, any>();
     static ToggleChildrenRun = new Map<Doc, () => void>();
-    _prevProps: TreeViewProps;
-    @observable _props: TreeViewProps;
     constructor(props: TreeViewProps) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         if (!TreeView._openLevelScript) {
             TreeView._openTitleScript = ScriptField.MakeScript('scriptContext.setEditTitle(documentView)', { scriptContext: 'any', documentView: 'any' });
@@ -297,8 +295,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         this._props.hierarchyIndex !== undefined && this._props.RemFromMap?.(this.Document, this._props.hierarchyIndex);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
+    componentDidUpdate(prevProps: Readonly<TreeViewProps>) {
         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 dd7e12e41..0d5fcdaeb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -1,8 +1,9 @@
-import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { IReactionDisposer, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { copyProps } from '../../../../Utils';
+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.
@@ -47,14 +48,11 @@ export interface CollectionFreeFormInfoStateProps {
 }
 
 @observer
-export class CollectionFreeFormInfoState extends React.Component<CollectionFreeFormInfoStateProps> {
+export class CollectionFreeFormInfoState extends ObservableReactComponent<CollectionFreeFormInfoStateProps> {
     _disposers: IReactionDisposer[] = [];
 
-    _prevProps: React.PropsWithChildren<CollectionFreeFormInfoStateProps>;
-    @observable _props: React.PropsWithChildren<CollectionFreeFormInfoStateProps>;
-    constructor(props: React.PropsWithChildren<CollectionFreeFormInfoStateProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
@@ -62,7 +60,7 @@ export class CollectionFreeFormInfoState extends React.Component<CollectionFreeF
         return this._props.infoState;
     }
     get Arcs() {
-        return Object.keys(this.State).map(key => this.State[key]);
+        return Object.keys(this.State ?? []).map(key => this.State?.[key]);
     }
 
     clearState = () => this._disposers.map(disposer => disposer());
@@ -84,8 +82,8 @@ export class CollectionFreeFormInfoState extends React.Component<CollectionFreeF
     componentDidMount(): void {
         this.initState();
     }
-    componentDidUpdate() {
-        copyProps(this);
+    componentDidUpdate(prevProps: Readonly<CollectionFreeFormInfoStateProps>) {
+        copyProps(this, prevProps);
         this.clearState();
         this.initState();
     }
@@ -93,6 +91,6 @@ export class CollectionFreeFormInfoState extends React.Component<CollectionFreeF
         this.clearState();
     }
     render() {
-        return <div className="infoUI">{this.State[StateMessage]}</div>;
+        return <div className="infoUI">{this.State?.[StateMessage]}</div>;
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index f0a052c1d..4d3752c02 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -1,21 +1,13 @@
-import { IReactionDisposer, computed, observable, reaction, action, runInAction, makeObservable } from 'mobx';
+import { IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc } from '../../../../fields/Doc';
-import { ScriptField } from '../../../../fields/ScriptField';
-import { PresBox } from '../../nodes/trails/PresBox';
-import './CollectionFreeFormView.scss';
 import * as React from 'react';
-import { CollectionFreeFormView } from './CollectionFreeFormView';
-import { NumCast } from '../../../../fields/Types';
+import { Doc } from '../../../../fields/Doc';
 import { LinkManager } from '../../../util/LinkManager';
-import { InkTool } from '../../../../fields/InkField';
-import { LinkDocPreview } from '../../nodes/LinkDocPreview';
-import { DocumentLinksButton, DocButtonState } from '../../nodes/DocumentLinksButton';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { CollectionFreeFormInfoState, infoState, StateMessage, infoArc, StateEntryFunc, InfoState } from './CollectionFreeFormInfoState';
-import { string32 } from 'pdfjs-dist/types/src/shared/util';
-import { any } from 'bluebird';
-import { copyProps } from '../../../../Utils';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { DocButtonState } from '../../nodes/DocumentLinksButton';
+import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState';
+import { CollectionFreeFormView } from './CollectionFreeFormView';
+import './CollectionFreeFormView.scss';
 
 export interface CollectionFreeFormInfoUIProps {
     Document: Doc;
@@ -23,7 +15,7 @@ export interface CollectionFreeFormInfoUIProps {
 }
 
 @observer
-export class CollectionFreeFormInfoUI extends React.Component<CollectionFreeFormInfoUIProps> {
+export class CollectionFreeFormInfoUI extends ObservableReactComponent<CollectionFreeFormInfoUIProps> {
     private _disposers: { [name: string]: IReactionDisposer } = {};
 
     @observable _currState: infoState | undefined = undefined;
@@ -33,17 +25,12 @@ export class CollectionFreeFormInfoUI extends React.Component<CollectionFreeForm
     set currState(val) {
         this._currState = val;
     }
-    _prevProps: React.PropsWithChildren<CollectionFreeFormInfoUIProps>;
-    @observable _props: React.PropsWithChildren<CollectionFreeFormInfoUIProps>;
-    constructor(props: React.PropsWithChildren<CollectionFreeFormInfoUIProps>) {
+
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = this.props;
         makeObservable(this);
         this.currState = this.setupStates();
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     setCurrState = (state: infoState) => {
         if (state) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index be3d2439c..6337c8d34 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,19 +1,20 @@
 import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, Field } from '../../../../fields/Doc';
 import { Brushed, DocCss } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { Cast, NumCast, StrCast } from '../../../../fields/Types';
-import { copyProps, emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { LinkManager } from '../../../util/LinkManager';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Colors } from '../../global/globalEnums';
 import { DocumentView } from '../../nodes/DocumentView';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
 import './CollectionFreeFormLinkView.scss';
-import * as React from 'react';
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -24,22 +25,16 @@ export interface CollectionFreeFormLinkViewProps {
 // props.screentolocatransform
 
 @observer
-export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
+export class CollectionFreeFormLinkView extends ObservableReactComponent<CollectionFreeFormLinkViewProps> {
     @observable _opacity: number = 0;
     @observable _start = 0;
     _anchorDisposer: IReactionDisposer | undefined;
     _timeout: NodeJS.Timeout | undefined;
-    _prevProps: CollectionFreeFormLinkViewProps;
-    @observable _props: CollectionFreeFormLinkViewProps;
-    constructor(props: CollectionFreeFormLinkViewProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
     componentWillUnmount() {
         this._anchorDisposer?.();
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index aa40fb809..1f4688729 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
 import { Bezier } from 'bezier-js';
 import { Colors } from 'browndash-components';
-import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction, runInAction, toJS } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction, toJS } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
@@ -16,7 +16,7 @@ import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../
 import { ImageField } from '../../../../fields/URLField';
 import { TraceMobx } from '../../../../fields/util';
 import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, copyProps, DashColor, emptyFunction, intersectRect, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -43,7 +43,7 @@ import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../../nodes/trails/PresBox';
 import { CreateImage } from '../../nodes/WebBoxRenderer';
 import { StyleProp } from '../../StyleProvider';
-import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
+import { CollectionSubView } from '../CollectionSubView';
 import { TreeViewType } from '../CollectionTreeView';
 import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
 import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI';
@@ -72,17 +72,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return 'CollectionFreeFormView(' + this._props.Document.title?.toString() + ')';
     } // this makes mobx trace() statements more descriptive
 
-    _prevProps: React.PropsWithChildren<SubCollectionViewProps & Partial<collectionFreeformViewProps>>;
-    @override _props: React.PropsWithChildren<SubCollectionViewProps & Partial<collectionFreeformViewProps>>;
     constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     @observable
     public static ShowPresPaths = false;
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 825bd5f19..7aa68b0d9 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -20,7 +20,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
     public isShown = () => this._opacity > 0;
     constructor(props: Readonly<{}>) {
         super(props);
-
         MarqueeOptionsMenu.Instance = this;
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 10beb120a..2c65726b2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,5 +1,7 @@
 import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Utils, intersectRect, lightOrDark, returnFalse } from '../../../../Utils';
 import { Doc, Opt } from '../../../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
@@ -7,25 +9,24 @@ import { InkData, InkField, InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
-import { ImageField, nullAudio } from '../../../../fields/URLField';
+import { ImageField } from '../../../../fields/URLField';
 import { GetEffectiveAcl } from '../../../../fields/util';
-import { intersectRect, lightOrDark, returnFalse, Utils } from '../../../../Utils';
 import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
-import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
+import { DocUtils, Docs, DocumentOptions } from '../../../documents/Documents';
 import { SelectionManager } from '../../../util/SelectionManager';
+import { freeformScrollMode } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
-import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { PreviewCursor } from '../../PreviewCursor';
 import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
-import { PreviewCursor } from '../../PreviewCursor';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { SubCollectionViewProps } from '../CollectionSubView';
 import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
 import './MarqueeView.scss';
-import * as React from 'react';
-import { freeformScrollMode } from '../../../util/SettingsManager';
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -51,16 +52,14 @@ export interface MarqueeViewBounds {
 }
 
 @observer
-export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
+export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps & MarqueeViewProps> {
     public static CurViewBounds(pinDoc: Doc, panelWidth: number, panelHeight: number) {
         const ps = NumCast(pinDoc._freeform_scale, 1);
         return { left: NumCast(pinDoc._freeform_panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._freeform_panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
     }
 
-    @observable _props: React.PropsWithChildren<SubCollectionViewProps & MarqueeViewProps>;
-    constructor(props: React.PropsWithChildren<SubCollectionViewProps & MarqueeViewProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = props;
         makeObservable(this);
     }
 
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 697a11ccc..1e19964d7 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, Lambda, observable, reaction } from 'mobx';
+import { action, computed, Lambda, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Opt } from '../../../../fields/Doc';
@@ -27,6 +27,11 @@ export class CollectionGridView extends CollectionSubView() {
     @observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
     private dropLocation: object = {}; // sets the drop location for external drops
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
 
     @computed get numCols() {
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index a9782f699..f1fb68003 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,26 +1,26 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { Toggle, ToggleType, Type } from 'browndash-components';
-import { action, IReactionDisposer, makeObservable, observable, override, reaction, runInAction, untracked } from 'mobx';
+import { IReactionDisposer, action, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { Utils, emptyFunction, returnEmptyDoclist, returnTrue } from '../../../../Utils';
 import { Doc, Opt } from '../../../../fields/Doc';
 import { Height, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { copyProps, emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
+import { UndoStack } from '../../UndoStack';
 import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
 import { DocumentView } from '../../nodes/DocumentView';
 import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
-import { UndoStack } from '../../UndoStack';
 import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
-import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
+import { CollectionSubView } from '../CollectionSubView';
 import './CollectionLinearView.scss';
 
 /**
@@ -33,24 +33,15 @@ import './CollectionLinearView.scss';
  */
 @observer
 export class CollectionLinearView extends CollectionSubView() {
-    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
-    @observable private _selectedIndex = -1;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _widthDisposer?: IReactionDisposer;
     private _selectedDisposer?: IReactionDisposer;
 
-    _prevProps: SubCollectionViewProps;
-    @override _props: SubCollectionViewProps;
-    constructor(props: SubCollectionViewProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     componentWillUnmount() {
         this._dropDisposer?.();
         this._widthDisposer?.();
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 037786abf..b951a4b17 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { Button } from 'browndash-components';
-import { action, computed } from 'mobx';
+import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
@@ -37,6 +37,11 @@ const resizerWidth = 8;
 
 @observer
 export class CollectionMulticolumnView extends CollectionSubView() {
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     /**
      * @returns the list of layout documents whose width unit is
      * *, denoting that it will be displayed with a ratio, not fixed pixel, value
@@ -238,8 +243,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         return this.props.isContentActive?.() === false || childDocsActive === false
             ? false //
             : this.props.isDocumentActive?.() && childDocsActive
-            ? true
-            : undefined;
+              ? true
+              : undefined;
     };
     getDisplayDoc = (childLayout: Doc) => {
         const width = () => this.lookupPixels(childLayout);
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index bbf3481dd..f1c6c6e1e 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from 'mobx';
+import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
@@ -33,6 +33,11 @@ const resizerHeight = 8;
 
 @observer
 export class CollectionMultirowView extends CollectionSubView() {
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     /**
      * @returns the list of layout documents whose width unit is
      * *, denoting that it will be displayed with a ratio, not fixed pixel, value
@@ -234,8 +239,8 @@ export class CollectionMultirowView extends CollectionSubView() {
         return this.props.isContentActive?.() === false || childDocsActive === false
             ? false //
             : this.props.isDocumentActive?.() && childDocsActive
-            ? true
-            : undefined;
+              ? true
+              : undefined;
     };
     getDisplayDoc = (layout: Doc) => {
         const height = () => this.lookupPixels(layout);
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 276415d56..2546f5b02 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, makeObservable, observable, ObservableMap, observe, override } from 'mobx';
+import { action, computed, makeObservable, observable, ObservableMap, observe } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from '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 { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
+import { 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';
@@ -18,8 +18,9 @@ import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from '../../nodes/DocumentView';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { DefaultStyleProvider, StyleProp } from '../../StyleProvider';
-import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
+import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
@@ -57,6 +58,11 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _tableContentRef: HTMLDivElement | null = null;
     private _menuTarget = React.createRef<HTMLDivElement>();
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     static _rowHeight: number = 50;
     static _rowSingleLineHeight: number = 32;
     public static _minColWidth: number = 25;
@@ -79,18 +85,6 @@ 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;
@@ -959,17 +953,12 @@ interface CollectionSchemaViewDocProps {
 }
 
 @observer
-class CollectionSchemaViewDoc extends React.Component<CollectionSchemaViewDocProps> {
-    _prevProps: CollectionSchemaViewDocProps;
-    @observable _props;
-    constructor(props: CollectionSchemaViewDocProps) {
+class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaViewDocProps> {
+    constructor(props: any) {
         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) => {
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 737d7e0f4..5a3be826b 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, makeObservable, override } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
+import * as React from 'react';
 import { CgClose } from 'react-icons/cg';
 import { FaExternalLinkAlt } from 'react-icons/fa';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
 import { Doc } from '../../../../fields/Doc';
 import { BoolCast } from '../../../../fields/Types';
-import { copyProps, emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
@@ -30,16 +30,10 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
     }
     private _ref: HTMLDivElement | null = null;
 
-    _prevProps: FieldViewProps & SchemaRowBoxProps;
-    @override _props: FieldViewProps & SchemaRowBoxProps;
-    constructor(props: FieldViewProps & SchemaRowBoxProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     bounds = () => this._ref?.getBoundingClientRect();
 
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index bcfe2c232..85269028b 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,27 +1,28 @@
-import * as React from 'react';
-import Select, { MenuPlacement } from 'react-select';
 import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { extname } from 'path';
+import * as React from 'react';
 import DatePicker from 'react-datepicker';
+import Select from 'react-select';
+import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils';
 import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast, Field } from '../../../../fields/Doc';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
 import { FInfo } from '../../../documents/Documents';
 import { DocFocusOrOpen } from '../../../util/DocumentManager';
 import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoBatch, undoable } from '../../../util/UndoManager';
 import { EditableView } from '../../EditableView';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { DefaultStyleProvider } from '../../StyleProvider';
 import { Colors } from '../../global/globalEnums';
 import { OpenWhere } from '../../nodes/DocumentView';
-import { FieldView, FieldViewProps } from '../../nodes/FieldView';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { FieldViewProps } from '../../nodes/FieldView';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { ColumnType, FInfotoColType } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 
 export interface SchemaTableCellProps {
@@ -47,7 +48,12 @@ export interface SchemaTableCellProps {
 }
 
 @observer
-export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
+export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellProps> {
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     static addFieldDoc = (doc: Doc, where: OpenWhere) => {
         DocFocusOrOpen(doc);
         return true;
@@ -95,12 +101,6 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
         return { color, textDecoration, fieldProps, cursor, pointerEvents };
     }
 
-    @observable _props: React.PropsWithChildren<SchemaTableCellProps>;
-    constructor(props: React.PropsWithChildren<SchemaTableCellProps>) {
-        super(props);
-        this._props = props;
-    }
-
     @computed get selected() {
         const selected: [Doc, number] | undefined = this._props.selectedCell();
         return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
@@ -182,16 +182,14 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
 
 // mj: most of this is adapted from old schema code so I'm not sure what it does tbh
 @observer
-export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
-    @observable _previewRef: HTMLImageElement | undefined = undefined;
-
-    _props: React.PropsWithChildren<SchemaTableCellProps>;
-    constructor(props: React.PropsWithChildren<SchemaTableCellProps>) {
+export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellProps> {
+    constructor(props: any) {
         super(props);
-        this._props = props;
         makeObservable(this);
     }
 
+    @observable _previewRef: HTMLImageElement | undefined = undefined;
+
     choosePath(url: URL) {
         if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
         if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
@@ -251,16 +249,13 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
 }
 
 @observer
-export class SchemaDateCell extends React.Component<SchemaTableCellProps> {
-    @observable _pickingDate: boolean = false;
-
-    @observable _props: React.PropsWithChildren<SchemaTableCellProps>;
-    constructor(props: React.PropsWithChildren<SchemaTableCellProps>) {
+export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProps> {
+    constructor(props: any) {
         super(props);
-        this._props = props;
         makeObservable(this);
     }
 
+    @observable _pickingDate: boolean = false;
     @computed get date(): DateField {
         // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
         return DateCast(this._props.Document[this._props.fieldKey]);
@@ -282,11 +277,9 @@ export class SchemaDateCell extends React.Component<SchemaTableCellProps> {
     }
 }
 @observer
-export class SchemaRTFCell extends React.Component<SchemaTableCellProps> {
-    @observable _props: React.PropsWithChildren<SchemaTableCellProps>;
-    constructor(props: React.PropsWithChildren<SchemaTableCellProps>) {
+export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps> {
+    constructor(props: any) {
         super(props);
-        this._props = props;
         makeObservable(this);
     }
 
@@ -306,11 +299,9 @@ export class SchemaRTFCell extends React.Component<SchemaTableCellProps> {
     }
 }
 @observer
-export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
-    @observable _props: React.PropsWithChildren<SchemaTableCellProps>;
-    constructor(props: React.PropsWithChildren<SchemaTableCellProps>) {
+export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProps> {
+    constructor(props: any) {
         super(props);
-        this._props = props;
         makeObservable(this);
     }
 
@@ -352,11 +343,9 @@ export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
     }
 }
 @observer
-export class SchemaEnumerationCell extends React.Component<SchemaTableCellProps> {
-    @observable _props: React.PropsWithChildren<SchemaTableCellProps>;
-    constructor(props: React.PropsWithChildren<SchemaTableCellProps>) {
+export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableCellProps> {
+    constructor(props: any) {
         super(props);
-        this._props = props;
         makeObservable(this);
     }
 
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index af22f41e1..b7376e901 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -1,13 +1,14 @@
 import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc } from '../../../fields/Doc';
 import { LinkManager } from '../../util/LinkManager';
+import { SettingsManager } from '../../util/SettingsManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DocumentView } from '../nodes/DocumentView';
 import { LinkInfo } from '../nodes/LinkDocPreview';
 import './LinkMenu.scss';
 import { LinkMenuGroup } from './LinkMenuGroup';
-import * as React from 'react';
-import { SettingsManager } from '../../util/SettingsManager';
 
 interface Props {
     docView: DocumentView;
@@ -20,10 +21,10 @@ interface Props {
  * the outermost component for the link menu of a node that contains a list of its linked nodes
  */
 @observer
-export class LinkMenu extends React.Component<Props> {
+export class LinkMenu extends ObservableReactComponent<Props> {
     _editorRef = React.createRef<HTMLDivElement>();
     @observable _linkMenuRef = React.createRef<HTMLDivElement>();
-    constructor(props: Props) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
     }
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index d77525e04..06073b52c 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,23 +1,24 @@
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, observable, makeObservable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { Cast, DocCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
-import { emptyFunction, returnFalse, setupMoveUpEvents, copyProps } from '../../../Utils';
-import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { LinkFollower } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
-import { undoBatch } from '../../util/UndoManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
-import { LinkDocPreview, LinkInfo } from '../nodes/LinkDocPreview';
+import { LinkInfo } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
-import * as React from 'react';
+import { undoBatch } from '../../util/UndoManager';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { DocumentType } from '../../documents/DocumentTypes';
 
 interface LinkMenuItemProps {
     groupType: string;
@@ -50,20 +51,13 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
 }
 
 @observer
-export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
+export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> {
     private _drag = React.createRef<HTMLDivElement>();
     _editRef = React.createRef<HTMLDivElement>();
-
-    _prevProps: React.PropsWithChildren<LinkMenuItemProps>;
-    @observable _props: React.PropsWithChildren<LinkMenuItemProps>;
-    constructor(props: React.PropsWithChildren<LinkMenuItemProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     @observable private _showMore: boolean = false;
     @action toggleShowMore(e: React.PointerEvent) {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 0c671f7e3..567cf193e 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -1,14 +1,14 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, override, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { DateField } from '../../../fields/DateField';
 import { Doc } from '../../../fields/Doc';
 import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, DateCast, NumCast } from '../../../fields/Types';
 import { AudioField, nullAudio } from '../../../fields/URLField';
-import { copyProps, emptyFunction, formatTime, returnFalse, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction, formatTime, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { Networking } from '../../Network';
 import { DragManager } from '../../util/DragManager';
@@ -17,7 +17,7 @@ import { undoBatch } from '../../util/UndoManager';
 import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import './AudioBox.scss';
 import { DocFocusOptions } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
@@ -54,19 +54,13 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         return FieldView.LayoutString(AudioBox, fieldKey);
     }
 
-    _prevProps: React.PropsWithChildren<FieldViewProps>;
-    @override _props: React.PropsWithChildren<FieldViewProps>;
-    constructor(props: React.PropsWithChildren<FieldViewProps>) {
+    public static Enabled = false;
+
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-    public static Enabled = false;
-
     static topControlsHeight = 30; // height of upper controls above timeline
     static bottomControlsHeight = 20; // height of lower controls below timeline
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 8e7a6914f..c97c879af 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,21 +1,21 @@
-import { action, computed, observable, makeObservable, reaction, runInAction, override } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { OmitKeys, numberRange } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { copyProps, numberRange, OmitKeys } from '../../../Utils';
 import { DocumentManager } from '../../util/DocumentManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
-import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { DocComponent } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import './CollectionFreeFormDocumentView.scss';
 import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
-import * as React from 'react';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 
 export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentViewProps {
     x: number;
@@ -37,11 +37,8 @@ export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentView
 }
 @observer
 export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>() implements CollectionFreeFormDocumentViewProps {
-    _prevProps: React.PropsWithChildren<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>;
-    @override _props: React.PropsWithChildren<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>;
-    constructor(props: React.PropsWithChildren<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
     @observable X = this.props.x;
@@ -79,7 +76,7 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti
     w_Highlight = ()       => this.Highlight; // prettier-ignore
     w_Width = ()           => this.Width; // prettier-ignore
     w_Height = ()          => this.Height; // prettier-ignore
-    w_AutoDim = () => this.AutoDim;
+    w_AutoDim = ()         => this.AutoDim; // prettier-ignore
     w_Transition = ()      => this.Transition; // prettier-ignore
     w_DataTransition = ()  => this.DataTransition; // prettier-ignore
 
@@ -87,7 +84,6 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti
     PanelHeight = ()       => this._props.autoDim ? this._props.PanelHeight?.() : this.Height; // prettier-ignore
 
     componentDidUpdate() {
-        copyProps(this);
         this.WrapperKeys.forEach(action(keys => ((this as any)[keys.upper] = (this.props as any)[keys.lower])));
     }
     render() {
@@ -124,6 +120,10 @@ export interface CollectionFreeFormDocumentViewProps {
 
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & DocumentViewProps>() {
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
     get displayName() { // this makes mobx trace() statements more descriptive
         return 'CollectionFreeFormDocumentView(' + this.Document.title + ')';
     } // prettier-ignore
@@ -143,13 +143,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     public static animStringFields = ['backgroundColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
     public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
 
-    _props: React.PropsWithChildren<CollectionFreeFormDocumentViewProps & DocumentViewProps>;
-    constructor(props: React.PropsWithChildren<CollectionFreeFormDocumentViewProps & DocumentViewProps>) {
-        super(props);
-        this._props = Object.assign({}, props);
-        makeObservable(this);
-    }
-
     get CollectionFreeFormView() {
         return this._props.CollectionFreeFormView;
     }
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 62523ba00..de382fca5 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,10 +1,11 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, makeObservable, observable, override } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils';
 import { Doc, Opt } from '../../../fields/Doc';
 import { DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { copyProps, emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { DocUtils, Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
@@ -13,7 +14,6 @@ import './ComparisonBox.scss';
 import { DocumentView, DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { PinProps, PresBox } from './trails';
-import * as React from 'react';
 
 @observer
 export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -21,20 +21,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         return FieldView.LayoutString(ComparisonBox, fieldKey);
     }
     private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];
-
-    @observable _animating = '';
-
-    _prevProps: ViewBoxAnnotatableProps & FieldViewProps;
-    @override _props: ViewBoxAnnotatableProps & FieldViewProps;
-    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
+    @observable _animating = '';
 
     @computed get clipWidth() {
         return NumCast(this.layoutDoc[this.clipWidthKey], 50);
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index c615e7ff0..e161b4c4c 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,17 +1,20 @@
-import { computed, makeObservable, observable, runInAction, untracked } from 'mobx';
+import { computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import * as XRegExp from 'xregexp';
+import { OmitKeys, Without, emptyPath } from '../../../Utils';
 import { Doc, Opt } from '../../../fields/Doc';
 import { AclPrivate, DocData } from '../../../fields/DocSymbols';
 import { ScriptField } from '../../../fields/ScriptField';
 import { Cast, StrCast } from '../../../fields/Types';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { emptyPath, OmitKeys, Without } from '../../../Utils';
+import { InkingStroke } from '../InkingStroke';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { CollectionView } from '../collections/CollectionView';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { CollectionSchemaView } from '../collections/collectionSchema/CollectionSchemaView';
 import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
-import { CollectionView } from '../collections/CollectionView';
-import { InkingStroke } from '../InkingStroke';
 import { PresElementBox } from '../nodes/trails/PresElementBox';
 import { SearchBox } from '../search/SearchBox';
 import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo';
@@ -24,10 +27,8 @@ import './DocumentView.scss';
 import { EquationBox } from './EquationBox';
 import { FieldView, FieldViewProps } from './FieldView';
 import { FontIconBox } from './FontIconBox/FontIconBox';
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { FunctionPlotBox } from './FunctionPlotBox';
 import { ImageBox } from './ImageBox';
-import { ImportElementBox } from './importBox/ImportElementBox';
 import { KeyValueBox } from './KeyValueBox';
 import { LabelBox } from './LabelBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
@@ -40,11 +41,11 @@ import { PhysicsSimulationBox } from './PhysicsBox/PhysicsSimulationBox';
 import { RecordingBox } from './RecordingBox';
 import { ScreenshotBox } from './ScreenshotBox';
 import { ScriptingBox } from './ScriptingBox';
-import { PresBox } from './trails/PresBox';
 import { VideoBox } from './VideoBox';
 import { WebBox } from './WebBox';
-import * as React from 'react';
-import * as XRegExp from 'xregexp';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { ImportElementBox } from './importBox/ImportElementBox';
+import { PresBox } from './trails/PresBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -114,29 +115,18 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
 }
 
 @observer
-export class DocumentContentsView extends React.Component<
+export class DocumentContentsView extends ObservableReactComponent<
     DocumentViewProps &
         FieldViewProps & {
             setHeight?: (height: number) => void;
             layout_fieldKey: string;
         }
 > {
-    @observable _props!: DocumentViewProps &
-        FieldViewProps & {
-            setHeight?: (height: number) => void;
-            layout_fieldKey: string;
-        };
     constructor(props: any) {
         super(props);
-        this._props = props;
         makeObservable(this);
     }
 
-    componentDidUpdate(prevProps: Readonly<DocumentViewProps & { setHeight?: ((height: number) => void) | undefined; layout_fieldKey: string }>, prevState: Readonly<{}>, snapshot?: any): void {
-        // untracked(() => (this._props = this.props));
-        //   Object.keys(prevProps).forEach(pkey => (prevProps as any)[pkey] !== (this._props as any)[pkey] && console.log(pkey + ' ' + (prevProps as any)[pkey] + ' ' + (this._props as any)[pkey]));
-    }
-
     @computed get layout(): string {
         TraceMobx();
         if (this._props.LayoutTemplateString) return this._props.LayoutTemplateString;
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 9ec4d8c13..dfd610581 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -1,34 +1,28 @@
+import { Tooltip } from '@mui/material';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { DocumentView } from './DocumentView';
-import { DocumentManager } from '../../util/DocumentManager';
-import { Transformer, ts } from '../../util/Scripting';
+import { factory } from 'typescript';
 import { Field } from '../../../fields/Doc';
-import { Tooltip } from '@mui/material';
-import { action, makeObservable, observable } from 'mobx';
 import { Id } from '../../../fields/FieldSymbols';
-import { factory } from 'typescript';
-import { LightboxView } from '../LightboxView';
+import { DocumentManager } from '../../util/DocumentManager';
+import { Transformer, ts } from '../../util/Scripting';
 import { SettingsManager } from '../../util/SettingsManager';
-import { copyProps } from '../../../Utils';
+import { LightboxView } from '../LightboxView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { DocumentView } from './DocumentView';
 
 interface DocumentIconProps {
     view: DocumentView;
     index: number;
 }
 @observer
-export class DocumentIcon extends React.Component<DocumentIconProps> {
+export class DocumentIcon extends ObservableReactComponent<DocumentIconProps> {
     @observable _hovered = false;
-    _prevProps: DocumentIconProps;
-    @observable _props: DocumentIconProps;
-    constructor(props: DocumentIconProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     static get DocViews() {
         return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.IsLightboxDocView(v._props.docViewPath())) : DocumentManager.Instance.DocumentViews;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 19b43fd52..165057d21 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -2,20 +2,21 @@ 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 { StopEvent, emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { StrCast } from '../../../fields/Types';
-import { copyProps, emptyFunction, returnFalse, setupMoveUpEvents, StopEvent } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { Hypothesis } from '../../util/HypothesisUtils';
 import { LinkManager } from '../../util/LinkManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../util/UndoManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import './DocumentLinksButton.scss';
 import { DocumentView } from './DocumentView';
 import { LinkDescriptionPopup } from './LinkDescriptionPopup';
 import { TaskCompletionBox } from './TaskCompletedBox';
 import { PinProps } from './trails';
-import * as React from 'react';
 
 interface DocumentLinksButtonProps {
     View: DocumentView;
@@ -44,26 +45,18 @@ export class DocButtonState {
     }
 }
 @observer
-export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
+export class DocumentLinksButton extends ObservableReactComponent<DocumentLinksButtonProps> {
     private _linkButton = React.createRef<HTMLDivElement>();
     public static get StartLink() { return DocButtonState.Instance.StartLink; } // prettier-ignore
     public static set StartLink(value) { runInAction(() => (DocButtonState.Instance.StartLink = value)); } // prettier-ignore
     @observable public static StartLinkView: DocumentView | undefined = undefined;
     @observable public static AnnotationId: string | undefined = undefined;
     @observable public static AnnotationUri: string | undefined = undefined;
-
-    _prevProps: React.PropsWithChildren<DocumentLinksButtonProps>;
-    @observable _props: React.PropsWithChildren<DocumentLinksButtonProps>;
-    constructor(props: React.PropsWithChildren<DocumentLinksButtonProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     @undoBatch
     onLinkButtonMoved = (e: PointerEvent) => {
         if (this._props.InMenu && this._props.StartLink) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 08ee708f7..343f770d5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,9 +1,11 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { Dropdown, DropdownType, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction, runInAction, untracked } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 // import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
+import * as React from 'react';
+import { Utils, emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick } from '../../../Utils';
 import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
 import { AclPrivate, Animation, AudioPlay, DocViews } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
@@ -15,12 +17,11 @@ import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { copyProps, emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
-import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { DocServer } from '../../DocServer';
-import { DocOptions, Docs, DocUtils, FInfo } from '../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
+import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocOptions, DocUtils, Docs, FInfo } from '../../documents/Documents';
 import { DictationManager } from '../../util/DictationManager';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
@@ -32,13 +33,14 @@ import { SettingsManager } from '../../util/SettingsManager';
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { DocComponent } from '../DocComponent';
 import { EditableView } from '../EditableView';
 import { GestureOverlay } from '../GestureOverlay';
 import { LightboxView } from '../LightboxView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { StyleProp } from '../StyleProvider';
 import { UndoStack } from '../UndoStack';
 import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
@@ -46,12 +48,11 @@ import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView'
 import { DocumentLinksButton } from './DocumentLinksButton';
 import './DocumentView.scss';
 import { FieldViewProps } from './FieldView';
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { KeyValueBox } from './KeyValueBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { PresEffect, PresEffectDirection } from './trails';
 import { PinProps, PresBox } from './trails/PresBox';
-import * as React from 'react';
 const { Howl } = require('howler');
 
 interface Window {
@@ -253,20 +254,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     private _mainCont = React.createRef<HTMLDivElement>();
     private _titleRef = React.createRef<EditableView>();
     private _dropDisposer?: DragManager.DragDropDisposer;
-
-    @override _props: DocumentViewInternalProps;
-    _prevProps: DocumentViewInternalProps;
-
-    constructor(props: DocumentViewInternalProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        // untracked(() => (this._props = this._props));
-    }
-
     @observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
     @observable _animateScaleTime: Opt<number>; // milliseconds for animating between views.  defaults to 300 if not uset
     @observable _animateScalingTo = 0;
@@ -1329,19 +1321,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 }
 
 @observer
-export class DocumentView extends React.Component<DocumentViewProps> {
+export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
     public static ROOT_DIV = 'documentView-effectsWrapper';
 
-    @observable _props: DocumentViewProps;
-    _prevProps: DocumentViewProps;
     constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     @observable _selected = false;
     public get SELECTED() {
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index 02ed56333..ff92c701f 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -1,5 +1,4 @@
-import EquationEditor from './formattedText/EquationEditor';
-import { action, makeObservable, override, reaction } from 'mobx';
+import { action, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Id } from '../../../fields/FieldSymbols';
@@ -11,7 +10,7 @@ import { ViewBoxBaseComponent } from '../DocComponent';
 import { LightboxView } from '../LightboxView';
 import './EquationBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
-import { copyProps } from '../../../Utils';
+import EquationEditor from './formattedText/EquationEditor';
 
 @observer
 export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -21,18 +20,11 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static SelectOnLoad: string = '';
     _ref: React.RefObject<EquationEditor> = React.createRef();
 
-    _prevProps: React.PropsWithChildren<FieldViewProps>;
-    @override _props: React.PropsWithChildren<FieldViewProps>;
-    constructor(props: React.PropsWithChildren<FieldViewProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     componentDidMount() {
         this._props.setContentView?.(this);
         if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()))) {
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index d8b1f125c..5a8665aaf 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -1,13 +1,13 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { computed, makeObservable, observable, override } from 'mobx';
+import { computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { copyProps, emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { SettingsManager } from '../../../util/SettingsManager';
@@ -45,16 +45,11 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(FontIconBox, fieldKey);
     }
-    _prevProps: React.PropsWithChildren<ButtonProps>;
-    @override _props: React.PropsWithChildren<ButtonProps>;
-    constructor(props: React.PropsWithChildren<ButtonProps>) {
+
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
     //
     // This controls whether fontIconButtons will display labels under their icons or not
     //
@@ -64,6 +59,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
     public static set ShowIconLabels(show: boolean) {
         Doc.UserDoc()._showLabel = show;
     }
+
     @observable noTooltip = false;
     showTemplate = (): void => {
         const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index a04c27e01..29bffb583 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -1,15 +1,12 @@
 import functionPlot from 'function-plot';
-import { action, computed, makeObservable, override, reaction } from 'mobx';
+import { computed, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
-import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
-import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { listSpec } from '../../../fields/Schema';
 import { Cast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { copyProps } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
@@ -27,19 +24,12 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
     _plotId = '';
     _plotEle: any;
 
-    _prevProps: React.PropsWithChildren<FieldViewProps>;
-    @override _props: React.PropsWithChildren<FieldViewProps>;
     constructor(props: React.PropsWithChildren<FieldViewProps>) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         this._plotId = 'graph' + FunctionPlotBox.GraphCount++;
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     componentDidMount() {
         this._props.setContentView?.(this);
         reaction(
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 091c1a32f..b1ccd38ba 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, override, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { extname } from 'path';
 import * as React from 'react';
@@ -12,7 +12,7 @@ import { ObjectField } from '../../../fields/ObjectField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { copyProps, DashColor, emptyFunction, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
+import { DashColor, emptyFunction, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -55,19 +55,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     private _marqueeref = React.createRef<MarqueeAnnotator>();
     @observable _curSuffix = '';
 
-    _prevProps: ViewBoxAnnotatableProps & FieldViewProps;
-    @override _props: ViewBoxAnnotatableProps & FieldViewProps;
     constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
         super(props);
-        this._props = this._prevProps = props;
-        makeObservable(this);
         this._props.setContentView?.(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer?.();
         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.Document));
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 9aab53daf..73fdc3a23 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -1,26 +1,27 @@
 import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { returnAlways, returnTrue } from '../../../Utils';
 import { Doc, Field, FieldResult } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { DocCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
-import { copyProps, returnAll, returnAlways, returnTrue } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { SetupDrag } from '../../util/DragManager';
-import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripting';
+import { CompileScript, CompiledScript, ScriptOptions } from '../../util/Scripting';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DocumentIconContainer } from './DocumentIcon';
 import { OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { ImageBox } from './ImageBox';
 import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
-import * as React from 'react';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
 
 export type KVPScript = {
     script: CompiledScript;
@@ -28,28 +29,20 @@ export type KVPScript = {
     onDelegate: boolean;
 };
 @observer
-export class KeyValueBox extends React.Component<FieldViewProps> {
+export class KeyValueBox extends ObservableReactComponent<FieldViewProps> {
     public static LayoutString() {
         return FieldView.LayoutString(KeyValueBox, 'data');
     }
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
 
     private _mainCont = React.createRef<HTMLDivElement>();
     private _keyHeader = React.createRef<HTMLTableHeaderCellElement>();
     private _keyInput = React.createRef<HTMLInputElement>();
     private _valInput = React.createRef<HTMLInputElement>();
 
-    _prevProps: FieldViewProps;
-    @observable _props: FieldViewProps;
-    constructor(props: FieldViewProps) {
-        super(props);
-        this._props = this._prevProps = props;
-        makeObservable(this);
-    }
-
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     componentDidMount() {
         this._props.setContentView?.(this);
     }
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 40991f371..fd8d8ef56 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,21 +1,23 @@
-import { action, makeObservable, observable, toJS } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import { ObservableGroupMap } from 'mobx-utils';
+import * as React from 'react';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../Utils';
 import { Doc, Field } from '../../../fields/Doc';
-import { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../Utils';
+import { DocCast } from '../../../fields/Types';
+import { DocumentOptions, FInfo } from '../../documents/Documents';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { EditableView } from '../EditableView';
 import { DefaultStyleProvider } from '../StyleProvider';
-import { OpenWhere, OpenWhereMod } from './DocumentView';
-import { FieldView, FieldViewProps } from './FieldView';
+import { OpenWhere } from './DocumentView';
+import { FieldViewProps } from './FieldView';
 import { KeyValueBox } from './KeyValueBox';
 import './KeyValueBox.scss';
 import './KeyValuePair.scss';
-import * as React from 'react';
-import { DocCast } from '../../../fields/Types';
-import { Tooltip } from '@mui/material';
-import { DocumentOptions, FInfo } from '../../documents/Documents';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 
 // Represents one row in a key value plane
 
@@ -29,22 +31,15 @@ export interface KeyValuePairProps {
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
 }
 @observer
-export class KeyValuePair extends React.Component<KeyValuePairProps> {
+export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
     @observable private isPointerOver = false;
     @observable public isChecked = false;
     private checkbox = React.createRef<HTMLInputElement>();
-
-    _prevProps: KeyValuePairProps;
-    @observable _props: KeyValuePairProps;
-    constructor(props: KeyValuePairProps) {
+    constructor(props:any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     @action
     handleCheck = (e: React.ChangeEvent<HTMLInputElement>) => {
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 52ca8b5b1..934bce448 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -1,11 +1,10 @@
-import { action, computed, makeObservable, observable, override } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
-import { Cast, StrCast, NumCast, BoolCast } from '../../../fields/Types';
-import { copyProps } from '../../../Utils';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
@@ -31,16 +30,11 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
     private dropDisposer?: DragManager.DragDropDisposer;
     private _timeout: any;
 
-    _prevProps: React.PropsWithChildren<FieldViewProps & LabelBoxProps>;
-    @override _props: React.PropsWithChildren<FieldViewProps & LabelBoxProps>;
-    constructor(props: React.PropsWithChildren<FieldViewProps & LabelBoxProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
+
     componentDidMount() {
         this._props.setContentView?.(this);
     }
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index a7e905fca..b86ba72a0 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,9 +1,9 @@
-import { action, computed, makeObservable, observable, override } from 'mobx';
-import { observer } from 'mobx-react';
+import { action, computed, makeObservable } from 'mobx';
+import * as React from 'react';
+import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { LinkFollower } from '../../util/LinkFollower';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -12,11 +12,7 @@ import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LinkAnchorBox.scss';
 import { LinkInfo } from './LinkDocPreview';
-import * as React from 'react';
-const {
-    default: { MEDIUM_GRAY },
-} = require('../global/globalCssVariables.module.scss');
-@observer
+const { default: { MEDIUM_GRAY }, } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
 export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(LinkAnchorBox, fieldKey);
@@ -26,14 +22,9 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     _ref = React.createRef<HTMLDivElement>();
     _isOpen = false;
     _timeout: NodeJS.Timeout | undefined;
-    @observable _x = 0;
-    @observable _y = 0;
 
-    _prevProps: FieldViewProps;
-    @override _props: FieldViewProps;
-    constructor(props: FieldViewProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index acafd6d09..134f2e14a 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,10 +1,10 @@
-import * as React from 'react';
 import { Bezier } from 'bezier-js';
-import { computed, IReactionDisposer, makeObservable, observable, override, reaction } from 'mobx';
+import { computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Id } from '../../../fields/FieldSymbols';
 import { DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { aggregateBounds, copyProps, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils';
+import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils';
 import { DocumentManager } from '../../util/DocumentManager';
 import { Transform } from '../../util/Transform';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm';
@@ -20,18 +20,11 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return FieldView.LayoutString(LinkBox, fieldKey);
     }
 
-    _prevProps: FieldViewProps;
-    @override _props: FieldViewProps;
-    constructor(props: FieldViewProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     onClickScriptDisable = returnAlways;
     @computed get anchor1() {
         const anchor1 = DocCast(this.dataDoc.link_anchor_1);
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 108930ad9..fa7a55bc7 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -19,6 +19,7 @@ import { Transform } from '../../util/Transform';
 import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
 import './LinkDocPreview.scss';
 import * as React from 'react';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 
 export class LinkInfo {
     private static _instance: Opt<LinkInfo>;
@@ -49,15 +50,7 @@ interface LinkDocPreviewProps {
     noPreview?: boolean;
 }
 @observer
-export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
-    _prevProps: LinkDocPreviewProps;
-    @observable _props: LinkDocPreviewProps;
-    constructor(props: LinkDocPreviewProps) {
-        super(props);
-        this._props = this._prevProps = props;
-        makeObservable(this);
-    }
-
+export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps> {
     _infoRef = React.createRef<HTMLDivElement>();
     _linkDocRef = React.createRef<HTMLDivElement>();
     @observable _targetDoc: Opt<Doc> = undefined;
@@ -66,6 +59,10 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     @observable _linkSrc: Opt<Doc> = undefined;
     @observable _toolTipText = '';
     @observable _hrefInd = 0;
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
 
     @action
     init() {
@@ -84,9 +81,9 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         this._toolTipText = '';
         this.updateHref();
     }
-    componentDidUpdate(props: any) {
-        copyProps(this);
-        if (props.linkSrc !== this._props.linkSrc || props.linkDoc !== this._props.linkDoc || props.hrefs !== this._props.hrefs) this.init();
+    componentDidUpdate(prevProps: Readonly<LinkDocPreviewProps>) {
+        copyProps(this, prevProps);
+        if (prevProps.linkSrc !== this._props.linkSrc || prevProps.linkDoc !== this._props.linkDoc || prevProps.hrefs !== this._props.hrefs) this.init();
     }
     componentDidMount() {
         this.init();
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index ee1ea3ceb..b458e5a28 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,9 +1,9 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, Opt } from '../../../../fields/Doc';
+import * as React from 'react';
 import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils';
+import { Doc, Opt } from '../../../../fields/Doc';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 // import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup';
@@ -36,7 +36,6 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     constructor(props: Readonly<{}>) {
         super(props);
-
         MapAnchorMenu.Instance = this;
         MapAnchorMenu.Instance._canFade = false;
     }
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 98a302834..a420e0101 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,24 +1,24 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import BingMapsReact from 'bingmaps-react';
 import { Button, EditableText, IconButton, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, override, reaction, runInAction } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents } from '../../../../Utils';
 import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
 import { DocCss, Highlight } from '../../../../fields/DocSymbols';
 import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { Docs, DocUtils } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
+import { DocUtils, Docs } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { LinkManager } from '../../../util/LinkManager';
 import { Transform } from '../../../util/Transform';
-import { undoable, UndoManager } from '../../../util/UndoManager';
-import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
+import { UndoManager, undoable } from '../../../util/UndoManager';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
-import { Colors } from '../../global/globalEnums';
 import { SidebarAnnos } from '../../SidebarAnnos';
+import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
+import { Colors } from '../../global/globalEnums';
 import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { FormattedTextBox } from '../formattedText/FormattedTextBox';
@@ -67,18 +67,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [key: string]: IReactionDisposer } = {};
 
-    _unmounting = false;
-    _prevProps: ViewBoxAnnotatableProps & FieldViewProps;
-    @override _props: ViewBoxAnnotatableProps & FieldViewProps;
-    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
+    _unmounting = false;
 
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @computed get allSidebarDocs() {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index a453210eb..213f88177 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,8 +1,9 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as Pdfjs from 'pdfjs-dist';
 import 'pdfjs-dist/web/pdf_viewer.css';
+import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
@@ -10,7 +11,7 @@ import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, FieldValue, ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField, PdfField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { copyProps, emptyFunction, returnFalse, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, returnFalse, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -31,7 +32,6 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { ImageBox } from './ImageBox';
 import './PDFBox.scss';
 import { PinProps, PresBox } from './trails';
-import * as React from 'react';
 
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -58,11 +58,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url;
     }
 
-    _prevProps: ViewBoxAnnotatableProps & FieldViewProps;
-    @override _props: ViewBoxAnnotatableProps & FieldViewProps;
     constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         const nw = Doc.NativeWidth(this.Document, this.dataDoc) || 927;
         const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200;
@@ -72,9 +69,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => (this._pdf = pdf)));
         }
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     replaceCanvases = (oldDiv: HTMLElement, newDiv: HTMLElement) => {
         if (oldDiv.childNodes) {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index a4f4897ba..ebe86318d 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,15 +1,15 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, override, reaction, runInAction, untracked } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
 import { observer } from 'mobx-react';
 import { basename } from 'path';
+import * as React from 'react';
 import { Doc, StrListCast } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { AudioField, ImageField, VideoField } from '../../../fields/URLField';
-import { copyProps, emptyFunction, formatTime, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, formatTime, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
@@ -68,19 +68,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     private _playRegionTimer: any = null; // timeout for playback
     private _controlsFadeTimer: any = null; // timeout for controls fade
 
-    _prevProps: ViewBoxAnnotatableProps & FieldViewProps;
-    @override _props: ViewBoxAnnotatableProps & FieldViewProps;
     constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         this._props.setContentView?.(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     @observable _stackedTimeline: any; // CollectionStackedTimeline ref
     @observable static _nativeControls: boolean; // default html controls
     @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 995f9f6e0..2f92f1edb 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, override, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as WebRequest from 'web-request';
@@ -13,7 +13,7 @@ import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast, WebCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, copyProps, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
@@ -98,19 +98,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return Cast(this.Document[this._props.fieldKey], WebField)?.url;
     }
 
-    _prevProps: ViewBoxAnnotatableProps & FieldViewProps;
-    @override _props: ViewBoxAnnotatableProps & FieldViewProps;
     constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
         super(props);
-        this._props = this._prevProps = 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.
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     @action
     search = (searchString: string, bwd?: boolean, clear: boolean = false) => {
         if (!this._searching && !clear) {
diff --git a/src/client/views/nodes/audio/AudioWaveform.tsx b/src/client/views/nodes/audio/AudioWaveform.tsx
index 1b1a85800..01392c4a5 100644
--- a/src/client/views/nodes/audio/AudioWaveform.tsx
+++ b/src/client/views/nodes/audio/AudioWaveform.tsx
@@ -1,15 +1,16 @@
 import axios from 'axios';
-import { computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { computed, IReactionDisposer, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, NumListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { Cast } from '../../../../fields/Types';
-import { copyProps, numberRange } from '../../../../Utils';
+import { numberRange } from '../../../../Utils';
 import { Colors } from './../../global/globalEnums';
 import './AudioWaveform.scss';
 import { WaveCanvas } from './WaveCanvas';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
 
 /**
  * AudioWaveform
@@ -35,21 +36,15 @@ export interface AudioWaveformProps {
 }
 
 @observer
-export class AudioWaveform extends React.Component<AudioWaveformProps> {
+export class AudioWaveform extends ObservableReactComponent<AudioWaveformProps> {
     public static NUMBER_OF_BUCKETS = 100; // number of buckets data is divided into to draw waveform lines
     _disposer: IReactionDisposer | undefined;
-    _prevProps: React.PropsWithChildren<AudioWaveformProps>;
-    @observable _props: React.PropsWithChildren<AudioWaveformProps>;
-    constructor(props: React.PropsWithChildren<AudioWaveformProps>) {
+
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     get waveHeight() {
         return Math.max(50, this._props.PanelHeight);
     }
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index e2cceb906..555b752f0 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -2,22 +2,23 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { Cast, StrCast } from '../../../../fields/Types';
-import { copyProps, emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
+import { Cast } from '../../../../fields/Types';
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { Transform } from '../../../util/Transform';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { OpenWhere } from '../DocumentView';
 import './DashFieldView.scss';
 import { FormattedTextBox } from './FormattedTextBox';
-import * as React from 'react';
-import { Transform } from '../../../util/Transform';
 
 export class DashFieldView {
     dom: HTMLDivElement; // container for label and value
@@ -92,7 +93,7 @@ interface IDashFieldViewInternal {
 }
 
 @observer
-export class DashFieldViewInternal extends React.Component<IDashFieldViewInternal> {
+export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldViewInternal> {
     _reactionDisposer: IReactionDisposer | undefined;
     _textBoxDoc: Doc;
     _fieldKey: string;
@@ -100,11 +101,8 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     @observable _dashDoc: Doc | undefined = undefined;
     @observable _expanded = false;
 
-    _prevProps: IDashFieldViewInternal;
-    @observable _props: IDashFieldViewInternal;
     constructor(props: IDashFieldViewInternal) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         this._fieldKey = this._props.fieldKey;
         this._textBoxDoc = this._props.tbox.Document;
@@ -115,9 +113,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             this._dashDoc = this._props.tbox.Document;
         }
     }
-    componentDidUpdate(prevProps: Readonly<IDashFieldViewInternal>, prevState: Readonly<{}>, snapshot?: any): void {
-        copyProps(this);
-    }
+
     componentWillUnmount() {
         this._reactionDisposer?.();
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 56bf15b09..997c3f86d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { isEqual } from 'lodash';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, override, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { baseKeymap, selectAll } from 'prosemirror-commands';
 import { history } from 'prosemirror-history';
@@ -14,7 +14,7 @@ import { EditorView } from 'prosemirror-view';
 import { BsMarkdownFill } from 'react-icons/bs';
 import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -24,7 +24,7 @@ import { RichTextUtils } from '../../../../fields/RichTextUtils';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, copyProps, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
 import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
 import { DocServer } from '../../../DocServer';
@@ -55,7 +55,7 @@ import { StyleProp } from '../../StyleProvider';
 import { media_state } from '../AudioBox';
 import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
-import { LinkDocPreview, LinkInfo } from '../LinkDocPreview';
+import { LinkInfo } from '../LinkDocPreview';
 import { PinProps, PresBox } from '../trails';
 import { DashDocCommentView } from './DashDocCommentView';
 import { DashDocView } from './DashDocView';
@@ -208,20 +208,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return url.startsWith(document.location.origin) ? new URL(url).pathname.split('doc/').lastElement() : ''; // docId
     }
 
-    _prevProps: React.PropsWithChildren<FieldViewProps & FormattedTextBoxProps>;
-    @override _props: React.PropsWithChildren<FieldViewProps & FormattedTextBoxProps>;
     constructor(props: React.PropsWithChildren<FieldViewProps & FormattedTextBoxProps>) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         FormattedTextBox.Instance = this;
         this._recordingStart = Date.now();
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     // removes all hyperlink anchors for the removed linkDoc
     // TODO: bcz: Argh... if a section of text has multiple anchors, this should just remove the intended one.
     // but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing.
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 7de1eb717..4881070fd 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,21 +1,22 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { lift, wrapIn } from 'prosemirror-commands';
 import { Mark, MarkType, Node as ProsNode, ResolvedPos } from 'prosemirror-model';
 import { wrapInList } from 'prosemirror-schema-list';
 import { EditorState, NodeSelection, TextSelection } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
+import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
-import { copyProps, numberRange } from '../../../../Utils';
+import { numberRange } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { LinkManager } from '../../../util/LinkManager';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { EquationBox } from '../EquationBox';
 import { FieldViewProps } from '../FieldView';
 import { FormattedTextBox } from './FormattedTextBox';
@@ -63,18 +64,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable private showLinkDropdown: boolean = false;
 
     _reaction: IReactionDisposer | undefined;
-    _prevProps: AntimodeMenuProps;
-    @override _props: AntimodeMenuProps;
     constructor(props: AntimodeMenuProps) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
-        runInAction(() => {
-            RichTextMenu.Instance = this;
-            this.updateMenu(undefined, undefined, props, this.layoutDoc);
-            this._canFade = false;
-            this.Pinned = true;
-        });
+        RichTextMenu.Instance = this;
+        this.updateMenu(undefined, undefined, props, this.layoutDoc);
+        this._canFade = false;
+        this.Pinned = true;
     }
 
     @computed get noAutoLink() {
@@ -111,9 +107,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         return BoolCast(this.layoutDoc?.layout_centered);
     }
     _disposer: IReactionDisposer | undefined;
-    componentDidUpdate() {
-        copyProps(this);
-    }
     componentDidMount() {
         this._disposer = reaction(
             () => SelectionManager.Views.slice(),
@@ -368,7 +361,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         } 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);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
 
     setFontFamily = (family: string) => {
@@ -377,7 +370,7 @@ 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 Doc.UserDoc().fontFamily = family;
-        this.updateMenu(this.view, undefined, this._props, this.layoutDoc);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
 
     setHighlight(color: string) {
@@ -386,7 +379,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.setMark(highlightMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(highlightMark)), true);
             this.view.focus();
         } else Doc.UserDoc()._fontHighlight = color;
-        this.updateMenu(this.view, undefined, this._props, this.layoutDoc);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     }
 
     setColor(color: string) {
@@ -395,7 +388,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.setMark(colorMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(colorMark)), true);
             this.view.focus();
         } else Doc.UserDoc().fontColor = color;
-        this.updateMenu(this.view, undefined, this._props, this.layoutDoc);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     }
 
     // TODO: remove doesn't work
@@ -436,7 +429,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             }
         }
         this.view.focus();
-        this.updateMenu(this.view, undefined, this._props, this.layoutDoc);
+        this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
 
     insertSummarizer(state: EditorState, dispatch: any) {
@@ -818,22 +811,15 @@ interface ButtonDropdownProps {
 }
 
 @observer
-export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
+export class ButtonDropdown extends ObservableReactComponent<ButtonDropdownProps> {
     @observable private showDropdown: boolean = false;
     private ref: HTMLDivElement | null = null;
 
-    _prevProps: React.PropsWithChildren<ButtonDropdownProps>;
-    @observable _props: React.PropsWithChildren<ButtonDropdownProps>;
-    constructor(props: React.PropsWithChildren<ButtonDropdownProps>) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     componentDidMount() {
         document.addEventListener('pointerdown', this.onBlur);
     }
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 9fcb496b8..ec5d090dd 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -1,12 +1,13 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { copyProps, emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
@@ -24,7 +25,6 @@ import { StyleProp } from '../../StyleProvider';
 import { PresBox } from './PresBox';
 import './PresElementBox.scss';
 import { PresMovement } from './PresEnums';
-import * as React from 'react';
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -41,19 +41,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     readonly expandViewHeight = 100;
     readonly collapsedHeight = 35;
 
-    @observable _dragging = false;
-
-    _prevProps: FieldViewProps;
-    @override _props: FieldViewProps;
-    constructor(props: FieldViewProps) {
+    constructor(props: any) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
     }
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
+    @observable _dragging = false;
 
     // the presentation view that renders this slide
     @computed get presBoxView() {
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 0627876fa..a8c83ded6 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -1,12 +1,12 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, override, reaction } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { ColorResult } from 'react-color';
+import { Utils, returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../Utils';
 import { Doc, Opt } from '../../../fields/Doc';
-import { copyProps, returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../Utils';
-import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT';
+import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
@@ -23,18 +23,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     private _commentRef = React.createRef<HTMLDivElement>();
     private _cropRef = React.createRef<HTMLDivElement>();
 
-    _prevProps: AntimodeMenuProps;
-    @override _props: AntimodeMenuProps;
     constructor(props: AntimodeMenuProps) {
         super(props);
-        this._props = this._prevProps = props;
         makeObservable(this);
         AnchorMenu.Instance = this;
         AnchorMenu.Instance._canFade = false;
     }
-    componentDidUpdate() {
-        copyProps(this);
-    }
 
     @observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)';
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a581e9df6..e342c25b3 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,13 +1,16 @@
 import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as Pdfjs from 'pdfjs-dist';
 import 'pdfjs-dist/web/pdf_viewer.css';
+import * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs';
+import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Height } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, copyProps, emptyFunction, returnAll, returnFalse, returnNone, returnZero, smoothScroll, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnAll, returnFalse, returnNone, returnZero, smoothScroll, Utils } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -16,15 +19,13 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
-import { LinkDocPreview, LinkInfo } from '../nodes/LinkDocPreview';
+import { LinkInfo } from '../nodes/LinkDocPreview';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { StyleProp } from '../StyleProvider';
 import { AnchorMenu } from './AnchorMenu';
 import { Annotation } from './Annotation';
 import { GPTPopup } from './GPTPopup/GPTPopup';
 import './PDFViewer.scss';
-import * as React from 'react';
-import * as Pdfjs from 'pdfjs-dist';
-import * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
@@ -49,14 +50,11 @@ interface IViewerProps extends FieldViewProps {
  * Handles rendering and virtualization of the pdf
  */
 @observer
-export class PDFViewer extends React.Component<IViewerProps> {
+export class PDFViewer extends ObservableReactComponent<IViewerProps> {
     static _annotationStyle: any = addStyleSheet();
 
-    _prevProps: IViewerProps;
-    @observable _props: IViewerProps;
-    constructor(props: IViewerProps) {
+    constructor(props: any) {
         super(props);
-        this._prevProps = this._props = props;
         makeObservable(this);
     }
 
@@ -66,10 +64,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @observable _showWaiting = true;
     @observable Index: number = -1;
 
-    componentDidUpdate() {
-        copyProps(this);
-    }
-
     private _pdfViewer: any;
     private _styleRule: any; // stylesheet rule for making hyperlinks clickable
     private _retries = 0; // number of times tried to create the PDF viewer
-- 
cgit v1.2.3-70-g09d2


From 4a1d64e470f7f8fae90e5014f3e6e64c5ffea2c8 Mon Sep 17 00:00:00 2001
From: zaultavangar <zaul_tavangar@brown.edu>
Date: Sat, 16 Dec 2023 17:26:32 -0500
Subject: more functionality for calendar feature

---
 package-lock.json                                  | 3025 +++++++++++++++++---
 package.json                                       |   10 +-
 src/client/documents/DocumentTypes.ts              |    2 +
 src/client/documents/Documents.ts                  |   11 +
 src/client/util/CalendarManager.tsx                |  153 +-
 src/client/views/DashboardView.tsx                 |   31 +-
 .../views/collections/CollectionDockingView.tsx    |    1 +
 src/fields/Doc.ts                                  |    1 +
 8 files changed, 2790 insertions(+), 444 deletions(-)

(limited to 'src/client/views/DashboardView.tsx')

diff --git a/package-lock.json b/package-lock.json
index 9dec742fb..29ab1ff66 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4,6 +4,78 @@
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {
+        "@adobe/react-spectrum": {
+            "version": "3.32.2",
+            "resolved": "https://registry.npmjs.org/@adobe/react-spectrum/-/react-spectrum-3.32.2.tgz",
+            "integrity": "sha512-dKDJquOlpATCe09Tf+hWwPRCFZOtIFQB0Sj79lf4ypUHONuHZCTzBz287OmnzflbvlRD8igUVru76e+xORD+YQ==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/ssr": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/visually-hidden": "^3.8.7",
+                "@react-spectrum/actionbar": "^3.4.0",
+                "@react-spectrum/actiongroup": "^3.10.0",
+                "@react-spectrum/avatar": "^3.0.7",
+                "@react-spectrum/badge": "^3.1.8",
+                "@react-spectrum/breadcrumbs": "^3.9.2",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/buttongroup": "^3.6.8",
+                "@react-spectrum/calendar": "^3.4.3",
+                "@react-spectrum/checkbox": "^3.9.0",
+                "@react-spectrum/combobox": "^3.11.2",
+                "@react-spectrum/contextualhelp": "^3.6.5",
+                "@react-spectrum/datepicker": "^3.9.0",
+                "@react-spectrum/dialog": "^3.8.5",
+                "@react-spectrum/divider": "^3.5.8",
+                "@react-spectrum/dnd": "^3.3.5",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/icon": "^3.7.8",
+                "@react-spectrum/illustratedmessage": "^3.4.8",
+                "@react-spectrum/image": "^3.4.8",
+                "@react-spectrum/inlinealert": "^3.2.0",
+                "@react-spectrum/labeledvalue": "^3.1.8",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/link": "^3.6.2",
+                "@react-spectrum/list": "^3.7.4",
+                "@react-spectrum/listbox": "^3.12.4",
+                "@react-spectrum/menu": "^3.16.0",
+                "@react-spectrum/meter": "^3.4.8",
+                "@react-spectrum/numberfield": "^3.8.0",
+                "@react-spectrum/overlays": "^5.5.2",
+                "@react-spectrum/picker": "^3.13.2",
+                "@react-spectrum/progress": "^3.7.2",
+                "@react-spectrum/provider": "^3.9.2",
+                "@react-spectrum/radio": "^3.7.0",
+                "@react-spectrum/searchfield": "^3.8.0",
+                "@react-spectrum/slider": "^3.6.4",
+                "@react-spectrum/statuslight": "^3.5.8",
+                "@react-spectrum/switch": "^3.5.0",
+                "@react-spectrum/table": "^3.12.4",
+                "@react-spectrum/tabs": "^3.8.4",
+                "@react-spectrum/tag": "^3.2.0",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/textfield": "^3.11.0",
+                "@react-spectrum/theme-dark": "^3.5.7",
+                "@react-spectrum/theme-default": "^3.5.7",
+                "@react-spectrum/theme-light": "^3.4.7",
+                "@react-spectrum/tooltip": "^3.6.2",
+                "@react-spectrum/view": "^3.6.5",
+                "@react-spectrum/well": "^3.4.8",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/data": "^3.11.0",
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@adobe/react-spectrum-ui": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/@adobe/react-spectrum-ui/-/react-spectrum-ui-1.2.0.tgz",
+            "integrity": "sha512-os3EdjfyJbrukLcZ5uYtdFRiDlLB3zq2JoXp19J/IDpZ8btibJeRZYSwjL+LscEiT2pOYaF2McMQdkZTIwnllw=="
+        },
+        "@adobe/react-spectrum-workflow": {
+            "version": "2.3.4",
+            "resolved": "https://registry.npmjs.org/@adobe/react-spectrum-workflow/-/react-spectrum-workflow-2.3.4.tgz",
+            "integrity": "sha512-XPLzIBl58HdLF9WIPB7RDAvVXvCE3SjG+HaWQhW2P9MnxSz1DEA9O7mlTlYblJkMbfk10T/+RFaSupc1yoN+TA=="
+        },
         "@ampproject/remapping": {
             "version": "2.2.1",
             "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
@@ -2879,6 +2951,50 @@
             "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.2.tgz",
             "integrity": "sha512-ou3elfqG/hZsbmF4bxeJhPHIf3G2pm0ujc39hYEZrfVqt7Vk/Zji6CXc3W0pmYM8BW1g40U+akTl9DKZhFhInQ=="
         },
+        "@formatjs/ecma402-abstract": {
+            "version": "1.18.0",
+            "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.0.tgz",
+            "integrity": "sha512-PEVLoa3zBevWSCZzPIM/lvPCi8P5l4G+NXQMc/CjEiaCWgyHieUoo0nM7Bs0n/NbuQ6JpXEolivQ9pKSBHaDlA==",
+            "requires": {
+                "@formatjs/intl-localematcher": "0.5.2",
+                "tslib": "^2.4.0"
+            }
+        },
+        "@formatjs/fast-memoize": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz",
+            "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==",
+            "requires": {
+                "tslib": "^2.4.0"
+            }
+        },
+        "@formatjs/icu-messageformat-parser": {
+            "version": "2.7.3",
+            "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.3.tgz",
+            "integrity": "sha512-X/jy10V9S/vW+qlplqhMUxR8wErQ0mmIYSq4mrjpjDl9mbuGcCILcI1SUYkL5nlM4PJqpc0KOS0bFkkJNPxYRw==",
+            "requires": {
+                "@formatjs/ecma402-abstract": "1.18.0",
+                "@formatjs/icu-skeleton-parser": "1.7.0",
+                "tslib": "^2.4.0"
+            }
+        },
+        "@formatjs/icu-skeleton-parser": {
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.7.0.tgz",
+            "integrity": "sha512-Cfdo/fgbZzpN/jlN/ptQVe0lRHora+8ezrEeg2RfrNjyp+YStwBy7cqDY8k5/z2LzXg6O0AdzAV91XS0zIWv+A==",
+            "requires": {
+                "@formatjs/ecma402-abstract": "1.18.0",
+                "tslib": "^2.4.0"
+            }
+        },
+        "@formatjs/intl-localematcher": {
+            "version": "0.5.2",
+            "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.2.tgz",
+            "integrity": "sha512-txaaE2fiBMagLrR4jYhxzFO6wEdEG4TPMqrzBAcbr4HFUYzH/YC+lg6OIzKCHm8WgDdyQevxbAAV1OgcXctuGw==",
+            "requires": {
+                "tslib": "^2.4.0"
+            }
+        },
         "@fortawesome/fontawesome-common-types": {
             "version": "6.4.0",
             "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz",
@@ -3036,30 +3152,44 @@
             "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
             "dev": true
         },
-        "@hypnosphi/create-react-context": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
-            "integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==",
-            "requires": {
-                "gud": "^1.0.0",
-                "warning": "^4.0.3"
-            },
-            "dependencies": {
-                "warning": {
-                    "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
-                    "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
-                    "requires": {
-                        "loose-envify": "^1.0.0"
-                    }
-                }
-            }
-        },
         "@icons/material": {
             "version": "0.2.4",
             "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
             "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
         },
+        "@internationalized/date": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.5.0.tgz",
+            "integrity": "sha512-nw0Q+oRkizBWMioseI8+2TeUPEyopJVz5YxoYVzR0W1v+2YytiYah7s/ot35F149q/xAg4F1gT/6eTd+tsUpFQ==",
+            "requires": {
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@internationalized/message": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.1.tgz",
+            "integrity": "sha512-ZgHxf5HAPIaR0th+w0RUD62yF6vxitjlprSxmLJ1tam7FOekqRSDELMg4Cr/DdszG5YLsp5BG3FgHgqquQZbqw==",
+            "requires": {
+                "@swc/helpers": "^0.5.0",
+                "intl-messageformat": "^10.1.0"
+            }
+        },
+        "@internationalized/number": {
+            "version": "3.4.0",
+            "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.4.0.tgz",
+            "integrity": "sha512-8TvotW3qVDHC4uv/BVoN6Qx0Dm8clHY1/vpH+dh+XRiPW/9NVpKn1P8d1A+WLphWrMwyqyWXI7uWehJPviaeIw==",
+            "requires": {
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@internationalized/string": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.1.1.tgz",
+            "integrity": "sha512-fvSr6YRoVPgONiVIUhgCmIAlifMVCeej/snPZVzbzRPxGpHl3o1GRe+d/qh92D8KhgOciruDUH8I5mjdfdjzfA==",
+            "requires": {
+                "@swc/helpers": "^0.5.0"
+            }
+        },
         "@isaacs/cliui": {
             "version": "8.0.2",
             "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -3166,6 +3296,11 @@
                 "@jridgewell/sourcemap-codec": "1.4.14"
             }
         },
+        "@juggle/resize-observer": {
+            "version": "3.4.0",
+            "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
+            "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
+        },
         "@log4js-node/log4js-api": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/@log4js-node/log4js-api/-/log4js-api-1.0.2.tgz",
@@ -3992,234 +4127,6 @@
                 }
             }
         },
-        "@mui/x-date-pickers": {
-            "version": "6.18.5",
-            "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.18.5.tgz",
-            "integrity": "sha512-3jImYIWP2Xgi608yzm/Sz1v0MTjQQYdZSQOEIi3dWBfSAU9B06KXDpqlXfRSpTV+rtsnfYIIyiWlz6Ltk7sUWw==",
-            "requires": {
-                "@babel/runtime": "^7.23.2",
-                "@mui/base": "^5.0.0-beta.22",
-                "@mui/utils": "^5.14.16",
-                "@types/react-transition-group": "^4.4.8",
-                "clsx": "^2.0.0",
-                "prop-types": "^15.8.1",
-                "react-transition-group": "^4.4.5"
-            },
-            "dependencies": {
-                "@babel/runtime": {
-                    "version": "7.23.6",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz",
-                    "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
-                    "requires": {
-                        "regenerator-runtime": "^0.14.0"
-                    }
-                },
-                "@floating-ui/react-dom": {
-                    "version": "2.0.4",
-                    "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz",
-                    "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==",
-                    "requires": {
-                        "@floating-ui/dom": "^1.5.1"
-                    }
-                },
-                "@mui/base": {
-                    "version": "5.0.0-beta.27",
-                    "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.27.tgz",
-                    "integrity": "sha512-duL37qxihT1N0pW/gyXVezP7SttLkF+cLAs/y6g6ubEFmVadjbnZ45SeF12/vAiKzqwf5M0uFH1cczIPXFZygA==",
-                    "requires": {
-                        "@babel/runtime": "^7.23.5",
-                        "@floating-ui/react-dom": "^2.0.4",
-                        "@mui/types": "^7.2.11",
-                        "@mui/utils": "^5.15.0",
-                        "@popperjs/core": "^2.11.8",
-                        "clsx": "^2.0.0",
-                        "prop-types": "^15.8.1"
-                    }
-                },
-                "@mui/types": {
-                    "version": "7.2.11",
-                    "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.11.tgz",
-                    "integrity": "sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w=="
-                },
-                "@mui/utils": {
-                    "version": "5.15.0",
-                    "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.0.tgz",
-                    "integrity": "sha512-XSmTKStpKYamewxyJ256+srwEnsT3/6eNo6G7+WC1tj2Iq9GfUJ/6yUoB7YXjOD2jTZ3XobToZm4pVz1LBt6GA==",
-                    "requires": {
-                        "@babel/runtime": "^7.23.5",
-                        "@types/prop-types": "^15.7.11",
-                        "prop-types": "^15.8.1",
-                        "react-is": "^18.2.0"
-                    }
-                },
-                "@popperjs/core": {
-                    "version": "2.11.8",
-                    "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
-                    "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
-                },
-                "@types/prop-types": {
-                    "version": "15.7.11",
-                    "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
-                    "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
-                },
-                "@types/react-transition-group": {
-                    "version": "4.4.10",
-                    "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
-                    "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
-                    "requires": {
-                        "@types/react": "*"
-                    }
-                },
-                "clsx": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
-                    "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
-                },
-                "react-is": {
-                    "version": "18.2.0",
-                    "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
-                    "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
-                },
-                "regenerator-runtime": {
-                    "version": "0.14.1",
-                    "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
-                    "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
-                }
-            }
-        },
-        "@mui/x-date-pickers-pro": {
-            "version": "6.18.5",
-            "resolved": "https://registry.npmjs.org/@mui/x-date-pickers-pro/-/x-date-pickers-pro-6.18.5.tgz",
-            "integrity": "sha512-f1JKEpi0cVmsOTab3rnx1W4aEPuKnF9IU3Ib7an75rW321QdwFYTm9+V4ZwWnN0jwDq8+13jjhdDEzfqcX76RA==",
-            "requires": {
-                "@babel/runtime": "^7.23.2",
-                "@mui/base": "^5.0.0-beta.22",
-                "@mui/utils": "^5.14.16",
-                "@mui/x-date-pickers": "6.18.5",
-                "@mui/x-license-pro": "6.10.2",
-                "clsx": "^2.0.0",
-                "prop-types": "^15.8.1",
-                "react-transition-group": "^4.4.5"
-            },
-            "dependencies": {
-                "@babel/runtime": {
-                    "version": "7.23.6",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz",
-                    "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
-                    "requires": {
-                        "regenerator-runtime": "^0.14.0"
-                    }
-                },
-                "@floating-ui/react-dom": {
-                    "version": "2.0.4",
-                    "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz",
-                    "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==",
-                    "requires": {
-                        "@floating-ui/dom": "^1.5.1"
-                    }
-                },
-                "@mui/base": {
-                    "version": "5.0.0-beta.27",
-                    "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.27.tgz",
-                    "integrity": "sha512-duL37qxihT1N0pW/gyXVezP7SttLkF+cLAs/y6g6ubEFmVadjbnZ45SeF12/vAiKzqwf5M0uFH1cczIPXFZygA==",
-                    "requires": {
-                        "@babel/runtime": "^7.23.5",
-                        "@floating-ui/react-dom": "^2.0.4",
-                        "@mui/types": "^7.2.11",
-                        "@mui/utils": "^5.15.0",
-                        "@popperjs/core": "^2.11.8",
-                        "clsx": "^2.0.0",
-                        "prop-types": "^15.8.1"
-                    }
-                },
-                "@mui/types": {
-                    "version": "7.2.11",
-                    "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.11.tgz",
-                    "integrity": "sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w=="
-                },
-                "@mui/utils": {
-                    "version": "5.15.0",
-                    "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.0.tgz",
-                    "integrity": "sha512-XSmTKStpKYamewxyJ256+srwEnsT3/6eNo6G7+WC1tj2Iq9GfUJ/6yUoB7YXjOD2jTZ3XobToZm4pVz1LBt6GA==",
-                    "requires": {
-                        "@babel/runtime": "^7.23.5",
-                        "@types/prop-types": "^15.7.11",
-                        "prop-types": "^15.8.1",
-                        "react-is": "^18.2.0"
-                    }
-                },
-                "@popperjs/core": {
-                    "version": "2.11.8",
-                    "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
-                    "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
-                },
-                "@types/prop-types": {
-                    "version": "15.7.11",
-                    "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
-                    "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
-                },
-                "clsx": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
-                    "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
-                },
-                "react-is": {
-                    "version": "18.2.0",
-                    "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
-                    "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
-                },
-                "regenerator-runtime": {
-                    "version": "0.14.1",
-                    "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
-                    "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
-                }
-            }
-        },
-        "@mui/x-license-pro": {
-            "version": "6.10.2",
-            "resolved": "https://registry.npmjs.org/@mui/x-license-pro/-/x-license-pro-6.10.2.tgz",
-            "integrity": "sha512-Baw3shilU+eHgU+QYKNPFUKvfS5rSyNJ98pQx02E0gKA22hWp/XAt88K1qUfUMPlkPpvg/uci6gviQSSLZkuKw==",
-            "requires": {
-                "@babel/runtime": "^7.22.6",
-                "@mui/utils": "^5.13.7"
-            },
-            "dependencies": {
-                "@babel/runtime": {
-                    "version": "7.23.6",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz",
-                    "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
-                    "requires": {
-                        "regenerator-runtime": "^0.14.0"
-                    }
-                },
-                "@mui/utils": {
-                    "version": "5.15.0",
-                    "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.0.tgz",
-                    "integrity": "sha512-XSmTKStpKYamewxyJ256+srwEnsT3/6eNo6G7+WC1tj2Iq9GfUJ/6yUoB7YXjOD2jTZ3XobToZm4pVz1LBt6GA==",
-                    "requires": {
-                        "@babel/runtime": "^7.23.5",
-                        "@types/prop-types": "^15.7.11",
-                        "prop-types": "^15.8.1",
-                        "react-is": "^18.2.0"
-                    }
-                },
-                "@types/prop-types": {
-                    "version": "15.7.11",
-                    "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
-                    "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
-                },
-                "react-is": {
-                    "version": "18.2.0",
-                    "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
-                    "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
-                },
-                "regenerator-runtime": {
-                    "version": "0.14.1",
-                    "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
-                    "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
-                }
-            }
-        },
         "@nicolo-ribaudo/chokidar-2": {
             "version": "2.1.8-no-fsevents.3",
             "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
@@ -5320,138 +5227,2299 @@
                 }
             }
         },
-        "@octokit/auth-token": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz",
-            "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==",
+        "@octokit/auth-token": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz",
+            "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==",
+            "requires": {
+                "@octokit/types": "^9.0.0"
+            }
+        },
+        "@octokit/core": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz",
+            "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==",
+            "requires": {
+                "@octokit/auth-token": "^3.0.0",
+                "@octokit/graphql": "^5.0.0",
+                "@octokit/request": "^6.0.0",
+                "@octokit/request-error": "^3.0.0",
+                "@octokit/types": "^9.0.0",
+                "before-after-hook": "^2.2.0",
+                "universal-user-agent": "^6.0.0"
+            }
+        },
+        "@octokit/endpoint": {
+            "version": "7.0.5",
+            "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.5.tgz",
+            "integrity": "sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==",
+            "requires": {
+                "@octokit/types": "^9.0.0",
+                "is-plain-object": "^5.0.0",
+                "universal-user-agent": "^6.0.0"
+            }
+        },
+        "@octokit/graphql": {
+            "version": "5.0.5",
+            "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz",
+            "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==",
+            "requires": {
+                "@octokit/request": "^6.0.0",
+                "@octokit/types": "^9.0.0",
+                "universal-user-agent": "^6.0.0"
+            }
+        },
+        "@octokit/openapi-types": {
+            "version": "16.0.0",
+            "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
+            "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
+        },
+        "@octokit/request": {
+            "version": "6.2.3",
+            "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.3.tgz",
+            "integrity": "sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==",
+            "requires": {
+                "@octokit/endpoint": "^7.0.0",
+                "@octokit/request-error": "^3.0.0",
+                "@octokit/types": "^9.0.0",
+                "is-plain-object": "^5.0.0",
+                "node-fetch": "^2.6.7",
+                "universal-user-agent": "^6.0.0"
+            }
+        },
+        "@octokit/request-error": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz",
+            "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
+            "requires": {
+                "@octokit/types": "^9.0.0",
+                "deprecation": "^2.0.0",
+                "once": "^1.4.0"
+            }
+        },
+        "@octokit/types": {
+            "version": "9.0.0",
+            "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
+            "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
+            "requires": {
+                "@octokit/openapi-types": "^16.0.0"
+            }
+        },
+        "@opentelemetry/api": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz",
+            "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA=="
+        },
+        "@pkgjs/parseargs": {
+            "version": "0.11.0",
+            "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+            "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+            "optional": true
+        },
+        "@popperjs/core": {
+            "version": "2.11.7",
+            "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
+            "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw=="
+        },
+        "@react-aria/actiongroup": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/actiongroup/-/actiongroup-3.7.0.tgz",
+            "integrity": "sha512-PAOoZbmvcEDy7qpYSXK+lxs7Jl0/7LpRQ+wze2muhsn8bHdPKl67lbUx8qY2L7fcamXNl4sCx/5w/MiS33HbDA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/list": "^3.10.1",
+                "@react-types/actiongroup": "^3.4.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/breadcrumbs": {
+            "version": "3.5.8",
+            "resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.8.tgz",
+            "integrity": "sha512-jeek23igeqXct7S3ShW2jtFUc5g3fS9ZEBZkF64FWBrwfCiaZwb8TcKkK/xFw36/q5mxEt+seNiqnNzvsICJuQ==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/link": "^3.6.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/breadcrumbs": "^3.7.2",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/button": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/button/-/button-3.9.0.tgz",
+            "integrity": "sha512-Jri4OCN+4YmpJDPNQvk1DJoskKD9sdTxZaWWWJdAwoSIunZk3IEBXVvRfKzsEAVtI+UJN25zC2kyjXbVPS2XAA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/toggle": "^3.7.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/calendar": {
+            "version": "3.5.3",
+            "resolved": "https://registry.npmjs.org/@react-aria/calendar/-/calendar-3.5.3.tgz",
+            "integrity": "sha512-jW48jk0TIe0HAJS+z8zqd8M86FEuqrk1qEIjMWnf8rFnA7hPPpjdjUrY9vSIeC95NcbyZbFnr1bHzQjAIzosQw==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/live-announcer": "^3.3.1",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/calendar": "^3.4.2",
+                "@react-types/button": "^3.9.1",
+                "@react-types/calendar": "^3.4.2",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/checkbox": {
+            "version": "3.12.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/checkbox/-/checkbox-3.12.0.tgz",
+            "integrity": "sha512-CyFZoI+z9hhyB3wb7IBsZxE30vXfYO2vSyET16zlkJ4qiFMqMiVLE4ekq034MHltCdpAczgP5yfKgNnJOmj7vQ==",
+            "requires": {
+                "@react-aria/form": "^3.0.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/toggle": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/checkbox": "^3.6.0",
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/toggle": "^3.7.0",
+                "@react-types/checkbox": "^3.6.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/combobox": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/combobox/-/combobox-3.8.0.tgz",
+            "integrity": "sha512-lInzzZrH4vFlxmvDpXgQRkkREm7YIx258IRpQqll8Bny2vKMmZoF06zWMbcHP0CjFqYxExQeTjSYx0OTRRxkCQ==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/listbox": "^3.11.2",
+                "@react-aria/live-announcer": "^3.3.1",
+                "@react-aria/menu": "^3.11.2",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/textfield": "^3.13.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/combobox": "^3.8.0",
+                "@react-stately/form": "^3.0.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/combobox": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/datepicker": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/datepicker/-/datepicker-3.9.0.tgz",
+            "integrity": "sha512-FIpiJxwBNOM8a6hLOqQJ4JrvRiGL6Zr44E1mHtAWStp2kBEJ6+O2JRm4PQ5Pzvdw6xnCpOBdfESdNdlXN7lVqQ==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@internationalized/number": "^3.4.0",
+                "@internationalized/string": "^3.1.1",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/form": "^3.0.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/spinbutton": "^3.6.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/datepicker": "^3.9.0",
+                "@react-stately/form": "^3.0.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/calendar": "^3.4.2",
+                "@react-types/datepicker": "^3.7.0",
+                "@react-types/dialog": "^3.5.7",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/dialog": {
+            "version": "3.5.8",
+            "resolved": "https://registry.npmjs.org/@react-aria/dialog/-/dialog-3.5.8.tgz",
+            "integrity": "sha512-KIc1FORdHhZ3bWom4qHO0hmlL4e5Hup6N25EY8HP5I7Ftv9EBBGaO5grtxZ2fX8kiCJNI4y+k67ZZ71wKJvMiA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/dialog": "^3.5.7",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/dnd": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/dnd/-/dnd-3.5.0.tgz",
+            "integrity": "sha512-6IuqmXwnfgRfeXDbfsPZzScapCmtRIkphTBPoLT575uEbZC7ROLgRJ/4NIKxvtTA6IIBqUGcvaqU9Mpg8j4U5Q==",
+            "requires": {
+                "@internationalized/string": "^3.1.1",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/live-announcer": "^3.3.1",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/dnd": "^3.2.6",
+                "@react-types/button": "^3.9.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/focus": {
+            "version": "3.15.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.15.0.tgz",
+            "integrity": "sha512-nnxRyfqHuAjRwdQ4BpQyZPtGFKZmRU6cnaIb3pqWFCqEyJQensV7MA3TJ4Jhadq67cy1Ji5SYSlr1duBwjoYvw==",
+            "requires": {
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0",
+                "clsx": "^1.1.1"
+            }
+        },
+        "@react-aria/form": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/form/-/form-3.0.0.tgz",
+            "integrity": "sha512-APeGph9oTO8nro4ZObuy1hk+0hpF/ji9O3odPGhLkzP/HvW2J7NI9pjKJOINfgtYr2yvVUZf/MbTMxPwtAxhaQ==",
+            "requires": {
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/form": "^3.0.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/grid": {
+            "version": "3.8.5",
+            "resolved": "https://registry.npmjs.org/@react-aria/grid/-/grid-3.8.5.tgz",
+            "integrity": "sha512-0p+Bbs9rpQeOy8b75DamlzVPKylBoe/z0XwkeeTChHP2TK3TwPXh6J5EmisQx6K8zsb3iZULQRcP4QibvnMbrg==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/live-announcer": "^3.3.1",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/grid": "^3.8.3",
+                "@react-stately/selection": "^3.14.1",
+                "@react-stately/virtualizer": "^3.6.5",
+                "@react-types/checkbox": "^3.6.0",
+                "@react-types/grid": "^3.2.3",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/gridlist": {
+            "version": "3.7.2",
+            "resolved": "https://registry.npmjs.org/@react-aria/gridlist/-/gridlist-3.7.2.tgz",
+            "integrity": "sha512-9keGYZz0yILVqAnFzF6hGRtHm1vfSD1mNnH8oyn7mKjyr7qOln7s5f8Nl85ueMolfrV3H2rCZgM2itNQ+Ezzgg==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/grid": "^3.8.5",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/list": "^3.10.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/i18n": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.9.0.tgz",
+            "integrity": "sha512-ebGP/sVG0ZtNF4RNFzs/W01tl7waYpBManh1kKWgA4roDPFt/odkgkDBzKGl+ggBb7TQRHsfUFHuqKsrsMy9TA==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@internationalized/message": "^3.1.1",
+                "@internationalized/number": "^3.4.0",
+                "@internationalized/string": "^3.1.1",
+                "@react-aria/ssr": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/interactions": {
+            "version": "3.20.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.20.0.tgz",
+            "integrity": "sha512-JCCEyK2Nb4mEHucrgmqhTHTNAEqhsiM07jJmmY22eikxnCQnsEfdwXyg9cgZLG79D5V7jyqVRqOp2OsG7Qx7kQ==",
+            "requires": {
+                "@react-aria/ssr": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/label": {
+            "version": "3.7.3",
+            "resolved": "https://registry.npmjs.org/@react-aria/label/-/label-3.7.3.tgz",
+            "integrity": "sha512-v1zuqbpYyYaPjrBWpceGjMpwP4ne6fLoOXdoIZoKLux2jkAcyIF2kIJFiyYoPQYQJWGRNo7q1oSwamxmng4xJw==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/link": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/@react-aria/link/-/link-3.6.2.tgz",
+            "integrity": "sha512-v9gXgQ3Gev0JOlg2MAXcubDMgX+0BlJ+hTyFYFMuN/4jVBlAe426WKbjg+6MMzxwukWg9C3Q08JzqdFTi4cBng==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/link": "^3.5.2",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/listbox": {
+            "version": "3.11.2",
+            "resolved": "https://registry.npmjs.org/@react-aria/listbox/-/listbox-3.11.2.tgz",
+            "integrity": "sha512-FXdoqYLUTJn16OxodyS518PIcwzFkCfW5bxQepoy88NDMGtqp6u8fvEPpAoZbomvw/pV9MuEaMAw9qLyfkD4DA==",
+            "requires": {
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/list": "^3.10.1",
+                "@react-types/listbox": "^3.4.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/live-announcer": {
+            "version": "3.3.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.3.1.tgz",
+            "integrity": "sha512-hsc77U7S16trM86d+peqJCOCQ7/smO1cybgdpOuzXyiwcHQw8RQ4GrXrS37P4Ux/44E9nMZkOwATQRT2aK8+Ew==",
+            "requires": {
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/menu": {
+            "version": "3.11.2",
+            "resolved": "https://registry.npmjs.org/@react-aria/menu/-/menu-3.11.2.tgz",
+            "integrity": "sha512-I4R5FOvRtwIQW+0naXav5giZBp935X2tXB2xBg/cSAYDXgfLmFPLHkyPbO77hR6FwazfFfJoKdn0pVcRox3lrQ==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/menu": "^3.5.7",
+                "@react-stately/tree": "^3.7.4",
+                "@react-types/button": "^3.9.1",
+                "@react-types/menu": "^3.9.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/meter": {
+            "version": "3.4.8",
+            "resolved": "https://registry.npmjs.org/@react-aria/meter/-/meter-3.4.8.tgz",
+            "integrity": "sha512-u/pNisFs8UottonYlwqaS2i/NhHIw9LcApHo55XP7XMFCnaHPlq3mJzpSsr0zuCTvat2djoKelj41jT6Fhuw+A==",
+            "requires": {
+                "@react-aria/progress": "^3.4.8",
+                "@react-types/meter": "^3.3.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/numberfield": {
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/numberfield/-/numberfield-3.10.0.tgz",
+            "integrity": "sha512-ixkvkPTn18RNPnbaT726CHA+Wpr/qTYWboq8hSaJK0LiAtiEWCKg0pmVtJ4lFntAQ5GNp02xudTwhQdLN5WRig==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/spinbutton": "^3.6.0",
+                "@react-aria/textfield": "^3.13.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/numberfield": "^3.7.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/numberfield": "^3.7.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/overlays": {
+            "version": "3.19.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.19.0.tgz",
+            "integrity": "sha512-VN5GkB8+uZ2cfXljBtkqmrsAhBdGoj4un/agH0Qyihi2dazsMeafczSNnqzbpVgB4Zt2UHPJUkKwihgzXRxJJA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/ssr": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/visually-hidden": "^3.8.7",
+                "@react-stately/overlays": "^3.6.4",
+                "@react-types/button": "^3.9.1",
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/progress": {
+            "version": "3.4.8",
+            "resolved": "https://registry.npmjs.org/@react-aria/progress/-/progress-3.4.8.tgz",
+            "integrity": "sha512-Nah3aj5BNRa0+urQZimzb0vuKQK7lsc8BrUwJuHTwGRBSWUjCADExrJYdhDIR/nLUV2TCmAQl+GJtTgbEEj0DQ==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/progress": "^3.5.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/radio": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/radio/-/radio-3.9.0.tgz",
+            "integrity": "sha512-kr3+OQ1YU/3mURZfCsYaQmJ/c15qOm8uScaDRC39qz97bLNASakQqMImIaS+GluPKx1PEW3y2ErAgLplH28zZw==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/form": "^3.0.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/radio": "^3.10.0",
+                "@react-types/radio": "^3.6.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/searchfield": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/searchfield/-/searchfield-3.6.0.tgz",
+            "integrity": "sha512-mHaN+sx2SLqluvF0/YIBQ9WA5LakSWl79FgC0sOWEaOZhDswAbJ9tESdi/M/ahtOnVwblE0cpHRlUKV0Oz4gOw==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/textfield": "^3.13.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/searchfield": "^3.5.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/searchfield": "^3.5.2",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/select": {
+            "version": "3.14.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/select/-/select-3.14.0.tgz",
+            "integrity": "sha512-ulVFH8K1yr8CxQE7pzhlM3aWBltWfSbWdJV3FXDqM0kA+GHqqPwZVJcqPuegtaiju1z6nRk4q789kJa4o+4M9g==",
+            "requires": {
+                "@react-aria/form": "^3.0.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/listbox": "^3.11.2",
+                "@react-aria/menu": "^3.11.2",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/visually-hidden": "^3.8.7",
+                "@react-stately/select": "^3.6.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/select": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/selection": {
+            "version": "3.17.2",
+            "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.17.2.tgz",
+            "integrity": "sha512-AXXY3eOIWnITabMn6c0bpLPXkSX7040LOZU+7pQgtZJwDdZorLuKw4i7WS5i71LcV71ywG4mtqc9mOb/GfhUbg==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/selection": "^3.14.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/separator": {
+            "version": "3.3.8",
+            "resolved": "https://registry.npmjs.org/@react-aria/separator/-/separator-3.3.8.tgz",
+            "integrity": "sha512-u15HgH2IVKN/mx7Hp9dfNiFpPU/mq2EA7l0e2fsVSjA77nhSctUFBAqaR7FAI/y86RUhq3zplIz4BJek1/3Dvw==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/slider": {
+            "version": "3.7.3",
+            "resolved": "https://registry.npmjs.org/@react-aria/slider/-/slider-3.7.3.tgz",
+            "integrity": "sha512-AbrTD9UzMn0CwxFjOhJHz2ms2zdJlBL3XnbvqkpsmpXUl0u8WT1QAEaMnS5+792gnSGZs/ARDmse53o+IO8wTA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/slider": "^3.4.5",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/slider": "^3.7.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/spinbutton": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.6.0.tgz",
+            "integrity": "sha512-I7f1gfwVRcjguEXZijk0z5g8njZ2YWnQzVzcwGf8ocLPxfw1CnSivNCzwVj2ChXPX10uXewXVMLWVCz+BRC9uQ==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/live-announcer": "^3.3.1",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/ssr": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.0.tgz",
+            "integrity": "sha512-Bz6BqP6ZorCme9tSWHZVmmY+s7AU8l6Vl2NUYmBzezD//fVHHfFo4lFBn5tBuAaJEm3AuCLaJQ6H2qhxNSb7zg==",
+            "requires": {
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/switch": {
+            "version": "3.5.7",
+            "resolved": "https://registry.npmjs.org/@react-aria/switch/-/switch-3.5.7.tgz",
+            "integrity": "sha512-zBEsB071zzhQ82RwAA42pFLXHgrpya0OoRAsTO6jHZwiaYMsyqJI2eiXd7F6rqklpgyO6k7jOQklGUuoSJW4pA==",
+            "requires": {
+                "@react-aria/toggle": "^3.9.0",
+                "@react-stately/toggle": "^3.7.0",
+                "@react-types/switch": "^3.5.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/table": {
+            "version": "3.13.2",
+            "resolved": "https://registry.npmjs.org/@react-aria/table/-/table-3.13.2.tgz",
+            "integrity": "sha512-bJgMx2SZ8SFmTosbv6k1lZ1a0Yw3f8tzWhpIQodCaMHhtI7izA6YqDNx47NeBNYpVm9DFfAoWbb79HFJ+OKIJA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/grid": "^3.8.5",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/live-announcer": "^3.3.1",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/visually-hidden": "^3.8.7",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/flags": "^3.0.0",
+                "@react-stately/table": "^3.11.3",
+                "@react-stately/virtualizer": "^3.6.5",
+                "@react-types/checkbox": "^3.6.0",
+                "@react-types/grid": "^3.2.3",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/table": "^3.9.1",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/tabs": {
+            "version": "3.8.2",
+            "resolved": "https://registry.npmjs.org/@react-aria/tabs/-/tabs-3.8.2.tgz",
+            "integrity": "sha512-zDfeEEyJmcnH9TFvJECWIrJpxX4SmREFV1/P8hN6ZUJPYoeiGMXYYFvjcRb1r3LN8XKlbwR37AQ3Cn1/yhrUwQ==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/tabs": "^3.6.2",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/tabs": "^3.3.4",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/tag": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/tag/-/tag-3.3.0.tgz",
+            "integrity": "sha512-mANJTcPyut98O4D3cAKaNEV6QFfoljZCDAgC+uJkV/Zn8cU4JOFeNLAyNoLRlPvYw+msqr6wUyPkWNERuO+1Uw==",
+            "requires": {
+                "@react-aria/gridlist": "^3.7.2",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/list": "^3.10.1",
+                "@react-types/button": "^3.9.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/textfield": {
+            "version": "3.13.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/textfield/-/textfield-3.13.0.tgz",
+            "integrity": "sha512-sUlinDE+k/WhbskyqVOkuffuhiQpjgvp+iGRoralStVgb8Tcb+POxgAlw5jS4tNjdivCb3IjVJemUNJM7xsxxA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/form": "^3.0.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/textfield": "^3.9.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/toggle": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.9.0.tgz",
+            "integrity": "sha512-2YMWYQUEmcoAXtrAE86QXBS9XlmJyV6IFRlMTBNaeLTdH3AmACExgsyU66Tt0sKl6LMDMI376ItMFqAz27BBdQ==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/toggle": "^3.7.0",
+                "@react-types/checkbox": "^3.6.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/tooltip": {
+            "version": "3.6.5",
+            "resolved": "https://registry.npmjs.org/@react-aria/tooltip/-/tooltip-3.6.5.tgz",
+            "integrity": "sha512-hXw4Z8nYLOWz3QOQ807wWZdvDwR3gofsmZhAehg2HPRwdRfCQK+1cjVKeUd9cKCAxs0Cay7dV0oUdilLbCQ2Gg==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/tooltip": "^3.4.6",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/tooltip": "^3.4.6",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/utils": {
+            "version": "3.22.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.22.0.tgz",
+            "integrity": "sha512-Qi/m65GFFljXA/ayj1m5g3KZdgbZY3jacSSqD5vNUOEGiKsn4OQcsw8RfC2c0SgtLV1hLzsfvFI1OiryPlGCcw==",
+            "requires": {
+                "@react-aria/ssr": "^3.9.0",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0",
+                "clsx": "^1.1.1"
+            }
+        },
+        "@react-aria/virtualizer": {
+            "version": "3.9.7",
+            "resolved": "https://registry.npmjs.org/@react-aria/virtualizer/-/virtualizer-3.9.7.tgz",
+            "integrity": "sha512-xaLnzRypiJuGi91ns+LXLGfEhi4a2oz2ZUrZmIrMnBF3asEqULX1PEZoeSWrTWENzumM9D49fvB4JqonK0Ktwg==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-stately/virtualizer": "^3.6.5",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-aria/visually-hidden": {
+            "version": "3.8.7",
+            "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.7.tgz",
+            "integrity": "sha512-OuIGMVQIt7GC43h4x35BgkZid8lhoPu7Xz4TQRP8nvOJWb1lH7ehrRRuGdUsK3y90nwpxTdNdg4DILblg+VaLw==",
+            "requires": {
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-google-maps/api": {
+            "version": "2.18.1",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.18.1.tgz",
+            "integrity": "sha512-KVlUO/Shh+0g/3egWaKmY0sz6+0QOnYkBGvrBMJbz23519LauA+iJFc4NDCmWNHqD5Vhb/Bkg0kSJgq0Stz3Iw==",
+            "requires": {
+                "@googlemaps/js-api-loader": "1.15.1",
+                "@googlemaps/markerclusterer": "2.0.15",
+                "@react-google-maps/infobox": "2.16.0",
+                "@react-google-maps/marker-clusterer": "2.16.1",
+                "@types/google.maps": "3.50.5",
+                "invariant": "2.2.4"
+            }
+        },
+        "@react-google-maps/infobox": {
+            "version": "2.16.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.16.0.tgz",
+            "integrity": "sha512-ZojiMS25388RcUHQPycUAerSqdHDom+3dHczVcXHdT/i8fka3O8InkHxXwMhvBoM143ips7mv2BPaYOAJ5f4Nw=="
+        },
+        "@react-google-maps/marker-clusterer": {
+            "version": "2.16.1",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.16.1.tgz",
+            "integrity": "sha512-jOuyqzWLeXvQcoAu6TCVWHAuko+sDt0JjawNHBGqUNLywMtTCvYP0L0PiqJZOUCUeRYGdUy0AKxQ+30vAkvwag=="
+        },
+        "@react-spectrum/actionbar": {
+            "version": "3.4.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/actionbar/-/actionbar-3.4.0.tgz",
+            "integrity": "sha512-Kb0lSo5iFpb4Qeet0fN8Ful7TFtmtHkl1LJuKz53q2qYQll+E+r2VVAdcOQMJRMRAEnecfd5mxAjEVawdOqrdw==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/live-announcer": "^3.3.1",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/actiongroup": "^3.10.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/overlays": "^5.5.2",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-types/actionbar": "^3.1.4",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/actiongroup": {
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/actiongroup/-/actiongroup-3.10.0.tgz",
+            "integrity": "sha512-Wz6nD43bSFiUJsGJr8nIlFjiDzh7ZM65a7xnkHHk+wEPU6wDR6pA0fLVsscMMgNDqDaW2RclfIpJ2jBTBqZ7/w==",
+            "requires": {
+                "@react-aria/actiongroup": "^3.7.0",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/menu": "^3.16.0",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/tooltip": "^3.6.2",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/list": "^3.10.1",
+                "@react-types/actiongroup": "^3.4.6",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@spectrum-icons/workflow": "^4.2.7",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/avatar": {
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/avatar/-/avatar-3.0.7.tgz",
+            "integrity": "sha512-+WfgiweFfu8rIhTIEKC/4i0X97lqTWxvosjQMmAgDV2m3Z0Qf03stJzZFL9UISEF+EchE6G17bAYfZ5UjmmErQ==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/avatar": "^3.0.4",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/badge": {
+            "version": "3.1.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/badge/-/badge-3.1.8.tgz",
+            "integrity": "sha512-yplVbI2r/JJKVrsoc7cQpGv7JCPOApH6NYvjPo2UI7Vi5qI86BMP51BtHvZaxsmxKIinGBaoX44jcBcJw40Pmg==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/badge": "^3.1.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/breadcrumbs": {
+            "version": "3.9.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/breadcrumbs/-/breadcrumbs-3.9.2.tgz",
+            "integrity": "sha512-JMDeUpvzehx82mNizcc/Hnd1KYJCAnZARn9LJZpDhTqCMZW7OVkaUf8l5EtYPQpy6IAt477rQX4Gh/ipl+niLw==",
+            "requires": {
+                "@react-aria/breadcrumbs": "^3.5.8",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/menu": "^3.16.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-types/breadcrumbs": "^3.7.2",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/button": {
+            "version": "3.15.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/button/-/button-3.15.0.tgz",
+            "integrity": "sha512-Hr4A2ex9uxg93XsnboCgiFYSD/lh8dK4u7+0I+yZ075kinrQVfbS4x5QZKqTW/BRxMOifHqfzz1CY2FMZ+9FSA==",
+            "requires": {
+                "@react-aria/button": "^3.9.0",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/progress": "^3.7.2",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/toggle": "^3.7.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/buttongroup": {
+            "version": "3.6.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/buttongroup/-/buttongroup-3.6.8.tgz",
+            "integrity": "sha512-KyyhNBok0bmAMVYIu7V5sNtw+sywfihF3j60yLwVCGdNl8tekLtZs8bwQY+HhkDMuyM5OPK9ClY5mMZAzDxWoQ==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/buttongroup": "^3.3.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/calendar": {
+            "version": "3.4.3",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/calendar/-/calendar-3.4.3.tgz",
+            "integrity": "sha512-UF5lR+zHbur1tjjE+B6dcht9+o7iu+jfCf8sXfU0HSs7DoJGiUazpOuTKG3WQhuTMcuYYewGU7zXdphEM8NGcw==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@react-aria/calendar": "^3.5.3",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/visually-hidden": "^3.8.7",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/calendar": "^3.4.2",
+                "@react-types/button": "^3.9.1",
+                "@react-types/calendar": "^3.4.2",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/checkbox": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/checkbox/-/checkbox-3.9.0.tgz",
+            "integrity": "sha512-UDNy1qLSgvBtpsX+Df2GL7hBsnqtNPXj13lX5YflvbxqQhqMK5H838toMtbxM1fgO/JW1lD6mXOLx8TgK5WSOw==",
+            "requires": {
+                "@react-aria/checkbox": "^3.12.0",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/checkbox": "^3.6.0",
+                "@react-stately/toggle": "^3.7.0",
+                "@react-types/checkbox": "^3.6.0",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/combobox": {
+            "version": "3.11.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/combobox/-/combobox-3.11.2.tgz",
+            "integrity": "sha512-HSgXm/dleNOG5BJ00Dju0pUlYYXgTMXJ0zgt4yVRyDKpH+vBwL60TYTP/JoVik9YrZkYd3NJ+IgaS5a5MEIXMg==",
+            "requires": {
+                "@react-aria/button": "^3.9.0",
+                "@react-aria/combobox": "^3.8.0",
+                "@react-aria/dialog": "^3.5.8",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/form": "^3.0.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/label": "^3.7.3",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/listbox": "^3.12.4",
+                "@react-spectrum/overlays": "^5.5.2",
+                "@react-spectrum/progress": "^3.7.2",
+                "@react-spectrum/textfield": "^3.11.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/combobox": "^3.8.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/combobox": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/contextualhelp": {
+            "version": "3.6.5",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/contextualhelp/-/contextualhelp-3.6.5.tgz",
+            "integrity": "sha512-gDxB68DFXPz3odOkyVFFWJZOx5l57JXUuZnrrUXSP4TnX/snXL+1+aIBzMcXqdJJhW8OB9sYmWAkimffLMuzGA==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/dialog": "^3.8.5",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/contextualhelp": "^3.2.7",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/workflow": "^4.2.7",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/datepicker": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/datepicker/-/datepicker-3.9.0.tgz",
+            "integrity": "sha512-Hqq3/IigL939mbFtUWsr95/rr/IN9r7a2Nvquha6vg9UALk8XnTj0f3fJl6m16GdSavpfOvxz7Ef9+Vqijw0mg==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@react-aria/datepicker": "^3.9.0",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/calendar": "^3.4.3",
+                "@react-spectrum/dialog": "^3.8.5",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-spectrum/view": "^3.6.5",
+                "@react-stately/datepicker": "^3.9.0",
+                "@react-types/datepicker": "^3.7.0",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@spectrum-icons/workflow": "^4.2.7",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/dialog": {
+            "version": "3.8.5",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/dialog/-/dialog-3.8.5.tgz",
+            "integrity": "sha512-lkAgRhzawhOX9yEjnA1tQeb5Yyv9W4CbRYv7ZtY6pr8f35b8JQQUGKCby3A/TArW5zTBvoueoMt27gJDxnGF5w==",
+            "requires": {
+                "@react-aria/dialog": "^3.5.8",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/buttongroup": "^3.6.8",
+                "@react-spectrum/divider": "^3.5.8",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/overlays": "^5.5.2",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-spectrum/view": "^3.6.5",
+                "@react-stately/overlays": "^3.6.4",
+                "@react-types/button": "^3.9.1",
+                "@react-types/dialog": "^3.5.7",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/divider": {
+            "version": "3.5.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/divider/-/divider-3.5.8.tgz",
+            "integrity": "sha512-1+uIZW/TO/laTgvUpSaBuKyScpVKDXQhyWd0WyP2UioRA7gIh9rEgoMAIV4WPFUXBLdj6W12uYGOjSS2iXfzYQ==",
+            "requires": {
+                "@react-aria/separator": "^3.3.8",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/divider": "^3.3.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/dnd": {
+            "version": "3.3.5",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/dnd/-/dnd-3.3.5.tgz",
+            "integrity": "sha512-+lPqHd/CSbz/PV2HXIOpKLuBRpee3oz0nO0CRIog7zfIkI/wl2xaov+PdrUKk+58GUNGhz1C69QlPnDCV+t+0w==",
+            "requires": {
+                "@react-aria/dnd": "^3.5.0",
+                "@react-stately/dnd": "^3.2.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/form": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/form/-/form-3.7.0.tgz",
+            "integrity": "sha512-TrUUG6D28Ugy/CmiGJJxkr8ue4zRE3wQkC6HScNCLcVGSkrcsWzDDUxRYDrxwlyFNMuZjUDhiLMMPthZqZcIUA==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/form": "^3.0.0",
+                "@react-types/form": "^3.6.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/icon": {
+            "version": "3.7.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/icon/-/icon-3.7.8.tgz",
+            "integrity": "sha512-OllOy3+2laBHZNXAsVfy7J7ov9UsT/e6zTeqvWVIhYW8abpIYcCtJWxeaBPubVMcA47NRCLF46sEOuFhnTUxIw==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/illustratedmessage": {
+            "version": "3.4.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/illustratedmessage/-/illustratedmessage-3.4.8.tgz",
+            "integrity": "sha512-QHrjAZ9rXv+WpUR0QG3bz/3j1qLkc7qS4AwImZ9aoF7YRWMxLJCB2/b9tPGmWRWjjeH+gSSa4Q4y8FEr2hsYkA==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/illustratedmessage": "^3.3.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/image": {
+            "version": "3.4.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/image/-/image-3.4.8.tgz",
+            "integrity": "sha512-hdDFjAxF9poKsE820H37C3y8J8NBzNQx8c/Erp2O1LTQP5HynBimYxqezx5KAl9scFsOaHsbY+VdGdFTzOmIsg==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/image": "^3.3.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/inlinealert": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/inlinealert/-/inlinealert-3.2.0.tgz",
+            "integrity": "sha512-uRWLLl2M5tc7B3/Lzm2/JLxXOyi+cT6uYkq9eksBM41OMfvgLksT5vaOegiBGHgi2r7QktTMZnDzGk5sGvaQyA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/label": {
+            "version": "3.16.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/label/-/label-3.16.0.tgz",
+            "integrity": "sha512-VLTpsq8wxTSv5eA2LwUVW79gdja3oSAKiTZmU6dMlo3yuBOhTMAsaAbgFhopTvotjUim9JG24BRpajnuA7Jz4w==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/label": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/labeledvalue": {
+            "version": "3.1.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/labeledvalue/-/labeledvalue-3.1.8.tgz",
+            "integrity": "sha512-J/LaCVR3pWYbTg7sZg1p0W7jdWongUTjEE+2mlSdtJJaxLTUgOFgm32k/RU7foFK9+ds41S6gfDpv88aR4M+YA==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/layout": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/layout/-/layout-3.6.0.tgz",
+            "integrity": "sha512-eOG7u7dqqawMJq/cgr0Iq96Tqt2tuRVStnLgVpWJqKSYxJadd8Ja3mAkWzUq5SfX3r1w3UJWSJPcqYcu4TQ5yQ==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/layout": "^3.3.12",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/link": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/link/-/link-3.6.2.tgz",
+            "integrity": "sha512-Pa3fgGUpxzlWZ6fXVQcdLXDxp6Ui9KtJDSKZYAxQrTL4OzEA8rgF+FeG0d7MDAHW2okW6a/N9dJALrJEtlizGg==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/link": "^3.6.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/link": "^3.5.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/list": {
+            "version": "3.7.4",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/list/-/list-3.7.4.tgz",
+            "integrity": "sha512-ccMeyylTWknzZoWHGGDzmaPHKU2hns0UjHBDyNT8hCItO/YL0s2NWY83QO+iYS8Xb6n/EDts7IBrJJYrmxDRWQ==",
+            "requires": {
+                "@react-aria/button": "^3.9.0",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/gridlist": "^3.7.2",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/virtualizer": "^3.9.7",
+                "@react-aria/visually-hidden": "^3.8.7",
+                "@react-spectrum/checkbox": "^3.9.0",
+                "@react-spectrum/dnd": "^3.3.5",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/progress": "^3.7.2",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/layout": "^3.13.4",
+                "@react-stately/list": "^3.10.1",
+                "@react-types/grid": "^3.2.3",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0",
+                "react-transition-group": "^4.4.5"
+            }
+        },
+        "@react-spectrum/listbox": {
+            "version": "3.12.4",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/listbox/-/listbox-3.12.4.tgz",
+            "integrity": "sha512-qmgHbeoFMFAK3AViPWri2z0Aqio0h2pf0ROjjkNPmTIm9xZEFMwArLqWhftmEm+smMTuNLjZv8aYGahKwiQDrg==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/listbox": "^3.11.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/virtualizer": "^3.9.7",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/progress": "^3.7.2",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/layout": "^3.13.4",
+                "@react-stately/list": "^3.10.1",
+                "@react-stately/virtualizer": "^3.6.5",
+                "@react-types/listbox": "^3.4.6",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/menu": {
+            "version": "3.16.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/menu/-/menu-3.16.0.tgz",
+            "integrity": "sha512-wcUwBLifWvJvMQrdnh7+m9g4maW87zv7yB2D/cnGYIkXL1aNZxa6zHLxSegs1PrqdP77fhEXthVFcMreuIHO6A==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/menu": "^3.11.2",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/separator": "^3.3.8",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/overlays": "^5.5.2",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/menu": "^3.5.7",
+                "@react-stately/overlays": "^3.6.4",
+                "@react-stately/tree": "^3.7.4",
+                "@react-types/menu": "^3.9.6",
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@spectrum-icons/workflow": "^4.2.7",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/meter": {
+            "version": "3.4.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/meter/-/meter-3.4.8.tgz",
+            "integrity": "sha512-7nFE+B9OE6WyKJpBn8gAulNAlKqG2VFq74UiB/xV37rHXRIjn4waLaxK7s6uHCozOu2Ly5uTDJV5x3AN3W2ldw==",
+            "requires": {
+                "@react-aria/meter": "^3.4.8",
+                "@react-spectrum/progress": "^3.7.2",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/meter": "^3.3.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/numberfield": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/numberfield/-/numberfield-3.8.0.tgz",
+            "integrity": "sha512-uboKuVBDYR5BkGw1qY3udKmsMRipkhQIY1diKK0uu5ETdKrjhXRNQ5L+xfshT/K960tmztwnKk/nj1jePjk6MA==",
+            "requires": {
+                "@react-aria/button": "^3.9.0",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/numberfield": "^3.10.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/textfield": "^3.11.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/numberfield": "^3.7.0",
+                "@react-types/button": "^3.9.1",
+                "@react-types/numberfield": "^3.7.0",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@spectrum-icons/workflow": "^4.2.7",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/overlays": {
+            "version": "5.5.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/overlays/-/overlays-5.5.2.tgz",
+            "integrity": "sha512-4VXK1HMazuETgSnpPSPszuCB/4WaVhPX0h+1ICllReuvv9mvgWt6KoCkqJSoVMM8qlB4XocGUPsI1pL+Bck78w==",
+            "requires": {
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/overlays": "^3.6.4",
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0",
+                "react-transition-group": "^4.4.5"
+            }
+        },
+        "@react-spectrum/picker": {
+            "version": "3.13.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/picker/-/picker-3.13.2.tgz",
+            "integrity": "sha512-o7t1tnCKrYbDpZ/n3vohok82Yx6iEv8TQXUrXkiqHbRiohN72PB4WJMOlBjMbNaprcC4tdkHVuGlAhSwQHYBdA==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/select": "^3.14.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/listbox": "^3.12.4",
+                "@react-spectrum/overlays": "^5.5.2",
+                "@react-spectrum/progress": "^3.7.2",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/select": "^3.6.0",
+                "@react-types/select": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/progress": {
+            "version": "3.7.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/progress/-/progress-3.7.2.tgz",
+            "integrity": "sha512-YtAvWHC4rwBqRagoS/Mmt2NOZo4G6p5rIdhiVp/UjKa1oqEEiut29dMvr/JS30AoNPPSc95ctydgUU6JynyGJQ==",
+            "requires": {
+                "@react-aria/progress": "^3.4.8",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/progress": "^3.5.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/provider": {
+            "version": "3.9.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/provider/-/provider-3.9.2.tgz",
+            "integrity": "sha512-5DZA8UdAXWzLJo7nxO7VgTqvmjN6cbjctMWhLbv81jSLWRqm+O6RhRZjnbEVXqwfyPpxJzDG8ZhjaQe/kFs38A==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/provider": "^3.7.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0",
+                "clsx": "^1.1.1"
+            }
+        },
+        "@react-spectrum/radio": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/radio/-/radio-3.7.0.tgz",
+            "integrity": "sha512-AikFzw+aJ1gL6Ysnfb4wXDgsnPp0KB9jbMrqENW6KKZxQ5+au2Xzj28mnKDfAjBXlxQlTAhBo5SnI0WnjTmKWA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/radio": "^3.9.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/radio": "^3.10.0",
+                "@react-types/radio": "^3.6.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/searchfield": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/searchfield/-/searchfield-3.8.0.tgz",
+            "integrity": "sha512-WVI/5ONt1hSm+D/A7J3ICpVOue6J2FBBWKU9zVSK7cwcklWgUGyGkZLD8luVQtcpI5bDLb2tHixs7a1OXPC8Uw==",
+            "requires": {
+                "@react-aria/searchfield": "^3.6.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/textfield": "^3.11.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/searchfield": "^3.5.0",
+                "@react-types/searchfield": "^3.5.2",
+                "@react-types/textfield": "^3.9.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/slider": {
+            "version": "3.6.4",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/slider/-/slider-3.6.4.tgz",
+            "integrity": "sha512-ckKlRJhDKUIsZepnCzRcCe20Q4MaoC8dLEFNz/f6CItfZNoE8sH5OsHuDFOEbhNOgNyGjj9Dv99iKPAkRrDWkg==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/slider": "^3.7.3",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/visually-hidden": "^3.8.7",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/slider": "^3.4.5",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/slider": "^3.7.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/statuslight": {
+            "version": "3.5.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/statuslight/-/statuslight-3.5.8.tgz",
+            "integrity": "sha512-q0q/JzEmucU83fCGokIQZ09/TsFWKuv+eHm2FKRZNeLcTS1oGNZGsty7iFa7To+b4F3lA7HxEQ936EOjQuDOwQ==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/statuslight": "^3.3.6",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/switch": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/switch/-/switch-3.5.0.tgz",
+            "integrity": "sha512-a9SbC3seaU/Ai840q1PZEZraB3EJosDqww0zKU80pDtTqtP4b3KWwvHrVRWOQ2PeJm1bm/aPkuyMa7cM12m2tQ==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/switch": "^3.5.7",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/toggle": "^3.7.0",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/switch": "^3.5.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/table": {
+            "version": "3.12.4",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/table/-/table-3.12.4.tgz",
+            "integrity": "sha512-29dWRfs0rZv4PXByyRZPdgPVQiyjZAm4hFhvqmZnpeKQx3+Y63MrO0xdDYIIbopmANhKT/JPO3jIG4gWMvd72A==",
+            "requires": {
+                "@react-aria/button": "^3.9.0",
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/table": "^3.13.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-aria/virtualizer": "^3.9.7",
+                "@react-aria/visually-hidden": "^3.8.7",
+                "@react-spectrum/checkbox": "^3.9.0",
+                "@react-spectrum/dnd": "^3.3.5",
+                "@react-spectrum/layout": "^3.6.0",
+                "@react-spectrum/menu": "^3.16.0",
+                "@react-spectrum/progress": "^3.7.2",
+                "@react-spectrum/tooltip": "^3.6.2",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/flags": "^3.0.0",
+                "@react-stately/layout": "^3.13.4",
+                "@react-stately/table": "^3.11.3",
+                "@react-stately/virtualizer": "^3.6.5",
+                "@react-types/grid": "^3.2.3",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/table": "^3.9.1",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/tabs": {
+            "version": "3.8.4",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/tabs/-/tabs-3.8.4.tgz",
+            "integrity": "sha512-wDMHf8Xa3lCh7vkkpwT3G8Vd6ooo5VSHlMv1NUQsFjlymbZQ88aBkZfJVBxag7fWoO/Yo2IGf5Tb2Gpj1hW99Q==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/tabs": "^3.8.2",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/picker": "^3.13.2",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/list": "^3.10.1",
+                "@react-stately/tabs": "^3.6.2",
+                "@react-types/select": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/tabs": "^3.3.4",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/tag": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/tag/-/tag-3.2.0.tgz",
+            "integrity": "sha512-BrYpyaoJkaHL/74DzaCJjMzn544TvJbf9uJaZ3TUwt8MQ4rAOdHKn5fI3jiX665WWJPhsI6Dfns/gN/4HlqfJA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/selection": "^3.17.2",
+                "@react-aria/tag": "^3.3.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/button": "^3.15.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/text": "^3.5.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/list": "^3.10.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/text": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/text/-/text-3.5.0.tgz",
+            "integrity": "sha512-gZ2sqrfz/T8kYhS/TUXoR6ZhC9apU1i+K+8W9LV7LXAraZYQiDTiglTqnxER+qtW/+Xon1ZDoL2TICt63wHFFA==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/text": "^3.3.6",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/textfield": {
+            "version": "3.11.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/textfield/-/textfield-3.11.0.tgz",
+            "integrity": "sha512-bAG4hwFUJGWiViaOBXDWvYxsMvzs8+rCfdiLil7b5CPgbZCpvsHVMSt8dOQW18MqrJNGv2Zm8gZYjvFSnfklAA==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/interactions": "^3.20.0",
+                "@react-aria/textfield": "^3.13.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/form": "^3.7.0",
+                "@react-spectrum/label": "^3.16.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/textfield": "^3.9.0",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/theme-dark": {
+            "version": "3.5.7",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-dark/-/theme-dark-3.5.7.tgz",
+            "integrity": "sha512-WaBVkhJKsZNcsHCcm6HQOSV934KLxZK6cbTt/FjUZtNTHcQTb5s2xf2jPeMwcRbRJfzagfTuTr5BkM38B9Zi1Q==",
+            "requires": {
+                "@react-types/provider": "^3.7.1",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/theme-default": {
+            "version": "3.5.7",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-default/-/theme-default-3.5.7.tgz",
+            "integrity": "sha512-5GgCKfzA1be3pE5XtQ+5hk39I4BhU1qY+fHCDxvE/tndm2I7pCk26d6ifxA0H2RVKH8V3eiH0WG1TNXHcHIO1g==",
+            "requires": {
+                "@react-types/provider": "^3.7.1",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/theme-light": {
+            "version": "3.4.7",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-light/-/theme-light-3.4.7.tgz",
+            "integrity": "sha512-3NeIDN9QU8c40rqRSzurTdJRTZ5Uag4c2fLlF9Bve/yF7N6KlzK29H3EWNLBizAfozhPe5/HDEYqdArz7bxhlQ==",
+            "requires": {
+                "@react-types/provider": "^3.7.1",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/tooltip": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/tooltip/-/tooltip-3.6.2.tgz",
+            "integrity": "sha512-aZ0CSlWOGgRQe/u41PgCgiXdO7+GowGE8CA46BCA8DEoqrw3JjSOX2JYClrPUra1VxHXId8AeQGU5bRw3abb2g==",
+            "requires": {
+                "@react-aria/focus": "^3.15.0",
+                "@react-aria/overlays": "^3.19.0",
+                "@react-aria/tooltip": "^3.6.5",
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/overlays": "^5.5.2",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-stately/tooltip": "^3.4.6",
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/tooltip": "^3.4.6",
+                "@spectrum-icons/ui": "^3.6.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/utils": {
+            "version": "3.11.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/utils/-/utils-3.11.2.tgz",
+            "integrity": "sha512-JLv0ntBrrIvloUgqVhAZvTTiBqZ/pHtAWOuIgrii3PFUsds4OVX+YebB88rj639Zi/tFrSdfrlZJNER1ZOq9jw==",
+            "requires": {
+                "@react-aria/i18n": "^3.9.0",
+                "@react-aria/ssr": "^3.9.0",
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0",
+                "clsx": "^1.1.1"
+            }
+        },
+        "@react-spectrum/view": {
+            "version": "3.6.5",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/view/-/view-3.6.5.tgz",
+            "integrity": "sha512-y135Ownoi6Hw466k4hWcYBJmqjTQlKVgXtCT5aKpKm8l9RlpvPvtoUe5OBUxKkHHgB1TCIOpy76hLi9SQc0Omw==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/view": "^3.4.6",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-spectrum/well": {
+            "version": "3.4.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/well/-/well-3.4.8.tgz",
+            "integrity": "sha512-A2fJxlT/eAKyPTbCOmoUNB+BjDP8RnViZ7X6/gcxjEAtn5KVnJ6DmZTG8Kw+yPnuLxK+5Lui/dfGI9g19GWohw==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-spectrum/utils": "^3.11.2",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/well": "^3.3.6",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/calendar": {
+            "version": "3.4.2",
+            "resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.4.2.tgz",
+            "integrity": "sha512-RfH40rVa2EhUnQgqH3HTZL+YhL+6tZ8T9GbN1K3AbIM5BBEtkb3P8qGhcaI7WpwNy1rlRFFFXGcqFAMUncDg2Q==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/calendar": "^3.4.2",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/checkbox": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/checkbox/-/checkbox-3.6.0.tgz",
+            "integrity": "sha512-e1ChMwGovcOEDcdizqXDT6eDZixIMiPQOzNV5wPQ91SlGaIry9b0lQnK18tHg3yv2iiS6Ipj96cGBUKLJqQ+cQ==",
+            "requires": {
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/checkbox": "^3.6.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/collections": {
+            "version": "3.10.3",
+            "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.10.3.tgz",
+            "integrity": "sha512-fA28HIApAIz9sNGeOVXZJPgV5Kig6M72KI1t9sUbnRUr9Xq9OMJTR6ElDMXNe0iTeZffRFDOPYyqnX9zkxof6Q==",
+            "requires": {
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/combobox": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/combobox/-/combobox-3.8.0.tgz",
+            "integrity": "sha512-F74Avf7+8ruRqEB+3Lh6/C5jXc3ESJbRf9ovUxhmNAzBGeFKesPn5HpEpo87C+3OukGb+/Buvi3Rhib9+HVBKA==",
+            "requires": {
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/list": "^3.10.1",
+                "@react-stately/menu": "^3.5.7",
+                "@react-stately/select": "^3.6.0",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/combobox": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/data": {
+            "version": "3.11.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/data/-/data-3.11.0.tgz",
+            "integrity": "sha512-0BlPT58WrAtUvpiEfUuyvIsGFTzp/9vA5y+pk53kGJhOdc5tqBGHi9cg40pYE/i1vdHJGMpyHGRD9nkQb8wN3Q==",
+            "requires": {
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/datepicker": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/datepicker/-/datepicker-3.9.0.tgz",
+            "integrity": "sha512-p6BuxPbDxjIgBZmskdv2dR6XIdPEftCjS7kYe/+iLZxfz1vYiDqpJVb3ascLyBjl84bDDyr4z2vWcKhdDwyhEA==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@internationalized/string": "^3.1.1",
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/overlays": "^3.6.4",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/datepicker": "^3.7.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/dnd": {
+            "version": "3.2.6",
+            "resolved": "https://registry.npmjs.org/@react-stately/dnd/-/dnd-3.2.6.tgz",
+            "integrity": "sha512-ex3Pjn+9uIoqsBb9F4ZFJb3fB0YadN8uYBOEiBb9N4UXWyANibGUYJ2FvIbvq1nFDU7On7MW1J9e3vkGglX4FQ==",
+            "requires": {
+                "@react-stately/selection": "^3.14.1",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/flags": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.0.0.tgz",
+            "integrity": "sha512-e3i2ItHbIa0eEwmSXAnPdD7K8syW76JjGe8ENxwFJPW/H1Pu9RJfjkCb/Mq0WSPN/TpxBb54+I9TgrGhbCoZ9w==",
+            "requires": {
+                "@swc/helpers": "^0.4.14"
+            },
+            "dependencies": {
+                "@swc/helpers": {
+                    "version": "0.4.36",
+                    "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.36.tgz",
+                    "integrity": "sha512-5lxnyLEYFskErRPenYItLRSge5DjrJngYKdVjRSrWfza9G6KkgHEXi0vUZiyUeMU5JfXH1YnvXZzSp8ul88o2Q==",
+                    "requires": {
+                        "legacy-swc-helpers": "npm:@swc/helpers@=0.4.14",
+                        "tslib": "^2.4.0"
+                    },
+                    "dependencies": {
+                        "legacy-swc-helpers": {
+                            "version": "npm:@swc/helpers@0.4.14",
+                            "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz",
+                            "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==",
+                            "requires": {
+                                "tslib": "^2.4.0"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "@react-stately/form": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/form/-/form-3.0.0.tgz",
+            "integrity": "sha512-C8wkfFmtx1escizibhdka5JvTy9/Vp173CS9cakjvWTmnjYYC1nOlzwp7BsYWTgerCFbRY/BU/Cf/bJDxPiUKQ==",
+            "requires": {
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/grid": {
+            "version": "3.8.3",
+            "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.8.3.tgz",
+            "integrity": "sha512-JceGSJcuO6Zv+Aq5s2NZvmbMjdPjTtGNQR9kTgXKC/pOfM6FJ58bJiOmEllyN6oawqh4Ey8Xdqk9NuW4l2ctuw==",
+            "requires": {
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/selection": "^3.14.1",
+                "@react-types/grid": "^3.2.3",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/layout": {
+            "version": "3.13.4",
+            "resolved": "https://registry.npmjs.org/@react-stately/layout/-/layout-3.13.4.tgz",
+            "integrity": "sha512-gmtkB+EISfB1Ne/56cd4djJXUwCtcrjzA7BHzBBXcjJNmTc/NQh5+qh66JHkXTKF3PT5FdVtmO2MODqGaWkUeg==",
+            "requires": {
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/table": "^3.11.3",
+                "@react-stately/virtualizer": "^3.6.5",
+                "@react-types/grid": "^3.2.3",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/table": "^3.9.1",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/list": {
+            "version": "3.10.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.10.1.tgz",
+            "integrity": "sha512-iVarLMd7FmMT0H20dRWsFOHHX5+c4gK51AXP2BSr1VtDSfbL4dgaGgu7IaAMVc/rO0au1e1tPM2hutiIFvPcnA==",
+            "requires": {
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/selection": "^3.14.1",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/menu": {
+            "version": "3.5.7",
+            "resolved": "https://registry.npmjs.org/@react-stately/menu/-/menu-3.5.7.tgz",
+            "integrity": "sha512-bzTmAqzcMNatvyruWlvOdZSmMhz3+mkdxtqaZzYHq+DpR6ka57lIRj8dBnZWQGwV3RypMZfz+X6aIX4kruGVbw==",
+            "requires": {
+                "@react-stately/overlays": "^3.6.4",
+                "@react-types/menu": "^3.9.6",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/numberfield": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/numberfield/-/numberfield-3.7.0.tgz",
+            "integrity": "sha512-DOz4jL7T30KGUXpGh/z80aHf+DEOQfvCHVDfll+IU7p3sd+bbM5uj7JdwXpZgIYUK8KTf2N49sL6lq5uCoxh8w==",
+            "requires": {
+                "@internationalized/number": "^3.4.0",
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/numberfield": "^3.7.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/overlays": {
+            "version": "3.6.4",
+            "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.4.tgz",
+            "integrity": "sha512-tHEaoAGpE9dSnsskqLPVKum59yGteoSqsniTopodM+miQozbpPlSjdiQnzGLroy5Afx5OZYClE616muNHUILXA==",
+            "requires": {
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/overlays": "^3.8.4",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/radio": {
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/radio/-/radio-3.10.0.tgz",
+            "integrity": "sha512-d8IgZtUq/4vhE7YhyBVg1QdVoFS0caIcvPumXqtp/5vlDgpUsVy9jSeWtbk0H4FyUcmJlQhRcTylKB9THXY1YQ==",
+            "requires": {
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/radio": "^3.6.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/searchfield": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/searchfield/-/searchfield-3.5.0.tgz",
+            "integrity": "sha512-SStjChkn/33pEn40slKQPnBnmQYyxVazVwPjiBkdeVejC42lUVairUTrGJgF0PNoZTbxn0so2/XzjqTC9T8iCw==",
+            "requires": {
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/searchfield": "^3.5.2",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/select": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/select/-/select-3.6.0.tgz",
+            "integrity": "sha512-GvSE4DXmcvdRNUc+ciPU7gedt7LfRO8FFFIzhB/bCQhUlK6/xihUPrGXayzqxLeTQKttMH323LuYFKfwpJRhsA==",
+            "requires": {
+                "@react-stately/form": "^3.0.0",
+                "@react-stately/list": "^3.10.1",
+                "@react-stately/menu": "^3.5.7",
+                "@react-types/select": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/selection": {
+            "version": "3.14.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.14.1.tgz",
+            "integrity": "sha512-96/CerrB6yH4Ad9FkzBzyVerSPjcIj1NBTWTFHo1N+oHECvyGsDxZl7Y4LQR++teFK66FhX5KjCJQGae4IZd6A==",
+            "requires": {
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/slider": {
+            "version": "3.4.5",
+            "resolved": "https://registry.npmjs.org/@react-stately/slider/-/slider-3.4.5.tgz",
+            "integrity": "sha512-lJPZC8seYbnZDqAlZm3/QC95I5iluG8ouwkPMmvtWCz1baayV/jJtfxA/74zR7Vcob9Fe7O57g8Edhz/hv9xOQ==",
+            "requires": {
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/slider": "^3.7.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/table": {
+            "version": "3.11.3",
+            "resolved": "https://registry.npmjs.org/@react-stately/table/-/table-3.11.3.tgz",
+            "integrity": "sha512-r0rzSKbtMG4tjFpCGtXb8p6hOuek03c6rheJE88z4I/ujZ5EmEO6Ps8q0JMNEDCY2qigvKM+ODisMBeZCEkIJg==",
+            "requires": {
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/flags": "^3.0.0",
+                "@react-stately/grid": "^3.8.3",
+                "@react-stately/selection": "^3.14.1",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/grid": "^3.2.3",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/table": "^3.9.1",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/tabs": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/@react-stately/tabs/-/tabs-3.6.2.tgz",
+            "integrity": "sha512-f+U4D1FAVfVVcNRbtKIv4GrO37CLFClYQlXx9zIuSXjHsviapVD2IQSyAmpKo/CbgXhYRMdGwENZdOsmF/Ns7g==",
+            "requires": {
+                "@react-stately/list": "^3.10.1",
+                "@react-types/shared": "^3.22.0",
+                "@react-types/tabs": "^3.3.4",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/toggle": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.7.0.tgz",
+            "integrity": "sha512-TRksHkCJk/Xogq4181g3CYgJf+EfsJCqX5UZDSw1Z1Kgpvonjmdf6FAfQfCh9QR2OuXUL6hOLUDVLte5OPI+5g==",
+            "requires": {
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/checkbox": "^3.6.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/tooltip": {
+            "version": "3.4.6",
+            "resolved": "https://registry.npmjs.org/@react-stately/tooltip/-/tooltip-3.4.6.tgz",
+            "integrity": "sha512-uL93bmsXf+OOgpKLPEKfpDH4z+MK2CuqlqVxx7rshN0vjWOSoezE5nzwgee90+RpDrLNNNWTNa7n+NkDRpI1jA==",
+            "requires": {
+                "@react-stately/overlays": "^3.6.4",
+                "@react-types/tooltip": "^3.4.6",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/tree": {
+            "version": "3.7.4",
+            "resolved": "https://registry.npmjs.org/@react-stately/tree/-/tree-3.7.4.tgz",
+            "integrity": "sha512-0yvVODBS8WnSivLFX5ccEjCl2NA/8lbEt1E48wVcY1xcXgISNpw5MSGK5jC6YrtJPIqVolQIkNSbMreXGBktIg==",
+            "requires": {
+                "@react-stately/collections": "^3.10.3",
+                "@react-stately/selection": "^3.14.1",
+                "@react-stately/utils": "^3.9.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/utils": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.9.0.tgz",
+            "integrity": "sha512-yPKFY1F88HxuZ15BG2qwAYxtpE4HnIU0Ofi4CuBE0xC6I8mwo4OQjDzi+DZjxQngM9D6AeTTD6F1V8gkozA0Gw==",
+            "requires": {
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-stately/virtualizer": {
+            "version": "3.6.5",
+            "resolved": "https://registry.npmjs.org/@react-stately/virtualizer/-/virtualizer-3.6.5.tgz",
+            "integrity": "sha512-v0cZeNCGPMeo3LP4UrGuDo3Xpq7ufNaZyGObgSvdrIW49qK5F02kczcKy6NKg+QfOgC/+Nc9Tof/2S8dcxDrCA==",
+            "requires": {
+                "@react-aria/utils": "^3.22.0",
+                "@react-types/shared": "^3.22.0",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@react-three/fiber": {
+            "version": "6.2.3",
+            "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-6.2.3.tgz",
+            "integrity": "sha512-XuL3683iVcwjm3enour57k4OjkA1MQztqpRL2oQh30v7ygf1vJg95gKo8cqqgPJctc2pZAMUW1eRpr+Xqjwqrg==",
+            "requires": {
+                "@babel/runtime": "^7.13.10",
+                "react-merge-refs": "^1.1.0",
+                "react-reconciler": "^0.26.2",
+                "react-three-fiber": "0.0.0-deprecated",
+                "react-use-measure": "^2.0.4",
+                "resize-observer-polyfill": "^1.5.1",
+                "scheduler": "^0.20.2",
+                "use-asset": "^1.0.4",
+                "utility-types": "^3.10.0",
+                "zustand": "^3.5.1"
+            }
+        },
+        "@react-types/actionbar": {
+            "version": "3.1.4",
+            "resolved": "https://registry.npmjs.org/@react-types/actionbar/-/actionbar-3.1.4.tgz",
+            "integrity": "sha512-/z9N7ztd/MOdEDQNHTCNviYe0+rqy1s19Xg3tv/PV1oUCOsjrnja85VVxoa+AWR8IbwgDNIfpXg2Wa66b1FDbw==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/actiongroup": {
+            "version": "3.4.6",
+            "resolved": "https://registry.npmjs.org/@react-types/actiongroup/-/actiongroup-3.4.6.tgz",
+            "integrity": "sha512-Dho2mEDCU9ZAW+QX2HZkZhyxHK/EGfTvSWdHBFaCYsh4CPI/6PcvtirpSKMrzNNaZ97Exthv3GcLpAnLwM9jZw==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/avatar": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/@react-types/avatar/-/avatar-3.0.4.tgz",
+            "integrity": "sha512-fQ+qGce0EqcX0s2glnFjfvxSq42GHaqvl+eL8TnsDz0OIvB8KKzTO/rV/q1CIy/LtMP8fjCb6oqVFQcLfuODfw==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/badge": {
+            "version": "3.1.6",
+            "resolved": "https://registry.npmjs.org/@react-types/badge/-/badge-3.1.6.tgz",
+            "integrity": "sha512-6xjgfRnCVSBI6l/RQkI4u3tXiXw1aeFKRqXPcyIyt/kuu7rP0nKeYcM2XYyXXQp7vHfBdOEL2f6LwIHR/lx4uQ==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/breadcrumbs": {
+            "version": "3.7.2",
+            "resolved": "https://registry.npmjs.org/@react-types/breadcrumbs/-/breadcrumbs-3.7.2.tgz",
+            "integrity": "sha512-esl6RucDW2CNMsApJxNYfMtDaUcfLlwKMPH/loYsOBbKxGl2HsgVLMcdpjEkTRs2HCTNCbBXWpeU8AY77t+bsw==",
+            "requires": {
+                "@react-types/link": "^3.5.2",
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/button": {
+            "version": "3.9.1",
+            "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.9.1.tgz",
+            "integrity": "sha512-bf9iTar3PtqnyV9rA+wyFyrskZKhwmOuOd/ifYIjPs56YNVXWH5Wfqj6Dx3xdFBgtKx8mEVQxVhoX+WkHX+rtw==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/buttongroup": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/@react-types/buttongroup/-/buttongroup-3.3.6.tgz",
+            "integrity": "sha512-aKFDzAWM6bk2+EBDSZe3zq4NMuguXsPyZ9OexN0YLleK4IkRKE2S51PdChY/GAFhfs7VsOKgjHrYWUnrmCCYvw==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/calendar": {
+            "version": "3.4.2",
+            "resolved": "https://registry.npmjs.org/@react-types/calendar/-/calendar-3.4.2.tgz",
+            "integrity": "sha512-tCZ21un/8OAhpNtmSXDkOVvS5Pzp+y/JwNr6VGFi8HBC5F/c8SzuwV0jKN8ymsZSWbDQ68xXGNWxFaG43Bw8Pg==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/checkbox": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.6.0.tgz",
+            "integrity": "sha512-vgbuJzQpVCNT5AZWV0OozXCnihqrXxoZKfJFIw0xro47pT2sn3t5UC4RA9wfjDGMoK4frw1K/4HQLsQIOsPBkw==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/combobox": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-types/combobox/-/combobox-3.9.0.tgz",
+            "integrity": "sha512-VAQWM2jrIWROgcTKxj4k37WWpK/1zRjj1HfGeuenAQyOQwImqDwCHx5YxQR1GiUEFne4v1yXe2khT0T5Kt2vDg==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/contextualhelp": {
+            "version": "3.2.7",
+            "resolved": "https://registry.npmjs.org/@react-types/contextualhelp/-/contextualhelp-3.2.7.tgz",
+            "integrity": "sha512-BHwBSBhPekKc/PxDpnkvfcEgpaYLMrV6WYgMfUz2/BMYOjdm+pb1y80vpNkWtrJKytyqp1zeZ+Ca+xzX1HdazA==",
+            "requires": {
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/datepicker": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.7.0.tgz",
+            "integrity": "sha512-Uh+p6pZpMFc5ZBOns5TXCBbUvJp1KVROLBn2gk5dMEFVq78Qs1VFuAt4lwr9gQBOJrX5I/l65pRTwwWwAKxYtQ==",
+            "requires": {
+                "@internationalized/date": "^3.5.0",
+                "@react-types/calendar": "^3.4.2",
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/dialog": {
+            "version": "3.5.7",
+            "resolved": "https://registry.npmjs.org/@react-types/dialog/-/dialog-3.5.7.tgz",
+            "integrity": "sha512-geYoqAyQaTLG43AaXdMUVqZXYgkSifrD9cF7lR2kPAT0uGFv0YREi6ieU+aui8XJ83EW0xcxP+EPWd2YkN4D4w==",
+            "requires": {
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/divider": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/@react-types/divider/-/divider-3.3.6.tgz",
+            "integrity": "sha512-Iwwe349IiCX7ZQK1Oz4AN5kWwiXG0DECSN4qB3h+14n97JKy3chWJC7UA+V6+2p5DbxmLVZm4XxDRgx7y0lVTg==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/form": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-types/form/-/form-3.6.0.tgz",
+            "integrity": "sha512-+k6IpjQE+sVi/xoK5lnRGyeISkOQ+CKfuH8IeGcYVHr2voDxSJC5WZsp+L5zeoxuSorKokeEPKGOX2HFj9BG/A==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/grid": {
+            "version": "3.2.3",
+            "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.2.3.tgz",
+            "integrity": "sha512-GQM4RDmYhstcYZ0Odjq+xUwh1fhLmRebG6qMM8OXHTPQ77nhl3wc1UTGRhZm6mzEionplSRx4GCpEMEHMJIU0w==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/illustratedmessage": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/@react-types/illustratedmessage/-/illustratedmessage-3.3.6.tgz",
+            "integrity": "sha512-1FdJl1tR6mirmXT8yaTFeHNWdLXV6Dll66Mv1liEtTYsmCgn2anxwM73jK63t3jdT6ez/M1wGiwMlMtyiqo+ZQ==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/image": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/@react-types/image/-/image-3.3.6.tgz",
+            "integrity": "sha512-GSb0deyquS3kFt0e9SfPP9I/YaYYUToYYPzx9As0R0mzuVn6qTHhUtpKhBqQAOpE1Cd8XdEQeYsDB3sdOurI+A==",
             "requires": {
-                "@octokit/types": "^9.0.0"
+                "@react-types/shared": "^3.22.0"
             }
         },
-        "@octokit/core": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz",
-            "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==",
+        "@react-types/label": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-types/label/-/label-3.9.0.tgz",
+            "integrity": "sha512-nTmPf5ED8aLGqvFsZHIHwMPrRX0cfbOyayva//Rdis41KWQoKUB80DIQjE+iUDOgTivIxGBkpqdIZVqRuehTnw==",
             "requires": {
-                "@octokit/auth-token": "^3.0.0",
-                "@octokit/graphql": "^5.0.0",
-                "@octokit/request": "^6.0.0",
-                "@octokit/request-error": "^3.0.0",
-                "@octokit/types": "^9.0.0",
-                "before-after-hook": "^2.2.0",
-                "universal-user-agent": "^6.0.0"
+                "@react-types/shared": "^3.22.0"
             }
         },
-        "@octokit/endpoint": {
-            "version": "7.0.5",
-            "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.5.tgz",
-            "integrity": "sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==",
+        "@react-types/layout": {
+            "version": "3.3.12",
+            "resolved": "https://registry.npmjs.org/@react-types/layout/-/layout-3.3.12.tgz",
+            "integrity": "sha512-Ai6limgTVYQoGiXUvsXg8MHik+YAtRWEVLQhT5E1nQkDkNkQyccB+waUSfORhRkjJcnp+KMcbmPZ8V5ZO42rvQ==",
             "requires": {
-                "@octokit/types": "^9.0.0",
-                "is-plain-object": "^5.0.0",
-                "universal-user-agent": "^6.0.0"
+                "@react-types/shared": "^3.22.0"
             }
         },
-        "@octokit/graphql": {
-            "version": "5.0.5",
-            "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz",
-            "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==",
+        "@react-types/link": {
+            "version": "3.5.2",
+            "resolved": "https://registry.npmjs.org/@react-types/link/-/link-3.5.2.tgz",
+            "integrity": "sha512-/s51/WejmpLiyxOgP89s4txgxYoGaPe8pVDItVo1h4+BhU1Puyvgv/Jx8t9dPvo6LUXbraaN+SgKk/QDxaiirw==",
             "requires": {
-                "@octokit/request": "^6.0.0",
-                "@octokit/types": "^9.0.0",
-                "universal-user-agent": "^6.0.0"
+                "@react-types/shared": "^3.22.0"
             }
         },
-        "@octokit/openapi-types": {
-            "version": "16.0.0",
-            "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-            "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
+        "@react-types/listbox": {
+            "version": "3.4.6",
+            "resolved": "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.4.6.tgz",
+            "integrity": "sha512-XOQvrTqNh5WIPDvKiWiep8T07RAsMfjAXTjDbnjxVlKACUXkcwpts9kFaLnJ9LJRFt6DwItfP+WMkzvmx63/NQ==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
         },
-        "@octokit/request": {
-            "version": "6.2.3",
-            "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.3.tgz",
-            "integrity": "sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==",
+        "@react-types/menu": {
+            "version": "3.9.6",
+            "resolved": "https://registry.npmjs.org/@react-types/menu/-/menu-3.9.6.tgz",
+            "integrity": "sha512-w/RbFInOf4nNayQDv5c2L8IMJbcFOkBhsT3xvvpTy+CHvJcQdjggwaV1sRiw7eF/PwB81k2CwigmidUzHJhKDg==",
             "requires": {
-                "@octokit/endpoint": "^7.0.0",
-                "@octokit/request-error": "^3.0.0",
-                "@octokit/types": "^9.0.0",
-                "is-plain-object": "^5.0.0",
-                "node-fetch": "^2.6.7",
-                "universal-user-agent": "^6.0.0"
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0"
             }
         },
-        "@octokit/request-error": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz",
-            "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
+        "@react-types/meter": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/@react-types/meter/-/meter-3.3.6.tgz",
+            "integrity": "sha512-1XYp1fA9UU0lO6kjf3TwVE8mppOJa64mBKAcLWtTyq1e/cYIAbx5o6CsuUx0YDpXKF6gdtvIWvfmxeWsmqJ1jQ==",
             "requires": {
-                "@octokit/types": "^9.0.0",
-                "deprecation": "^2.0.0",
-                "once": "^1.4.0"
+                "@react-types/progress": "^3.5.1"
             }
         },
-        "@octokit/types": {
-            "version": "9.0.0",
-            "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-            "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
+        "@react-types/numberfield": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-types/numberfield/-/numberfield-3.7.0.tgz",
+            "integrity": "sha512-gaGi+vqm1Y8LCWRsWYUjcGftPIzl+8W2VOfkgKMLM8y76nnwTPtmAqs+Ap1cg7sEJSfsiKMq93e9yvP3udrC2w==",
             "requires": {
-                "@octokit/openapi-types": "^16.0.0"
+                "@react-types/shared": "^3.22.0"
             }
         },
-        "@opentelemetry/api": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz",
-            "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA=="
+        "@react-types/overlays": {
+            "version": "3.8.4",
+            "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.4.tgz",
+            "integrity": "sha512-pfgNlQnbF6RB/R2oSxyqAP3Uzz0xE/k5q4n5gUeCDNLjY5qxFHGE8xniZZ503nZYw6VBa9XMN1efDOKQyeiO0w==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
         },
-        "@pkgjs/parseargs": {
-            "version": "0.11.0",
-            "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
-            "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
-            "optional": true
+        "@react-types/progress": {
+            "version": "3.5.1",
+            "resolved": "https://registry.npmjs.org/@react-types/progress/-/progress-3.5.1.tgz",
+            "integrity": "sha512-CqsUjczUK/SfuFzDcajBBaXRTW0D3G9S/yqLDj9e8E0ii+lGDLt1PHj24t1J7E88U2rVYqmM9VL4NHTt8o3IYA==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
         },
-        "@popperjs/core": {
-            "version": "2.11.7",
-            "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
-            "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw=="
+        "@react-types/provider": {
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/@react-types/provider/-/provider-3.7.1.tgz",
+            "integrity": "sha512-WKwHwG5b0LkI570tbHCy4hBhT/E+OrdgIybScDxM713B2OwmMKKyaPKdV05SeoomP8oiPvkaAeXhLZa1ah7CYg==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
         },
-        "@react-google-maps/api": {
-            "version": "2.18.1",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.18.1.tgz",
-            "integrity": "sha512-KVlUO/Shh+0g/3egWaKmY0sz6+0QOnYkBGvrBMJbz23519LauA+iJFc4NDCmWNHqD5Vhb/Bkg0kSJgq0Stz3Iw==",
+        "@react-types/radio": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-types/radio/-/radio-3.6.0.tgz",
+            "integrity": "sha512-VOZzegxxZS55gHRVyWu278Q4y/rEQGiAVQCUqi25GmpbMe4MlHrzg16c76RiZMUK9PPoyv+XNUgAaPmxebkn7g==",
             "requires": {
-                "@googlemaps/js-api-loader": "1.15.1",
-                "@googlemaps/markerclusterer": "2.0.15",
-                "@react-google-maps/infobox": "2.16.0",
-                "@react-google-maps/marker-clusterer": "2.16.1",
-                "@types/google.maps": "3.50.5",
-                "invariant": "2.2.4"
+                "@react-types/shared": "^3.22.0"
             }
         },
-        "@react-google-maps/infobox": {
-            "version": "2.16.0",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.16.0.tgz",
-            "integrity": "sha512-ZojiMS25388RcUHQPycUAerSqdHDom+3dHczVcXHdT/i8fka3O8InkHxXwMhvBoM143ips7mv2BPaYOAJ5f4Nw=="
+        "@react-types/searchfield": {
+            "version": "3.5.2",
+            "resolved": "https://registry.npmjs.org/@react-types/searchfield/-/searchfield-3.5.2.tgz",
+            "integrity": "sha512-JAK2/Kg4Dr393FYfbRw0TlXKnJPX77sq1x/ZBxtO6p64+MuuIYKqw0i9PwDlo1PViw2QI5u8GFhKA2TgemY9uA==",
+            "requires": {
+                "@react-types/shared": "^3.22.0",
+                "@react-types/textfield": "^3.9.0"
+            }
         },
-        "@react-google-maps/marker-clusterer": {
-            "version": "2.16.1",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.16.1.tgz",
-            "integrity": "sha512-jOuyqzWLeXvQcoAu6TCVWHAuko+sDt0JjawNHBGqUNLywMtTCvYP0L0PiqJZOUCUeRYGdUy0AKxQ+30vAkvwag=="
+        "@react-types/select": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.9.0.tgz",
+            "integrity": "sha512-0nalGmcoma4jreICLSJae/uKAuMiVyWgqWjGrGiUGGcdDchH4limKVEqNDaBwLvxVT6NB5LLsaipCTCAEEl4Rg==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
         },
-        "@react-three/fiber": {
-            "version": "6.2.3",
-            "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-6.2.3.tgz",
-            "integrity": "sha512-XuL3683iVcwjm3enour57k4OjkA1MQztqpRL2oQh30v7ygf1vJg95gKo8cqqgPJctc2pZAMUW1eRpr+Xqjwqrg==",
+        "@react-types/shared": {
+            "version": "3.22.0",
+            "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.22.0.tgz",
+            "integrity": "sha512-yVOekZWbtSmmiThGEIARbBpnmUIuePFlLyctjvCbgJgGhz8JnEJOipLQ/a4anaWfzAgzSceQP8j/K+VOOePleA=="
+        },
+        "@react-types/slider": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-types/slider/-/slider-3.7.0.tgz",
+            "integrity": "sha512-uyQXUVFfqc9SPUW0LZLMan2n232F/OflRafiHXz9viLFa9tVOupVa7GhASRAoHojwkjoJ1LjFlPih7g5dOZ0/Q==",
             "requires": {
-                "@babel/runtime": "^7.13.10",
-                "react-merge-refs": "^1.1.0",
-                "react-reconciler": "^0.26.2",
-                "react-three-fiber": "0.0.0-deprecated",
-                "react-use-measure": "^2.0.4",
-                "resize-observer-polyfill": "^1.5.1",
-                "scheduler": "^0.20.2",
-                "use-asset": "^1.0.4",
-                "utility-types": "^3.10.0",
-                "zustand": "^3.5.1"
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/statuslight": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/@react-types/statuslight/-/statuslight-3.3.6.tgz",
+            "integrity": "sha512-MB/CnsbaE6reOrnpowJfgkpeSNY0ZuqA6g/k8331a+TP2yIO6X0cUYyEGG8S/k9hFyFCMKlcmmm4pwMrX4sZtQ==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/switch": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.0.tgz",
+            "integrity": "sha512-/wNmUGjk69bP6t5k2QkAdrNN5Eb9Rz4dOyp0pCPmoeE+5haW6sV5NmtkvWX1NSc4DQz1xL/a5b+A0vxPCP22Jw==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/table": {
+            "version": "3.9.1",
+            "resolved": "https://registry.npmjs.org/@react-types/table/-/table-3.9.1.tgz",
+            "integrity": "sha512-3e+Oouw9jGqNDg+JRg7v7fgPqDZd6DtST9S/UPp81f32ntnQ8Wsu7S/J4eyLHu5CVQDqcHkf4xPeeXBgPx4qmw==",
+            "requires": {
+                "@react-types/grid": "^3.2.3",
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/tabs": {
+            "version": "3.3.4",
+            "resolved": "https://registry.npmjs.org/@react-types/tabs/-/tabs-3.3.4.tgz",
+            "integrity": "sha512-4mCTtFrwMRypyGTZCvNYVT9CkknexO/UYvqwDm2jMYb8JgjRvxnomu776Yh7uyiYKWyql2upm20jqasEOm620w==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/text": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/@react-types/text/-/text-3.3.6.tgz",
+            "integrity": "sha512-cO3IQ/DQ/xUGGskJ8/zCLkbzvrjlQbRnrJl95BEGs97CmiN+zqGoCqvDhjWEbuPRtfGXJ27CYZDC2oVZetUG4w==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/textfield": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/@react-types/textfield/-/textfield-3.9.0.tgz",
+            "integrity": "sha512-D/DiwzsfkwlAg3uv8hoIfwju+zhB/hWDEdTvxQbPkntDr0kmN/QfI17NMSzbOBCInC4ABX87ViXLGxr940ykGA==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/tooltip": {
+            "version": "3.4.6",
+            "resolved": "https://registry.npmjs.org/@react-types/tooltip/-/tooltip-3.4.6.tgz",
+            "integrity": "sha512-RaZewdER7ZcsNL99RhVHs8kSLyzIBkwc0W6eFZrxST2MD9J5GzkVWRhIiqtFOd5U1aYnxdJ6woq72Ef+le6Vfw==",
+            "requires": {
+                "@react-types/overlays": "^3.8.4",
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/view": {
+            "version": "3.4.6",
+            "resolved": "https://registry.npmjs.org/@react-types/view/-/view-3.4.6.tgz",
+            "integrity": "sha512-GAdvvabJAYrVCgOUsZp8KkmNLfkKnDmoMNmwCN9I2OnSS+5JyjTrgNIOiznMjDEqhXTbaefcsVofoUfTYXjtyQ==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
+            }
+        },
+        "@react-types/well": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/@react-types/well/-/well-3.3.6.tgz",
+            "integrity": "sha512-NX4+bMmNYrbjllKR9Xxg0YHNWrscHzZQmcdYiM/Z8qZ1TNVPhXeLmKxCDamlmUSZudCqwui4q5xwzuUyrRRA6w==",
+            "requires": {
+                "@react-types/shared": "^3.22.0"
             }
         },
         "@rkusa/linebreak": {
@@ -5462,6 +7530,23 @@
                 "unicode-trie": "^0.3.0"
             }
         },
+        "@rsuite/icon-font": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/@rsuite/icon-font/-/icon-font-4.0.0.tgz",
+            "integrity": "sha512-rZTgpTH3H3HLczCA2rnkWfoMKm0ZXoRzsrkVujfP/FfslnKUMvO6w56pa8pCvhWGpNEPUsLS2ULnFGpTEcup/Q=="
+        },
+        "@rsuite/icons": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/@rsuite/icons/-/icons-1.0.3.tgz",
+            "integrity": "sha512-qkjYFn1v5YV9eH57Q4AJ8CwsQYfILun2wdoxhQg5+xYxkIu6UyF8vTMmpOzLvcybTE7D8STm4dH7vhpyhPOC7g==",
+            "requires": {
+                "@babel/runtime": "^7.12.1",
+                "@rsuite/icon-font": "^4.0.0",
+                "classnames": "^2.2.5",
+                "insert-css": "^2.0.0",
+                "lodash": "^4.17.20"
+            }
+        },
         "@sigstore/protobuf-specs": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz",
@@ -5488,6 +7573,34 @@
             "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
             "integrity": "sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw=="
         },
+        "@spectrum-icons/ui": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/@spectrum-icons/ui/-/ui-3.6.2.tgz",
+            "integrity": "sha512-X13fgmi3h0fzolZF+cVJ6NvA91VCb055eBGkhhkQ+vjvUZrHZ8ACD2YUfaDt44BX+fsGUBOZ4fwOrdXnAaVhDQ==",
+            "requires": {
+                "@adobe/react-spectrum-ui": "1.2.0",
+                "@react-spectrum/icon": "^3.7.8",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@spectrum-icons/workflow": {
+            "version": "4.2.7",
+            "resolved": "https://registry.npmjs.org/@spectrum-icons/workflow/-/workflow-4.2.7.tgz",
+            "integrity": "sha512-Qrl2VPDsNd6WyEbqSvcMHPPRKHr6hhFcYXuh26h7XosmIf1irHZqvhpBGEJun2ADao4EQkwMUDy+3VSzvxuGIQ==",
+            "requires": {
+                "@adobe/react-spectrum-workflow": "2.3.4",
+                "@react-spectrum/icon": "^3.7.8",
+                "@swc/helpers": "^0.5.0"
+            }
+        },
+        "@swc/helpers": {
+            "version": "0.5.3",
+            "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz",
+            "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==",
+            "requires": {
+                "tslib": "^2.4.0"
+            }
+        },
         "@szmarczak/http-timer": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
@@ -6959,8 +9072,7 @@
         "@types/chai": {
             "version": "4.3.4",
             "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz",
-            "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==",
-            "dev": true
+            "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw=="
         },
         "@types/color": {
             "version": "3.0.3",
@@ -7551,8 +9663,7 @@
         "@types/lodash": {
             "version": "4.14.191",
             "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
-            "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
-            "dev": true
+            "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
         },
         "@types/mapbox-gl": {
             "version": "2.7.19",
@@ -7923,25 +10034,16 @@
                 "@types/reactcss": "*"
             }
         },
-        "@types/react-date-range": {
-            "version": "1.4.9",
-            "resolved": "https://registry.npmjs.org/@types/react-date-range/-/react-date-range-1.4.9.tgz",
-            "integrity": "sha512-5oVEDW0ElYmY1+YVSzdMUR8stxSI5QrRJCgCFUvuEAV5197t412vimD9aVTW6g4JTaxCnMmB1BdEOT/odpaBxQ==",
-            "dev": true,
-            "requires": {
-                "@types/react": "*",
-                "date-fns": "^2.16.1"
-            }
-        },
         "@types/react-datepicker": {
-            "version": "3.1.8",
-            "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-3.1.8.tgz",
-            "integrity": "sha512-RFEg7++xhosMq02i2lsuaUPEbZGn66U3dxtvw9LU/ZRqLkBGr9Ft2LTz6vbeYYVtaBdOr0NcQatOLnlfUaS8kw==",
+            "version": "4.19.4",
+            "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.19.4.tgz",
+            "integrity": "sha512-HRD0LHTxBVe61LRJgTdPscbapLQl7+jI/7bxnPGpvzdJ/iXN9q7ucYv8HKULeIAN84O5LzFhwTMOkO4QnIUJaQ==",
             "dev": true,
             "requires": {
+                "@popperjs/core": "^2.9.2",
                 "@types/react": "*",
                 "date-fns": "^2.0.1",
-                "popper.js": "^1.14.1"
+                "react-popper": "^2.2.5"
             }
         },
         "@types/react-dom": {
@@ -8044,6 +10146,14 @@
                 "@types/react": "*"
             }
         },
+        "@types/react-window": {
+            "version": "1.8.8",
+            "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz",
+            "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==",
+            "requires": {
+                "@types/react": "*"
+            }
+        },
         "@types/reactcss": {
             "version": "1.2.6",
             "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.6.tgz",
@@ -13711,6 +15821,14 @@
                 "@babel/runtime": "^7.1.2"
             }
         },
+        "dom-lib": {
+            "version": "3.1.6",
+            "resolved": "https://registry.npmjs.org/dom-lib/-/dom-lib-3.1.6.tgz",
+            "integrity": "sha512-xXEhStHDoAyfhnz8mqDwZ9rnqdqz/9BcrKd1UEw6BlA/l17emFb2dK7q8IX8ArU31pScSU9otEnL6wzvpoT5aw==",
+            "requires": {
+                "@babel/runtime": "^7.20.0"
+            }
+        },
         "dom-serializer": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@@ -18153,6 +20271,11 @@
                 }
             }
         },
+        "insert-css": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz",
+            "integrity": "sha512-xGq5ISgcUP5cvGkS2MMFLtPDBtrtQPSFfC6gA6U8wHKqfjTIMZLZNxOItQnoSjdOzlXOLU/yD32RKC4SvjNbtA=="
+        },
         "inspect-function": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
@@ -18335,6 +20458,17 @@
                 }
             }
         },
+        "intl-messageformat": {
+            "version": "10.5.8",
+            "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.8.tgz",
+            "integrity": "sha512-NRf0jpBWV0vd671G5b06wNofAN8tp7WWDogMZyaU8GUAsmbouyvgwmFJI7zLjfAMpm3zK+vSwRP3jzaoIcMbaA==",
+            "requires": {
+                "@formatjs/ecma402-abstract": "1.18.0",
+                "@formatjs/fast-memoize": "2.2.0",
+                "@formatjs/icu-messageformat-parser": "2.7.3",
+                "tslib": "^2.4.0"
+            }
+        },
         "invariant": {
             "version": "2.2.4",
             "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -26964,11 +29098,6 @@
                 "splaytree": "^3.1.0"
             }
         },
-        "popper.js": {
-            "version": "1.16.1",
-            "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
-            "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
-        },
         "portfinder": {
             "version": "1.0.32",
             "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
@@ -27940,27 +30069,45 @@
                 }
             }
         },
-        "react-date-range": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/react-date-range/-/react-date-range-1.4.0.tgz",
-            "integrity": "sha512-+9t0HyClbCqw1IhYbpWecjsiaftCeRN5cdhsi9v06YdimwyMR2yYHWcgVn3URwtN/txhqKpEZB6UX1fHpvK76w==",
-            "requires": {
-                "classnames": "^2.2.6",
-                "prop-types": "^15.7.2",
-                "react-list": "^0.8.13",
-                "shallow-equal": "^1.2.1"
-            }
-        },
         "react-datepicker": {
-            "version": "3.8.0",
-            "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.8.0.tgz",
-            "integrity": "sha512-iFVNEp8DJoX5yEvEiciM7sJKmLGrvE70U38KhpG13XrulNSijeHw1RZkhd/0UmuXR71dcZB/kdfjiidifstZjw==",
+            "version": "4.24.0",
+            "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.24.0.tgz",
+            "integrity": "sha512-2QUC2pP+x4v3Jp06gnFllxKsJR0yoT/K6y86ItxEsveTXUpsx+NBkChWXjU0JsGx/PL8EQnsxN0wHl4zdA1m/g==",
             "requires": {
+                "@popperjs/core": "^2.11.8",
                 "classnames": "^2.2.6",
-                "date-fns": "^2.0.1",
+                "date-fns": "^2.30.0",
                 "prop-types": "^15.7.2",
-                "react-onclickoutside": "^6.10.0",
-                "react-popper": "^1.3.8"
+                "react-onclickoutside": "^6.13.0",
+                "react-popper": "^2.3.0"
+            },
+            "dependencies": {
+                "@babel/runtime": {
+                    "version": "7.23.6",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz",
+                    "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
+                    "requires": {
+                        "regenerator-runtime": "^0.14.0"
+                    }
+                },
+                "@popperjs/core": {
+                    "version": "2.11.8",
+                    "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+                    "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
+                },
+                "date-fns": {
+                    "version": "2.30.0",
+                    "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+                    "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+                    "requires": {
+                        "@babel/runtime": "^7.21.0"
+                    }
+                },
+                "regenerator-runtime": {
+                    "version": "0.14.1",
+                    "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+                    "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+                }
             }
         },
         "react-dock": {
@@ -28030,6 +30177,11 @@
                 }
             }
         },
+        "react-fast-compare": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+            "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
+        },
         "react-grid-layout": {
             "version": "1.3.4",
             "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-1.3.4.tgz",
@@ -28133,14 +30285,6 @@
             "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
             "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
         },
-        "react-list": {
-            "version": "0.8.17",
-            "resolved": "https://registry.npmjs.org/react-list/-/react-list-0.8.17.tgz",
-            "integrity": "sha512-pgmzGi0G5uGrdHzMhgO7KR1wx5ZXVvI3SsJUmkblSAKtewIhMwbQiMuQiTE83ozo04BQJbe0r3WIWzSO0dR1xg==",
-            "requires": {
-                "prop-types": "15"
-            }
-        },
         "react-loader-spinner": {
             "version": "5.3.4",
             "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-5.3.4.tgz",
@@ -28250,21 +30394,16 @@
             "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ=="
         },
         "react-onclickoutside": {
-            "version": "6.12.2",
-            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz",
-            "integrity": "sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA=="
+            "version": "6.13.0",
+            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz",
+            "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A=="
         },
         "react-popper": {
-            "version": "1.3.11",
-            "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
-            "integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==",
-            "requires": {
-                "@babel/runtime": "^7.1.2",
-                "@hypnosphi/create-react-context": "^0.3.1",
-                "deep-equal": "^1.1.1",
-                "popper.js": "^1.14.4",
-                "prop-types": "^15.6.1",
-                "typed-styles": "^0.0.7",
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+            "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+            "requires": {
+                "react-fast-compare": "^3.0.1",
                 "warning": "^4.0.2"
             },
             "dependencies": {
@@ -28472,6 +30611,20 @@
                 }
             }
         },
+        "react-use-set": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/react-use-set/-/react-use-set-1.0.0.tgz",
+            "integrity": "sha512-6BBbOcWc/tOKuwd9gDtdunvOr/g40S0SkCBYvrSJvpI0upzNlHmLoeDvylnoP8PrjQXItClAFxseVGGhEkk7kw=="
+        },
+        "react-window": {
+            "version": "1.8.10",
+            "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
+            "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
+            "requires": {
+                "@babel/runtime": "^7.0.0",
+                "memoize-one": ">=3.1.1 <6"
+            }
+        },
         "reactcss": {
             "version": "1.2.3",
             "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
@@ -29283,6 +31436,50 @@
             "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.3.tgz",
             "integrity": "sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q=="
         },
+        "rsuite": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/rsuite/-/rsuite-5.48.0.tgz",
+            "integrity": "sha512-RCqBOU867qErFw/QCJHaA47lwZ7u++/Pk1gpG0YFL3FXxvS3rpo1YDO1QlRyN0WqyZuuANEymbqe6fG21J8+ig==",
+            "requires": {
+                "@babel/runtime": "^7.20.1",
+                "@juggle/resize-observer": "^3.4.0",
+                "@rsuite/icons": "^1.0.2",
+                "@types/chai": "^4.3.3",
+                "@types/lodash": "^4.14.184",
+                "@types/prop-types": "^15.7.5",
+                "@types/react-window": "^1.8.5",
+                "classnames": "^2.3.1",
+                "date-fns": "^2.29.3",
+                "dom-lib": "^3.1.3",
+                "lodash": "^4.17.11",
+                "prop-types": "^15.8.1",
+                "react-use-set": "^1.0.0",
+                "react-window": "^1.8.8",
+                "rsuite-table": "^5.18.2",
+                "schema-typed": "^2.1.3"
+            }
+        },
+        "rsuite-table": {
+            "version": "5.18.2",
+            "resolved": "https://registry.npmjs.org/rsuite-table/-/rsuite-table-5.18.2.tgz",
+            "integrity": "sha512-IelmlHraExYgrkT13WWVENhCywWjBxPkpF2zpsqvMcwzaNAg9lHaVVyajcOKczqGB24NGRE6WgBF5n1RC6XAww==",
+            "requires": {
+                "@babel/runtime": "^7.12.5",
+                "@juggle/resize-observer": "^3.3.1",
+                "@rsuite/icons": "^1.0.0",
+                "classnames": "^2.3.1",
+                "dom-lib": "^3.1.3",
+                "lodash": "^4.17.21",
+                "react-is": "^17.0.2"
+            },
+            "dependencies": {
+                "react-is": {
+                    "version": "17.0.2",
+                    "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+                    "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+                }
+            }
+        },
         "rtcpeerconnection-shim": {
             "version": "1.2.15",
             "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz",
@@ -29451,6 +31648,11 @@
                 "object-assign": "^4.1.1"
             }
         },
+        "schema-typed": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/schema-typed/-/schema-typed-2.1.3.tgz",
+            "integrity": "sha512-Nk0LLOq0L64HaQsXQGAZ8Z176tDE4jewsxyWe+6QvidNiC33DMaWFg+LaLWJ85uPPBtqBBJlCq9W4c1KEA88WA=="
+        },
         "schema-utils": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -31827,11 +34029,6 @@
                 "is-typed-array": "^1.1.9"
             }
         },
-        "typed-styles": {
-            "version": "0.0.7",
-            "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
-            "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
-        },
         "typedarray": {
             "version": "0.0.6",
             "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
diff --git a/package.json b/package.json
index 58a25a68c..adaeebf8a 100644
--- a/package.json
+++ b/package.json
@@ -72,8 +72,7 @@
         "@types/react": "^18.0.15",
         "@types/react-autosuggest": "^9.3.14",
         "@types/react-color": "^2.17.6",
-        "@types/react-date-range": "^1.4.9",
-        "@types/react-datepicker": "^3.1.8",
+        "@types/react-datepicker": "^4.19.4",
         "@types/react-dom": "^18.0.6",
         "@types/react-grid-layout": "^1.3.2",
         "@types/react-icons": "^3.0.0",
@@ -132,6 +131,7 @@
         "webpack-hot-middleware": "^2.25.1"
     },
     "dependencies": {
+        "@adobe/react-spectrum": "^3.32.2",
         "@azure/storage-blob": "^12.14.0",
         "@emotion/react": "^11.11.0",
         "@emotion/styled": "^11.11.0",
@@ -149,8 +149,6 @@
         "@material-ui/core": "^4.12.3",
         "@mui/icons-material": "^5.11.16",
         "@mui/material": "^5.13.1",
-        "@mui/x-date-pickers": "^6.18.5",
-        "@mui/x-date-pickers-pro": "^6.18.5",
         "@octokit/core": "^4.0.4",
         "@react-google-maps/api": "^2.7.0",
         "@react-three/fiber": "^6.2.3",
@@ -300,8 +298,7 @@
         "react-chartjs-2": "^4.3.0",
         "react-color": "^2.19.3",
         "react-compound-slider": "^2.5.0",
-        "react-date-range": "^1.4.0",
-        "react-datepicker": "^3.8.0",
+        "react-datepicker": "^4.24.0",
         "react-dom": "^18.2.0",
         "react-dropzone": "^14.2.3",
         "react-grid-layout": "^1.3.4",
@@ -328,6 +325,7 @@
         "request-promise": "^4.2.6",
         "reveal.js": "^4.3.0",
         "rimraf": "^3.0.0",
+        "rsuite": "^5.48.0",
         "serializr": "^1.5.4",
         "sharp": "^0.23.4",
         "shelljs": "^0.8.5",
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 306e9e14b..5b9f71641 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -38,6 +38,7 @@ export enum DocumentType {
     GROUP = 'group',
     PUSHPIN = 'pushpin',
     MAPROUTE = 'maproute',
+    CALENDAR = 'calendar',
 
     SCRIPTDB = 'scriptdb', // database of scripts
     GROUPDB = 'groupdb', // database of groups
@@ -62,4 +63,5 @@ export enum CollectionViewType {
     Pile = 'pileup',
     StackedTimeline = 'stacked timeline',
     NoteTaking = 'notetaking',
+    Calendar = 'calendar_view'
 }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f5f140ae9..778dbe5b8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -186,6 +186,8 @@ export class DocumentOptions {
     map_bearing?: NUMt = new NumInfo('bearing of a map view', false);
     map_style?: STRt = new StrInfo('mapbox style for a map view', false);
 
+    date_range?: STRt = new StrInfo('date range for calendar', false);
+
     wikiData?: STRt = new StrInfo('WikiData ID related to map location');
     description?: STRt = new StrInfo('A description of the document');
     _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)', false);
@@ -1039,6 +1041,7 @@ export namespace Docs {
         export function LoadingDocument(file: File | string, options: DocumentOptions) {
             return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file == 'string' ? file : file.name, ...options }, undefined, '');
         }
+ 
 
         export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
@@ -1160,6 +1163,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.MAPROUTE), new List(documents), { infoWindowOpen, ...options }, id);
         }
 
+        export function CalendarDocument(options: DocumentOptions={}, documents: Array<Doc>){
+            return InstanceFromProto(Prototypes.get(DocumentType.CALENDAR), new List(documents), options)
+        }
+
         // shouldn't ever need to create a KVP document-- instead set the LayoutTemplateString to be a KeyValueBox for the DocumentView (see addDocTab in TabDocView)
         // export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
         //     return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + '.kvp', ...options });
@@ -1214,6 +1221,10 @@ export namespace Docs {
             return doc;
         }
 
+        export function CalendarCollectionDocument(documents: Array<Doc>, options: DocumentOptions){
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), {...options,  _type_collection: CollectionViewType.Calendar});
+        }
+
         export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Stacking }, id, undefined, protoId);
         }
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx
index 39ba41652..e471db7c6 100644
--- a/src/client/util/CalendarManager.tsx
+++ b/src/client/util/CalendarManager.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
 import './CalendarManager.scss';
 import { observer } from "mobx-react";
 import { action, computed, observable, runInAction } from 'mobx';
-import { Doc } from '../../fields/Doc';
+import { Doc, DocListCast } from '../../fields/Doc';
 import { DocumentView } from '../views/nodes/DocumentView';
 import { DictationOverlay } from '../views/DictationOverlay';
 import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
@@ -10,19 +10,38 @@ import { MainViewModal } from '../views/MainViewModal';
 import { TextField } from '@material-ui/core';
 import Select from 'react-select';
 import { SettingsManager } from './SettingsManager';
-import { StrCast } from '../../fields/Types';
+import { DocCast, StrCast } from '../../fields/Types';
 import { SelectionManager } from './SelectionManager';
 import { DocumentManager } from './DocumentManager';
 import { DocData } from '../../fields/DocSymbols';
-import { DateRange, Range, RangeKeyDict } from 'react-date-range';
+// import { DateRange, Range, RangeKeyDict } from 'react-date-range';
 import { Button } from 'browndash-components';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import {DateRangePicker} from '@adobe/react-spectrum';
 import { IconLookup, faPlus } from '@fortawesome/free-solid-svg-icons';
-import 'react-date-range/dist/styles.css';
-import 'react-date-range/dist/theme/default.css';
+import { Docs } from '../documents/Documents';
+// import 'react-date-range/dist/styles.css';
+// import 'react-date-range/dist/theme/default.css';
 
 type CreationType = 'new-calendar' | 'existing-calendar' | 'manage-calendars';
 
+interface CalendarSelectOptions {
+    label: string,
+    value: string
+}
+
+const formatDateToString = (date: Date) => {
+    const year = date.getFullYear();
+    const month = String(date.getMonth() + 1).padStart(2, '0'); 
+    const day = String(date.getDate()).padStart(2, '0');
+
+    return `${year}-${month}-${day}`;
+}
+
+// TODO: If doc is already part of a calendar, display that
+// TODO: For a doc already in a calendar: give option to edit date range, delete from calendar
+
+
 @observer
 export class CalendarManager extends React.Component<{}> {
     public static Instance: CalendarManager;
@@ -36,11 +55,28 @@ export class CalendarManager extends React.Component<{}> {
 
     @observable private creationType: CreationType = 'new-calendar';
 
+    @observable private existingCalendars: Doc[] = DocListCast(Doc.MyCalendars.data);
+
+    @computed get selectOptions() {
+        return this.existingCalendars.map(calendar => ({ label: StrCast(calendar.title), value: StrCast(calendar.title) }));
+    }
+
+    @observable 
+    selectedExistingCalendarOption: CalendarSelectOptions | null = null;
+
     @observable 
     calendarName: string = "";
 
+    @observable 
+    calendarDescription: string = "";
+
+    @observable
+    errorMessage: string = "";
+
     @action
     setInterationType = (type: CreationType) => {
+        this.errorMessage = "";
+        this.calendarName = "";
         this.creationType = type;
     }
 
@@ -76,6 +112,60 @@ export class CalendarManager extends React.Component<{}> {
         
     }
 
+    @action
+    handleSelectChange = (option: any) => {
+        let selectOpt = option as CalendarSelectOptions;
+        this.selectedExistingCalendarOption = selectOpt;
+        this.calendarName = selectOpt.value; // or label
+    }
+
+    @action
+    handleTextFieldChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
+        this.calendarName = event.target.value;
+    }
+
+    // TODO: Make undoable
+    private addToCalendar = () => {
+        let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
+        const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData]; // doc to add to calendar
+
+        if (targetDoc) {
+            let calendar: Doc;
+            if (this.creationType === 'new-calendar'){
+                if (!this.existingCalendars.find(doc => StrCast(doc.title) === this.calendarName)){ 
+                    calendar = Docs.Create.CalendarDocument({
+                        title: this.calendarName,
+                        description: this.calendarDescription
+                    }, []);
+                } else {
+                    this.errorMessage = "Calendar with this name already exists"
+                    return;
+                }
+            } else {
+                // find existing calendar based on selected name (should technically always find one)
+                const existingCalendar = this.existingCalendars.find(calendar => StrCast(calendar.title) === this.calendarName);
+                if (existingCalendar) calendar = existingCalendar;
+                else {
+                    this.errorMessage = "Must select an existing calendar";
+                    return;
+                }
+            }
+            // Get start and end date strings
+            const startDateStr = formatDateToString(this.selectedDateRange.start);
+            const endDateStr = formatDateToString(this.selectedDateRange.end);
+
+            const subDocEmbedding = Doc.MakeEmbedding(targetDoc); // embedding 
+            subDocEmbedding.embedContainer = calendar; // set embed container
+            subDocEmbedding.date_range = `${startDateStr}-${endDateStr}`; // set subDoc date range
+
+            Doc.AddDocToList(calendar, 'data', subDocEmbedding); // add embedded subDoc to calendar
+            
+            Doc.AddDocToList(Doc.MyCalendars, 'data', calendar); // add to dashboard calendars
+            
+        }
+
+    }
+
     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];
@@ -108,20 +198,19 @@ export class CalendarManager extends React.Component<{}> {
     };
 
     @observable
-    selectedDateRange: Range[] = [{
-        startDate: new Date(),
-        endDate: undefined,
-        key: 'selection'
+    selectedDateRange: any = [{
+        start: new Date(),
+        end: new Date(),
     }]
 
     @action
-    setSelectedDateRange = (range: Range[]) => {
+    setSelectedDateRange = (range: any) => {
         this.selectedDateRange = range;
     }
 
     @computed
     get createButtonActive() {
-        if (this.calendarName.length === 0) return false // disabled if no calendar name
+        if (this.calendarName.length === 0 || this.errorMessage.length > 0) return false // disabled if no calendar name
         let startDate: Date | undefined;
         let endDate: Date | undefined;
         try {
@@ -170,6 +259,7 @@ export class CalendarManager extends React.Component<{}> {
                     {this.creationType === 'new-calendar' ? 
                         <TextField
                             fullWidth
+                            onChange={this.handleTextFieldChange}
                             placeholder='Enter calendar name...'
                             variant='filled'
                             style={{
@@ -178,38 +268,55 @@ export class CalendarManager extends React.Component<{}> {
                                 borderRadius: '5px'
 
                             }}
-                            
                         />
                     :
                         <Select
                             className='existing-calendar-search'
                             placeholder='Search for existing calendar...'
-                            isMulti
+                            isClearable
                             isSearchable
+                            options={this.selectOptions}
+                            value={this.selectedExistingCalendarOption}
+                            onChange={this.handleSelectChange}
+                            styles={{
+                                control: () => ({
+                                    display: 'inline-flex',
+                                    width: '100%',
+                                }),
+                                indicatorSeparator: () => ({
+                                    display: 'inline-flex',
+                                    visibility: 'hidden',
+                                }),
+                                indicatorsContainer: () => ({
+                                    display: 'inline-flex',
+                                    textDecorationColor: 'black',
+                                }),
+                                valueContainer: () => ({
+                                    display: 'inline-flex',
+                                    fontStyle: StrCast(Doc.UserDoc().userColor),
+                                    color: StrCast(Doc.UserDoc().userColor),
+                                    width: '100%',
+                                }),
+                            }}
                         >
-                            
                         </Select>
                     }
                 </div>
                 <div className='date-range-picker-container'>
-                    <DateRange
-                        className='react-date-range'
-                        editableDateInputs={true}
-                        // ranges={[selectionRange]}
-                        onChange={item => this.setSelectedDateRange([item.selection])}
-                        ranges={this.selectedDateRange}
-                    // onChange={this.handleSelect}
-                    />
+                    <DateRangePicker 
+                        value={this.selectedDateRange}
+                        onChange={(v => this.setSelectedDateRange(v))}
+                        label="Date range" />
                 </div>
                 <div className='create-button-container'>
                     <Button
                         active={this.createButtonActive}
+                        onClick={() => {}}
                         text="Add to Calendar"
                         iconPlacement='right'
                         icon={<FontAwesomeIcon icon={faPlus as IconLookup}/>}
                     />
                 </div>
-                
             </div>
         )
     }
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 2765e95e6..3c8221f6a 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -379,13 +379,42 @@ export class DashboardView extends React.Component {
         Doc.AddDocToList(Doc.MyDashboards, 'data', dashboardDoc);
 
         DashboardView.SetupDashboardTrails(dashboardDoc);
-
+        DashboardView.SetupDashboardCalendars(dashboardDoc);
         // open this new dashboard
         Doc.ActiveDashboard = dashboardDoc;
         Doc.ActivePage = 'dashboard';
         Doc.ActivePresentation = undefined;
     };
 
+    public static SetupDashboardCalendars(dashboardDoc: Doc){
+        // this section is creating the button document itself  ===  myTrails = new Button
+        
+        //  create a a list of calendars (as a CalendarCollectionDocument) and store it on the new dashboard
+        const reqdOpts: DocumentOptions = {
+            title: 'My Calendars',
+            _layout_showTitle: 'title',
+            _height: 100,
+            treeView_HideTitle: true,
+            _layout_fitWidth: true,
+            _gridGap: 5,
+            _forceActive: true,
+            childDragAction: 'embed',
+            treeView_TruncateTitleWidth: 150,
+            ignoreClick: true,
+            contextMenuIcons: new List<string>(['plus']),
+            contextMenuLabels: new List<string>(['Create New Calendar']),
+            _lockedPosition: true,
+            layout_boxShadow: '0 0',
+            childDontRegisterViews: true,
+            dropAction: 'same',
+            isSystem: true,
+            layout_explainer: 'All of the calendars that you have created will appear here.',
+        };
+        const myCalendars = DocUtils.AssignScripts(Docs.Create.CalendarCollectionDocument([], reqdOpts));
+        // { treeView_ChildDoubleClick: 'openPresentation(documentView.rootDoc)' }
+        dashboardDoc.myCalendars = new PrefetchProxy(myCalendars);
+    }
+
     public static SetupDashboardTrails(dashboardDoc: Doc) {
         // this section is creating the button document itself  ===  myTrails = new Button
         const reqdBtnOpts: DocumentOptions = {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2e8047309..f479b9b11 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -465,6 +465,7 @@ export class CollectionDockingView extends CollectionSubView() {
         });
         const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
         DashboardView.SetupDashboardTrails(copy);
+        DashboardView.SetupDashboardCalendars(copy); // Zaul TODO: needed?
         return DashboardView.openDashboard(copy);
     }
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 1678d9012..cfbef2d21 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -147,6 +147,7 @@ export class Doc extends RefField {
     public static get MyTopBarBtns()          { return DocCast(Doc.UserDoc().myTopBarBtns); } // prettier-ignore
     public static get MyRecentlyClosed()      { return DocCast(Doc.UserDoc().myRecentlyClosed); } // prettier-ignore
     public static get MyTrails()              { return DocCast(Doc.ActiveDashboard?.myTrails); } // prettier-ignore
+    public static get MyCalendars()           { return DocCast(Doc.ActiveDashboard?.myCalendars); } // prettier-ignore
     public static get MyOverlayDocs()         { return DocListCast(Doc.ActiveDashboard?.myOverlayDocs ?? DocCast(Doc.UserDoc().myOverlayDocs)?.data); } // prettier-ignore
     public static get MyPublishedDocs()       { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs ?? DocCast(Doc.UserDoc().myPublishedDocs)?.data); } // prettier-ignore
     public static get MyDashboards()          { return DocCast(Doc.UserDoc().myDashboards); } // prettier-ignore
-- 
cgit v1.2.3-70-g09d2

